# Dokumen Teknis
# SPK MOORA E-KIN Penilaian Kinerja Pegawai

## 1. Ringkasan Teknis

Dokumen ini menjabarkan rancangan teknis Sistem Pendukung Keputusan (SPK) penilaian kinerja pegawai berbasis metode MOORA. Sumber utama rancangan berasal dari BAB 3: Analisis dan Perancangan, yang memuat kebutuhan sistem, DFD Level 0, DFD Level 1, ERD, desain tabel, dan desain antarmuka.

Sistem dirancang sebagai aplikasi web dengan modul autentikasi, master data, input penilaian, perhitungan MOORA, perangkingan, dashboard, dan laporan.

## 2. Arsitektur Sistem

```text
Pengguna Web
  -> Frontend Web UI
  -> Backend Application
  -> Database Relasional
  -> Modul Perhitungan MOORA
  -> Modul Laporan

Opsional:
Sistem e-Kinerja -> Import/API -> Backend Application
```

Komponen utama:

| Komponen | Tanggung Jawab |
| --- | --- |
| Frontend Web UI | Menampilkan login, dashboard, data master, penilaian, hasil, dan laporan. |
| Backend Application | Menangani autentikasi, validasi, transaksi data, kalkulasi, dan kontrol akses. |
| Database | Menyimpan user, pegawai, kriteria, periode, penilaian, hasil MOORA, peringkat, dan pimpinan. |
| Modul MOORA | Menghitung normalisasi, pembobotan, nilai optimasi Yi, dan ranking. |
| Modul Laporan | Menyediakan ringkasan hasil per periode untuk admin dan pimpinan. |

## 2.1 Standar Implementasi Aplikasi

Implementasi aplikasi menggunakan PHP modular dengan pemisahan tanggung jawab yang jelas antara konfigurasi, routing, controller, model, service, helper, view, dan asset frontend. Pendekatan ini dipilih agar aplikasi tetap ringan, mudah dipelihara, dan tidak mengunci pengembangan pada framework tertentu.

### 2.1.1 Teknologi Utama

| Area | Standar |
| --- | --- |
| Bahasa backend | PHP modular. |
| Database | MySQL/MariaDB. |
| Koneksi database | PDO dengan prepared statement. |
| Frontend | HTML, CSS, JavaScript, jQuery. |
| Tabel data | DataTables server-side processing. |
| Select field | Select2 untuk seluruh elemen `select`. |
| Request dinamis | AJAX untuk aksi simpan, ubah, hapus, filter, dan load data tabel. |
| Penamaan kode PHP/JS | camelCase untuk variabel, fungsi, method, dan key respons internal. |
| Penamaan class | PascalCase. |
| Penamaan tabel/kolom database | snake_case mengikuti rancangan database. |

### 2.1.2 Struktur Folder Direkomendasikan

```text
app/
  Config/
    app.php
    database.php
  Controllers/
    AuthController.php
    DashboardController.php
    PegawaiController.php
    KriteriaController.php
    PeriodeController.php
    PenilaianController.php
    HasilController.php
    LaporanController.php
  Core/
    App.php
    Controller.php
    Database.php
    Middleware.php
    Router.php
    View.php
  Helpers/
    authHelper.php
    responseHelper.php
    securityHelper.php
    stringHelper.php
  Models/
    Admin.php
    Pegawai.php
    Kriteria.php
    Periode.php
    Penilaian.php
    HasilMoora.php
    Peringkat.php
    Pimpinan.php
  Services/
    DataTableService.php
    MooraService.php
    ReportService.php
  Views/
    layouts/
    auth/
    dashboard/
    pegawai/
    kriteria/
    periode/
    penilaian/
    hasil/
    laporan/
public/
  index.php
  assets/
    css/
    js/
      app.js
      datatable.js
      form.js
      select2.js
    vendor/
storage/
  logs/
```

Catatan: bila hosting mengharuskan entry point di root, `public/index.php` dapat disesuaikan menjadi `index.php`, tetapi struktur `app/` tetap dipertahankan.

### 2.1.3 Konvensi Penamaan

- Variabel PHP menggunakan camelCase, contoh: `$idPegawai`, `$namaKriteria`, `$totalBobot`.
- Method dan function menggunakan camelCase, contoh: `getPegawaiList()`, `hitungMoora()`, `jsonResponse()`.
- Class menggunakan PascalCase, contoh: `PegawaiController`, `MooraService`, `DataTableService`.
- File class mengikuti nama class, contoh: `PegawaiController.php`.
- Kolom database tetap snake_case, contoh: `id_pegawai`, `nama_pegawai`, `tanggal_penilaian`.
- Route menggunakan huruf kecil dan tanda hubung jika perlu, contoh: `/hasil-perhitungan`.

### 2.1.4 Konfigurasi Database

Database sudah tersedia dan digunakan sebagai target implementasi.

| Item | Nilai |
| --- | --- |
| Database name | `nusakod_simoora` |
| Database user | `nusakod_user` |
| Database password | Disimpan di file konfigurasi lokal/env, tidak ditulis sebagai plaintext di dokumen teknis. |

Contoh pola konfigurasi:

```php
return [
    'host' => getenv('DB_HOST') ?: 'localhost',
    'database' => getenv('DB_DATABASE') ?: 'nusakod_simoora',
    'username' => getenv('DB_USERNAME') ?: 'nusakod_user',
    'password' => getenv('DB_PASSWORD') ?: '',
    'charset' => 'utf8mb4',
];
```

Password database dipasang pada file konfigurasi lokal server atau environment variable. File yang memuat password tidak boleh ditampilkan di UI dan tidak perlu dimasukkan ke dokumentasi publik.

## 3. Aktor dan Hak Akses

| Role | Akses |
| --- | --- |
| Admin | Full access ke data user, pegawai, kriteria, periode, penilaian, hasil perhitungan, dan laporan. |
| Pimpinan | Read-only terhadap hasil perhitungan, peringkat, dan laporan penilaian. |

Implementasi minimal dapat menggunakan satu role Admin dan menjadikan Pimpinan sebagai data penerima laporan. Jika dibutuhkan, role Pimpinan dapat ditambahkan sebagai user login terpisah.

## 4. Modul Sistem

### 4.1 Autentikasi

- Login dengan username dan password.
- Password disimpan sebagai hash.
- Session/token dibuat setelah login valid.
- Logout menghapus session/token.
- Middleware membatasi halaman internal agar hanya dapat diakses pengguna terautentikasi.

### 4.2 Dashboard Analitik

Menampilkan:

- Total kriteria.
- Total pegawai.
- Total periode.
- Total pengguna sistem.
- Grafik top 5 pegawai terbaik pada periode terpilih.
- Ringkasan informasi metode MOORA.

### 4.3 Manajemen User

- CRUD user.
- Pengaturan role admin/pimpinan jika role pimpinan diaktifkan.
- Status aktif/nonaktif direkomendasikan agar akun historis tidak perlu dihapus fisik.

### 4.4 Manajemen Pegawai

- CRUD pegawai.
- Field utama: NIP, nama pegawai, jabatan, unit kerja, status.
- NIP disimpan sebagai teks agar aman dari hilangnya angka nol di depan dan variasi format.

### 4.5 Manajemen Kriteria dan Bobot

- CRUD kriteria.
- Setiap kriteria memiliki kode, nama, jenis benefit/cost, bobot, dan status aktif.
- Sistem memvalidasi total bobot kriteria aktif sama dengan 1.0 atau 100 persen.

### 4.6 Manajemen Periode

- CRUD periode penilaian.
- Periode memiliki nama, tanggal mulai, tanggal selesai, tahun, semester, dan status aktif/dikunci.
- Periode yang sudah dikunci tidak boleh diubah tanpa proses pembukaan kunci oleh admin berwenang.

### 4.7 Penilaian Pegawai

- Admin memilih periode aktif.
- Sistem menampilkan matriks pegawai x kriteria.
- Admin mengisi nilai setiap pegawai pada setiap kriteria.
- Sistem menyimpan data induk penilaian dan detail penilaian.
- Sistem menolak penyimpanan jika ada nilai wajib yang kosong.

### 4.8 Perhitungan MOORA

- Sistem mengambil data penilaian lengkap pada periode tertentu.
- Sistem menghitung matriks keputusan X.
- Sistem menghitung normalisasi R.
- Sistem mengalikan normalisasi dengan bobot.
- Sistem menghitung nilai optimasi Yi.
- Sistem menyimpan skor pada tabel hasil MOORA.
- Sistem menghasilkan peringkat dan menyimpannya pada tabel peringkat.

### 4.9 Laporan

- Laporan hasil penilaian per periode.
- Memuat NIP, nama pegawai, nilai Yi, ranking, dan ringkasan pegawai terbaik.
- Dapat dikembangkan untuk ekspor PDF/Excel.

## 5. Aliran Data

### 5.1 DFD Level 0

Entitas eksternal:

- Admin: mengirim data login, data pegawai, data kriteria, bobot, nilai kinerja, dan capaian kinerja.
- Pimpinan: menerima hasil perhitungan MOORA, peringkat kinerja pegawai, laporan penilaian, dan laporan kinerja.

Proses utama:

- Sistem Pendukung Keputusan Penilaian Kinerja Pegawai dengan metode MOORA.

Output utama:

- Nilai preferensi.
- Peringkat pegawai.
- Laporan penilaian kinerja.

### 5.2 DFD Level 1

| Proses | Nama | Input | Output |
| --- | --- | --- | --- |
| 1.0 | Pegawai | Data pegawai | Data pegawai tersimpan. |
| 2.0 | Kriteria dan Bobot | Data kriteria dan bobot | Data kriteria tersimpan. |
| 3.0 | Pengolahan Penilaian | Nilai kinerja dan capaian kinerja | Data penilaian. |
| 4.0 | Perhitungan MOORA | Data penilaian, kriteria, bobot | Skor MOORA. |
| 5.0 | Perangkingan | Skor MOORA | Peringkat pegawai. |
| 6.0 | Laporan | Pegawai, kriteria, penilaian, hasil, peringkat | Laporan untuk pimpinan. |

## 6. Rancangan Database

Tipe data di bawah adalah rekomendasi teknis yang merapikan desain BAB 3 agar lebih aman untuk implementasi nyata. Nama tabel tetap mengikuti dokumen sumber.

### 6.1 tb_admin

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_admin | BIGINT UNSIGNED | PK, auto increment | ID user. |
| username | VARCHAR(50) | UNIQUE, NOT NULL | Username login. |
| password_hash | VARCHAR(255) | NOT NULL | Hash password, bukan plaintext. |
| level | ENUM('admin','pimpinan') | NOT NULL | Role pengguna. |
| status | ENUM('aktif','nonaktif') | DEFAULT 'aktif' | Status akun. |
| created_at | DATETIME | NOT NULL | Waktu dibuat. |
| updated_at | DATETIME | NULL | Waktu diperbarui. |

### 6.2 tb_pegawai

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_pegawai | BIGINT UNSIGNED | PK, auto increment | ID pegawai. |
| nip | VARCHAR(30) | UNIQUE, NOT NULL | Nomor induk pegawai. |
| nama_pegawai | VARCHAR(100) | NOT NULL | Nama pegawai. |
| jabatan | VARCHAR(100) | NOT NULL | Jabatan pegawai. |
| unit_kerja | VARCHAR(100) | NULL | Unit kerja. |
| status | ENUM('aktif','nonaktif') | DEFAULT 'aktif' | Status pegawai. |
| created_at | DATETIME | NOT NULL | Waktu dibuat. |
| updated_at | DATETIME | NULL | Waktu diperbarui. |

### 6.3 tb_kriteria_dan_bobot

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_kriteria | BIGINT UNSIGNED | PK, auto increment | ID kriteria. |
| kode_kriteria | VARCHAR(10) | UNIQUE, NOT NULL | Contoh: C1, C2. |
| nama_kriteria | VARCHAR(100) | NOT NULL | Nama kriteria. |
| jenis_kriteria | ENUM('benefit','cost') | NOT NULL | Jenis kriteria. |
| bobot | DECIMAL(8,6) | NOT NULL | Bobot 0-1. |
| status | ENUM('aktif','nonaktif') | DEFAULT 'aktif' | Status kriteria. |
| created_at | DATETIME | NOT NULL | Waktu dibuat. |
| updated_at | DATETIME | NULL | Waktu diperbarui. |

### 6.4 tb_periode

Tabel ini tidak dirinci pada desain tabel BAB 3, tetapi muncul pada menu dan alur UI sehingga direkomendasikan untuk implementasi.

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_periode | BIGINT UNSIGNED | PK, auto increment | ID periode. |
| nama_periode | VARCHAR(100) | NOT NULL | Contoh: Semester 1 (2026). |
| tahun | YEAR | NOT NULL | Tahun penilaian. |
| semester | TINYINT | NULL | Semester 1/2 jika digunakan. |
| tanggal_mulai | DATE | NULL | Awal periode. |
| tanggal_selesai | DATE | NULL | Akhir periode. |
| status | ENUM('draft','aktif','dikunci') | DEFAULT 'draft' | Status periode. |
| created_at | DATETIME | NOT NULL | Waktu dibuat. |
| updated_at | DATETIME | NULL | Waktu diperbarui. |

### 6.5 tb_penilaian

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_penilaian | BIGINT UNSIGNED | PK, auto increment | ID penilaian. |
| id_periode | BIGINT UNSIGNED | FK -> tb_periode.id_periode | Periode penilaian. |
| id_pegawai | BIGINT UNSIGNED | FK -> tb_pegawai.id_pegawai | Pegawai yang dinilai. |
| tanggal_penilaian | DATE | NOT NULL | Tanggal penilaian. |
| capaian_kinerja | VARCHAR(100) | NULL | Capaian kinerja pegawai. |
| created_by | BIGINT UNSIGNED | FK -> tb_admin.id_admin | Admin penginput. |
| created_at | DATETIME | NOT NULL | Waktu dibuat. |
| updated_at | DATETIME | NULL | Waktu diperbarui. |

Constraint unik yang direkomendasikan:

```sql
UNIQUE (id_periode, id_pegawai)
```

### 6.6 tb_detail_penilaian

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_detail | BIGINT UNSIGNED | PK, auto increment | ID detail. |
| id_penilaian | BIGINT UNSIGNED | FK -> tb_penilaian.id_penilaian | Data induk penilaian. |
| id_kriteria | BIGINT UNSIGNED | FK -> tb_kriteria_dan_bobot.id_kriteria | Kriteria. |
| nilai | DECIMAL(10,4) | NOT NULL | Nilai pegawai untuk kriteria. |

Constraint unik yang direkomendasikan:

```sql
UNIQUE (id_penilaian, id_kriteria)
```

### 6.7 tb_hasil_moora

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_hasil | BIGINT UNSIGNED | PK, auto increment | ID hasil. |
| id_penilaian | BIGINT UNSIGNED | FK -> tb_penilaian.id_penilaian | Penilaian terkait. |
| skor_moora | DECIMAL(18,10) | NOT NULL | Nilai preferensi Yi. |
| tanggal_hitung | DATETIME | NOT NULL | Waktu perhitungan. |

### 6.8 tb_peringkat

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_peringkat | BIGINT UNSIGNED | PK, auto increment | ID peringkat. |
| id_hasil | BIGINT UNSIGNED | FK -> tb_hasil_moora.id_hasil | Hasil MOORA. |
| id_periode | BIGINT UNSIGNED | FK -> tb_periode.id_periode | Periode ranking. |
| peringkat | INT | NOT NULL | Urutan ranking. |

Constraint unik yang direkomendasikan:

```sql
UNIQUE (id_periode, peringkat)
```

### 6.9 tb_pimpinan

| Kolom | Tipe | Constraint | Keterangan |
| --- | --- | --- | --- |
| id_pimpinan | BIGINT UNSIGNED | PK, auto increment | ID pimpinan. |
| nama_pimpinan | VARCHAR(100) | NOT NULL | Nama pimpinan. |
| jabatan | VARCHAR(100) | NOT NULL | Jabatan pimpinan. |
| created_at | DATETIME | NOT NULL | Waktu dibuat. |
| updated_at | DATETIME | NULL | Waktu diperbarui. |

## 7. Relasi Database

- `tb_admin` 1:N `tb_pegawai` secara konseptual sebagai pengelola data.
- `tb_pegawai` 1:N `tb_penilaian`.
- `tb_periode` 1:N `tb_penilaian`.
- `tb_penilaian` 1:N `tb_detail_penilaian`.
- `tb_kriteria_dan_bobot` 1:N `tb_detail_penilaian`.
- `tb_penilaian` 1:1 `tb_hasil_moora`.
- `tb_hasil_moora` 1:1 `tb_peringkat`.
- `tb_peringkat` N:1 `tb_pimpinan` secara konseptual sebagai data yang dilaporkan.

## 8. Algoritma Perhitungan MOORA

### 8.1 Input

- Daftar pegawai pada periode tertentu.
- Daftar kriteria aktif.
- Bobot setiap kriteria.
- Jenis kriteria: benefit atau cost.
- Nilai setiap pegawai pada setiap kriteria.

### 8.2 Langkah Perhitungan

1. Ambil matriks keputusan X dengan baris pegawai dan kolom kriteria.
2. Untuk setiap kriteria j, hitung penyebut normalisasi:

```text
denominator_j = sqrt(sum(Xij^2))
```

3. Hitung nilai normalisasi:

```text
Rij = Xij / denominator_j
```

4. Hitung nilai berbobot:

```text
Vij = Wj * Rij
```

5. Hitung nilai optimasi Yi:

```text
Yi = sum(Vij untuk benefit) - sum(Vij untuk cost)
```

6. Urutkan pegawai berdasarkan Yi dari tertinggi ke terendah.
7. Simpan skor pada `tb_hasil_moora` dan ranking pada `tb_peringkat`.

### 8.3 Pseudocode

```text
function hitungMoora(id_periode):
    kriteria = ambilKriteriaAktif()
    penilaian = ambilPenilaianLengkap(id_periode)

    validasiTotalBobot(kriteria)
    validasiNilaiLengkap(penilaian, kriteria)

    for setiap kriteria j:
        denominator[j] = sqrt(sum(nilai[i][j]^2 untuk semua pegawai i))
        if denominator[j] == 0:
            throw error "Nilai kriteria tidak dapat dinormalisasi"

    for setiap pegawai i:
        benefit_total = 0
        cost_total = 0

        for setiap kriteria j:
            r = nilai[i][j] / denominator[j]
            v = r * bobot[j]

            if jenis[j] == "benefit":
                benefit_total += v
            else:
                cost_total += v

        yi[i] = benefit_total - cost_total

    ranking = urutkanMenurun(yi)
    simpanHasilDanPeringkat(id_periode, ranking)
```

## 9. Validasi Data

| Area | Validasi |
| --- | --- |
| Login | Username dan password wajib diisi. Akun harus aktif. |
| Pegawai | NIP unik, nama dan jabatan wajib diisi. |
| Kriteria | Kode unik, nama wajib diisi, bobot > 0, jenis harus benefit/cost. |
| Bobot | Total bobot kriteria aktif harus 1.0 atau 100 persen. |
| Periode | Hanya satu periode aktif jika aturan bisnis mengharuskan. Periode dikunci tidak boleh diubah. |
| Penilaian | Setiap pegawai harus memiliki nilai untuk seluruh kriteria aktif. |
| MOORA | Denominator normalisasi tidak boleh nol. |

## 10. Rekomendasi Endpoint/Route

Jika sistem dibangun sebagai aplikasi MVC atau REST, route berikut dapat digunakan sebagai acuan.

| Method | Route | Fungsi |
| --- | --- | --- |
| GET | `/login` | Form login. |
| POST | `/login` | Proses login. |
| POST | `/logout` | Logout. |
| GET | `/dashboard` | Dashboard analitik. |
| GET | `/users` | Daftar user. |
| POST | `/users` | Tambah user. |
| PUT | `/users/{id}` | Ubah user. |
| DELETE | `/users/{id}` | Hapus/nonaktifkan user. |
| GET | `/pegawai` | Daftar pegawai. |
| POST | `/pegawai` | Tambah pegawai. |
| PUT | `/pegawai/{id}` | Ubah pegawai. |
| DELETE | `/pegawai/{id}` | Hapus/nonaktifkan pegawai. |
| GET | `/kriteria` | Daftar kriteria. |
| POST | `/kriteria` | Tambah kriteria. |
| PUT | `/kriteria/{id}` | Ubah kriteria. |
| DELETE | `/kriteria/{id}` | Hapus/nonaktifkan kriteria. |
| GET | `/periode` | Daftar periode. |
| POST | `/periode` | Tambah periode. |
| PUT | `/periode/{id}` | Ubah periode. |
| GET | `/penilaian?periode={id}` | Form/matriks penilaian. |
| POST | `/penilaian` | Simpan penilaian. |
| GET | `/hasil?periode={id}` | Tampilkan hasil perhitungan. |
| POST | `/hasil/hitung` | Jalankan perhitungan MOORA. |
| GET | `/laporan?periode={id}` | Laporan penilaian. |
| GET | `/panduan` | Petunjuk penggunaan. |

### 10.1 Endpoint DataTables Server-side

Setiap halaman tabel memiliki endpoint JSON khusus untuk DataTables. Endpoint ini dipisahkan dari halaman HTML agar rendering halaman dan pengambilan data tidak saling bercampur.

| Method | Route | Fungsi |
| --- | --- | --- |
| GET | `/users/datatable` | DataTables user. |
| GET | `/pegawai/datatable` | DataTables pegawai. |
| GET | `/kriteria/datatable` | DataTables kriteria. |
| GET | `/periode/datatable` | DataTables periode. |
| GET | `/hasil/datatable` | DataTables hasil perhitungan. |
| GET | `/laporan/datatable` | DataTables laporan penilaian. |

Parameter standar yang diterima dari DataTables:

| Parameter | Keterangan |
| --- | --- |
| `draw` | Nomor request dari DataTables. |
| `start` | Offset awal data. |
| `length` | Jumlah data per halaman. |
| `search[value]` | Kata kunci pencarian global. |
| `order[0][column]` | Index kolom yang diurutkan. |
| `order[0][dir]` | Arah urutan `asc` atau `desc`. |
| `columns` | Definisi kolom dari DataTables. |

Backend harus memakai service global `DataTableService` untuk:

- Membaca parameter DataTables secara aman.
- Memetakan kolom frontend ke kolom database yang diizinkan.
- Menjalankan search, order, limit, dan offset.
- Menghitung `recordsTotal` dan `recordsFiltered`.
- Mengembalikan JSON standar DataTables.

Kolom order/search tidak boleh langsung memakai input mentah dari request. Gunakan whitelist kolom per modul, contoh:

```php
$allowedColumns = [
    'nip' => 'p.nip',
    'namaPegawai' => 'p.nama_pegawai',
    'jabatan' => 'p.jabatan',
    'status' => 'p.status',
];
```

## 11. Antarmuka Sistem

Halaman berdasarkan BAB 3:

- Login: username, password, tombol masuk.
- Dashboard Analitik: summary card, grafik top 5 pegawai, informasi metode MOORA, tips penggunaan.
- Data Kriteria: tabel kode, nama kriteria, bobot, jenis, aksi detail/edit/hapus, tambah kriteria.
- Data Pegawai: tabel NIP, nama pegawai, jabatan, aksi edit/hapus, tambah pegawai.
- Penilaian Pegawai: filter periode, matriks pegawai x kriteria C1 sampai C5, dropdown/skala nilai, simpan penilaian.
- Hasil Perhitungan: matriks keputusan X, normalisasi R, hasil akhir, ranking, pegawai terbaik periode.
- Laporan Penilaian: ringkasan hasil per periode untuk pimpinan.

## 11.1 Standar Interaksi UI Global

Standar berikut wajib diterapkan ke seluruh halaman agar perilaku aplikasi konsisten.

### 11.1.1 Spinner pada Seluruh Tombol

- Semua tombol yang menjalankan proses harus menampilkan spinner saat diklik.
- Tombol harus masuk state disabled selama request berjalan untuk mencegah double submit.
- Teks/icon tombol dikembalikan ke kondisi awal setelah request selesai, baik sukses maupun gagal.
- Tombol aksi yang wajib memakai spinner meliputi login, simpan, tambah, ubah, hapus, filter, hitung MOORA, cetak/unduh laporan, dan refresh tabel.
- Implementasi dibuat sebagai fungsi global, misalnya `setButtonLoading(button, isLoading, loadingText)`.

Contoh perilaku:

```text
Klik Simpan -> tombol disabled + spinner -> AJAX berjalan -> sukses/gagal -> tombol normal lagi
```

### 11.1.2 Select2 untuk Semua Field Select

- Seluruh elemen `select` wajib menggunakan Select2.
- Select2 harus diinisialisasi melalui fungsi global, misalnya `initSelect2(container)`.
- Select2 pada modal wajib menggunakan opsi `dropdownParent` agar dropdown tidak tertutup layer modal.
- Select periode, pegawai, kriteria, role, status, jenis benefit/cost, dan filter laporan harus memakai Select2.
- Field select yang datanya besar, seperti pegawai, dapat menggunakan AJAX Select2 agar halaman tetap ringan.

### 11.1.3 DataTables AJAX Server-side sebagai Fungsi Global

- Semua tabel daftar data menggunakan DataTables dengan mode server-side.
- Inisialisasi DataTables tidak ditulis berulang di setiap halaman, tetapi memakai helper global, misalnya `initServerDataTable(options)`.
- Helper global wajib menangani konfigurasi standar: `processing`, `serverSide`, `ajax`, `columns`, nomor urut, search, pagination, order, refresh, dan error handler.
- Endpoint DataTables harus mengembalikan format standar DataTables: `draw`, `recordsTotal`, `recordsFiltered`, dan `data`.
- DataTables dipakai untuk minimal halaman user, pegawai, kriteria, periode, hasil perhitungan, dan laporan.

Contoh kontrak respons:

```json
{
  "draw": 1,
  "recordsTotal": 100,
  "recordsFiltered": 25,
  "data": []
}
```

### 11.1.4 AJAX Form Global

- Form tambah/ubah/login/penilaian dikirim menggunakan AJAX bila tidak membutuhkan reload penuh.
- Submit form memakai helper global, misalnya `submitAjaxForm(form, options)`.
- Respons sukses menutup modal jika ada, menampilkan notifikasi, dan me-refresh DataTable terkait.
- Respons gagal menampilkan pesan validasi pada field yang sesuai.
- Semua request AJAX membawa CSRF token jika CSRF protection diaktifkan.

Format respons standar aplikasi:

```json
{
  "success": true,
  "message": "Data berhasil disimpan.",
  "data": {}
}
```

Format respons validasi:

```json
{
  "success": false,
  "message": "Validasi gagal.",
  "errors": {
    "namaPegawai": "Nama pegawai wajib diisi."
  }
}
```

### 11.1.5 Notifikasi dan Konfirmasi

- Aksi sukses/gagal harus menampilkan notifikasi konsisten.
- Aksi destruktif seperti hapus/nonaktifkan wajib memakai dialog konfirmasi.
- Pesan error dari server harus ramah pengguna, sedangkan detail teknis dicatat ke log.
- Notifikasi dapat menggunakan SweetAlert2 atau komponen toast yang konsisten di seluruh aplikasi.

### 11.1.6 State Kosong, Loading, dan Error

- Setiap tabel atau panel data harus memiliki tampilan saat data kosong.
- Loading tidak hanya bergantung pada spinner tombol; DataTables tetap menggunakan indikator processing.
- Error koneksi atau server harus menampilkan pesan yang jelas dan menyediakan aksi coba lagi bila memungkinkan.

## 12. Keamanan

- Password wajib di-hash menggunakan algoritma standar framework, misalnya bcrypt/Argon2.
- Gunakan CSRF protection untuk form web.
- Validasi dan sanitasi semua input.
- Gunakan authorization middleware berdasarkan role.
- Hindari delete fisik untuk data penting; gunakan status aktif/nonaktif bila perlu audit.
- Catat waktu pembuatan dan pembaruan data.
- Batasi akses laporan sesuai role.
- Gunakan PDO prepared statement untuk seluruh query yang menerima input pengguna.
- Regenerasi session ID setelah login berhasil untuk mencegah session fixation.
- Terapkan timeout session agar akun internal tidak terus aktif tanpa batas.
- Simpan detail error teknis di log server, bukan ditampilkan langsung ke pengguna.
- Batasi upload/export bila fitur laporan file ditambahkan di fase berikutnya.

## 13. Strategi Pengujian

### 13.1 Unit Test

- Fungsi normalisasi MOORA.
- Fungsi pembobotan.
- Fungsi nilai optimasi Yi untuk benefit dan cost.
- Fungsi sorting ranking.
- Validasi total bobot.

### 13.2 Integration Test

- Simpan penilaian lengkap lalu jalankan perhitungan MOORA.
- Perubahan kriteria memengaruhi validasi bobot.
- Periode dikunci tidak dapat diubah.
- Laporan membaca hasil perhitungan yang benar.

### 13.3 Functional Test

- Login berhasil/gagal.
- CRUD pegawai.
- CRUD kriteria.
- Input penilaian per periode.
- Tampil hasil perhitungan.
- Tampil laporan.

### 13.4 Acceptance Test

- Admin dapat melakukan penilaian dari data master sampai laporan tanpa perhitungan manual.
- Ranking yang dihasilkan sama dengan perhitungan manual pembanding menggunakan rumus MOORA.
- Pimpinan dapat melihat laporan hasil penilaian per periode.

## 14. Catatan Implementasi

- Gunakan transaksi database saat menyimpan hasil perhitungan agar `tb_hasil_moora` dan `tb_peringkat` konsisten.
- Saat hitung ulang periode yang sama, hapus atau arsipkan hasil lama sebelum menyimpan hasil baru.
- Jika kriteria dapat berubah setelah periode dihitung, pertimbangkan snapshot kriteria dan bobot per periode.
- Jika data e-Kinerja diintegrasikan, sediakan layer import yang memetakan data eksternal ke `tb_penilaian` dan `tb_detail_penilaian`.
- Gunakan decimal untuk bobot dan skor agar hasil lebih stabil dibanding float biner.
- Gunakan soft delete atau kolom `status` untuk data master seperti pegawai, kriteria, periode, dan user agar histori penilaian tidak rusak.
- Tambahkan audit sederhana melalui kolom `created_by`, `updated_by`, `created_at`, dan `updated_at` pada tabel transaksi penting.
- Buat helper respons JSON global agar semua endpoint AJAX memiliki format respons yang sama.
- Buat middleware autentikasi dan authorization sejak awal agar halaman internal tidak terbuka tanpa login.
- Pisahkan query kompleks laporan/perhitungan ke service agar controller tetap tipis.
- Gunakan satu layout utama untuk sidebar, topbar, footer, asset CSS/JS, dan inisialisasi global.
- Pastikan semua halaman internal memakai breadcrumb sesuai rancangan UI.

## 15. Rekomendasi Tambahan Sebelum Coding

### 15.1 Prioritas Implementasi

Urutan pengerjaan yang direkomendasikan:

1. Core aplikasi: config, database PDO, router, base controller, view renderer, session, helper response.
2. Auth: login, logout, middleware auth, password hash.
3. Layout admin: sidebar, topbar, footer, asset global, spinner button, Select2, DataTables helper.
4. Master data: user, pegawai, kriteria, periode.
5. Penilaian: filter periode, matriks input nilai, validasi kelengkapan.
6. MOORA: service perhitungan, penyimpanan hasil, perangkingan.
7. Dashboard dan laporan.
8. Pengujian hitung manual vs hasil sistem.

### 15.2 Standar Asset Global

File JavaScript global yang direkomendasikan:

| File | Isi |
| --- | --- |
| `assets/js/app.js` | Setup CSRF, AJAX default, notifikasi, helper umum. |
| `assets/js/form.js` | Spinner tombol, submit AJAX, reset form, render error validasi. |
| `assets/js/select2.js` | Inisialisasi Select2 global dan Select2 AJAX. |
| `assets/js/datatable.js` | Fungsi global DataTables server-side. |

File CSS global yang direkomendasikan:

| File | Isi |
| --- | --- |
| `assets/css/app.css` | Layout, sidebar, tabel, form, tombol, state kosong, dan responsif. |

### 15.3 Standar Respons Controller

Controller yang dipanggil AJAX harus mengembalikan JSON standar:

```php
return jsonResponse([
    'success' => true,
    'message' => 'Data berhasil disimpan.',
    'data' => $data,
]);
```

Untuk validasi gagal:

```php
return jsonResponse([
    'success' => false,
    'message' => 'Validasi gagal.',
    'errors' => $errors,
], 422);
```

### 15.4 Standar Modal CRUD

- Form tambah dan edit data master sebaiknya memakai modal agar alur kerja cepat.
- Tombol tambah membuka modal kosong.
- Tombol edit mengambil data detail via AJAX lalu mengisi form modal.
- Tombol hapus/nonaktifkan menampilkan konfirmasi terlebih dahulu.
- Setelah sukses, modal ditutup dan DataTable di-refresh tanpa reload halaman.

### 15.5 Standar Laporan

- Laporan awal cukup tampil di halaman web dengan filter periode.
- Ekspor PDF/Excel dapat menjadi fitur tambahan setelah hasil web stabil.
- Laporan harus menampilkan tanggal cetak, periode, nama pimpinan bila digunakan, dan ringkasan pegawai terbaik.

### 15.6 Hal yang Perlu Dikonfirmasi Saat Implementasi

- Struktur database aktual yang sudah dibuat perlu dibandingkan dengan rancangan dokumen ini sebelum coding model.
- Nama kriteria C1 sampai C5 perlu dipastikan agar seed data awal sesuai kebutuhan penelitian.
- Role Pimpinan perlu diputuskan: login read-only atau hanya data penerima laporan.
- Format laporan final perlu dipastikan: tampilan web saja, PDF, Excel, atau kombinasi.
