# Audit Kondisi Project SMAN 1 — Detail

**Tanggal:** 2026-02-23

---

## 1. Struktur Module

### Module yang sudah ada

| Module | Lokasi | Isi utama |
|--------|--------|-----------|
| **Academic** | `app/Modules/Academic/` | Classes, Students, Subjects, Teachers, Schedules, Lesson Slots, **DapodikSync** (controller + service + mapping model) |
| **Attendance** | `app/Modules/Attendance/` | AttendanceSession, Checkin, Report |
| **Auth** | `app/Modules/Auth/` | Login, User, Role, UserRole |
| **Dashboard** | `app/Modules/Dashboard/` | Dashboard, Schedule, Attendance, Realtime |
| **Devices** | `app/Modules/Devices/` | Device, Mobile, DeviceAuth |
| **Notification** | `app/Modules/Notification/` | Telegram, Parent, StudentParent |
| **Geo** | `app/Modules/Geo/` | Zone, GeoFence |

### Cek per modul yang Anda tanyakan

| Modul | Status | Keterangan |
|-------|--------|------------|
| **Classes** | ✅ Ada | Di dalam **Academic**: ClassModel, ClassEntity, ClassController (CRUD), route GET/POST/PUT/DELETE `/api/academic/classes`, halaman `/dashboard/academic/classes` |
| **Students** | ✅ Ada | Di dalam **Academic**: StudentModel, StudentEntity, StudentController (CRUD + paginasi), route GET/POST/PUT/DELETE `/api/academic/students`, halaman `/dashboard/academic/students` |
| **Attendance** | ✅ Ada | Module **Attendance** terpisah: AttendanceSessionModel, AttendanceCheckinService, AttendanceReportService, dll. Tabel: `attendance_sessions` (bukan `attendance_logs`) |
| **Schedule** | ✅ Ada | Di dalam **Academic**: ScheduleModel, ScheduleManagementController, ScheduleResolverService, lesson_slots. Tabel: `schedules`, `lesson_slots` |
| **DapodikSync** | ✅ Ada | Di dalam **Academic**: DapodikClient, DapodikSyncService, DapodikSyncController, DapodikRombelMappingModel. Route: POST sync/students, GET/PUT rombels. Halaman `/dashboard/academic/dapodik` |

**Kesimpulan:** Semua modul yang Anda sebut (Classes, Students, Attendance, Schedule, DapodikSync) **sudah ada**. Tidak ada modul terpisah bernama "Classes" atau "Students"; semuanya di bawah **Academic**.

---

## 2. Database

### Tabel yang ada (beserta struktur)

**Catatan:** Tidak ada tabel bernama `attendance_logs`. Yang dipakai: **`attendance_sessions`**.

### 2.1 Tabel `classes`

| Field | Type | Relasi |
|-------|------|--------|
| id | INT PK AUTO_INCREMENT | - |
| name | VARCHAR(100) | - (rombel, mis. A, B, C) |
| grade | VARCHAR(50) | - (tingkat, mis. 10, X) |
| major | VARCHAR(50) NOT NULL DEFAULT 'IPA' | - (jurusan) |
| wali_user_id | INT NULL | FK → users.id (SET NULL) |
| created_at | DATETIME NULL | - |
| updated_at | DATETIME NULL | - |

**Relasi keluar:** `students.class_id`, `schedules.class_id`, `dapodik_rombel_mappings.class_id` → classes.id

---

### 2.2 Tabel `students`

| Field | Type | Relasi |
|-------|------|--------|
| id | INT PK AUTO_INCREMENT | - |
| nisn | VARCHAR(50) NOT NULL UNIQUE | - |
| name | VARCHAR(255) | - |
| gender | VARCHAR(1) NULL | L/P |
| class_id | INT NULL | FK → classes.id (SET NULL) — **nullable untuk unmapped** |
| is_active | TINYINT(1) DEFAULT 1 | - |
| parent_link_code | VARCHAR | (dari migration lain) |
| created_at | DATETIME NULL | - |
| updated_at | DATETIME NULL | - |

**Relasi:** class_id → classes.id (boleh NULL untuk siswa belum di-map dari Dapodik).

---

### 2.3 Tabel `attendance_sessions` (bukan attendance_logs)

| Field | Type | Relasi |
|-------|------|--------|
| id | INT PK | - |
| student_id | INT | FK → students.id |
| schedule_id | INT NULL | FK → schedules.id |
| device_id | INT | FK → devices.id |
| attendance_date | DATE | (dari migration unik) |
| checkin_at | DATETIME | - |
| latitude, longitude, confidence | DECIMAL | - |
| status | ENUM(PRESENT, LATE, OUTSIDE_ZONE, NO_SCHEDULE, INVALID_DEVICE) | - |
| created_at, updated_at | DATETIME NULL | - |

**Relasi:** student_id → students, schedule_id → schedules, device_id → devices.

---

### 2.4 Tabel `schedules`

| Field | Type | Relasi |
|-------|------|--------|
| id | INT PK | - |
| class_id | INT | FK → classes.id |
| lesson_slot_id | INT NULL | FK → lesson_slots.id |
| subject_id | INT | FK → subjects.id |
| teacher_user_id | INT NULL | FK → users.id |
| teacher_name | VARCHAR(255) | - |
| day_of_week | TINYINT (1=Senin, 7=Minggu) | - |
| start_time, end_time | TIME | - |
| room | VARCHAR(100) NULL | - |
| is_active | TINYINT DEFAULT 1 | - |
| created_at, updated_at | DATETIME NULL | - |

**Unique:** (class_id, lesson_slot_id, day_of_week).

**Relasi:** class_id → classes, subject_id → subjects, teacher_user_id → users, lesson_slot_id → lesson_slots.

---

### 2.5 Tabel tambahan terkait

- **dapodik_rombel_mappings:** id, dapodik_rombel (UNIQUE), class_id (FK → classes, NULL), last_seen_at, created_at, updated_at.
- **lesson_slots:** id, slot_number, start_time, end_time, is_active.
- **subjects:** id, name, code (nullable).

---

## 3. Flow Master Data

### 3.1 Kelas: input manual atau belum?

- **Input kelas:** **manual** lewat halaman **Classes** (`/dashboard/academic/classes`), hanya untuk role ADMIN.
- Form: Tingkat (text), Jurusan (text), Rombel (text), Wali Kelas (dropdown user).
- Data disimpan ke tabel **classes** (name = rombel, grade, major, wali_user_id).
- **Jika belum pernah input:** tabel `classes` **kosong** → tidak ada pilihan kelas di mana pun (Schedule Builder, Students, dll.).

### 3.2 Schedule Builder mengambil data kelas dari mana?

- **Dari API:** `GET /api/academic/classes` (admin_only).
- Halaman Schedule Builder index (`/dashboard/academic/schedule-builder`) memanggil API ini lalu mengisi **dropdown "Kelas"** (option value = class id, text = nama/rombel).
- Data sumber: **tabel `classes`** via ClassController::index().

### 3.3 Kenapa dropdown kelas bisa kosong?

- **Penyebab:** Tabel **classes tidak punya data** (belum ada satu baris pun).
- **Alur:** Tidak ada kelas → API `/api/academic/classes` mengembalikan array kosong → dropdown hanya berisi "— Pilih kelas —" → Open Builder tidak bisa dipakai.
- **Solusi:** Login sebagai ADMIN → buka **Academic → Classes** → tambah minimal satu kelas (mis. Tingkat 10, Jurusan IPA, Rombel 1). Setelah itu dropdown di Schedule Builder akan terisi.

---

## 4. Dapodik Integration

| Aspek | Status | Keterangan |
|-------|--------|------------|
| **Controller Dapodik Sync** | ✅ Ada | `App\Modules\Academic\Controllers\DapodikSyncController` |
| **Endpoint API** | ✅ Ada | POST `/api/academic/dapodik/sync/students`, GET `/api/academic/dapodik/rombels`, PUT `/api/academic/dapodik/rombels/{id}` (semua admin_only) |
| **Rencana vs realisasi** | ✅ Sudah diimplementasi | DapodikClient (getSekolah, getPesertaDidik), DapodikSyncService (sync siswa + rombel mapping), halaman Dapodik di dashboard |

**Env yang dipakai:** DAPODIK_BASE_URL, DAPODIK_TOKEN, DAPODIK_NPSN (di .env).

---

## 5. Student Page

### Apakah halaman Student sudah tampil?

- **Route:** ✅ Ada — `GET /dashboard/academic/students` → `DashboardAcademicController::students`, filter `dashboard_admin_page`.
- **Controller:** ✅ Ada — method `students()` me-render view `dashboard/students`.
- **View:** ✅ Ada — `app/Views/dashboard/students.php` (tabel, filter, paginasi, modal tambah/edit/hapus).
- **Query/relasi:** ✅ API GET `/api/academic/students` jalan; join ke `classes` untuk class_label; `class_id` boleh NULL (tidak gagal).

### Kenapa bisa “belum tampil”?

Kemungkinan:

1. **Bukan ADMIN** — halaman dan menu Students hanya untuk role ADMIN. Jika login bukan ADMIN, menu Academic (termasuk Students) tidak muncul.
2. **URL salah** — harus akses tepat: `/dashboard/academic/students` (mis. `http://localhost/sman1/backend/public/dashboard/academic/students`).
3. **Session/redirect** — jika belum login atau filter redirect, akan ke halaman login/dashboard.
4. **Data kosong** — halaman tetap tampil; yang kosong hanya **tabel siswa** (belum ada data siswa). Bukan penyebab “halaman tidak tampil”.

**Kesimpulan:** Secara kode, route, controller, view, dan relasi **sudah ada dan konsisten**. Kalau “belum tampil”, paling mungkin karena **bukan user ADMIN** atau **salah URL/base URL**.

---

## 6. Problem Diagnosis

### “Schedule Builder meminta kelas, tapi tidak ada tempat input kelas”

- **Faktanya:** **Sudah ada tempat input kelas**, yaitu halaman **Classes** (`/dashboard/academic/classes`), hanya untuk ADMIN.
- **Alur yang benar:**
  1. ADMIN buka **Academic → Classes** (atau lewat **Academic → Settings** → kartu Classes).
  2. Klik **Tambah Kelas** → isi Tingkat, Jurusan, Rombel, (opsional) Wali Kelas → Simpan.
  3. Setelah ada minimal 1 kelas, buka **Schedule Builder** → dropdown **Kelas** terisi → pilih kelas → Open Builder.

Jadi masalahnya bukan “tidak ada tempat input”, melainkan:

- **User belum pernah mengisi kelas** di halaman Classes, atau  
- **Menu Classes tidak terlihat** (mis. karena tidak login sebagai ADMIN).

**Rekomendasi:** Di halaman Schedule Builder index bisa ditambah kalimat singkat: “Jika dropdown kosong, tambah dulu kelas di **Academic → Classes**.”

---

## 7. Ringkasan Kondisi Project

| Aspek | Status | Keterangan |
|-------|--------|------------|
| **Module Academic (Classes, Students, Subjects, Teachers, Schedules, Lesson Slots)** | ✅ Sudah jadi | CRUD API + halaman dashboard per entitas |
| **Module Attendance** | ✅ Sudah jadi | attendance_sessions, check-in, report |
| **Module Schedule (jadwal per kelas)** | ✅ Sudah jadi | Schedule Builder, bulk save, lesson_slots |
| **Module DapodikSync** | ✅ Sudah jadi | Sync siswa, mapping rombel → class, halaman Dapodik |
| **Tabel classes, students, schedules, attendance_sessions** | ✅ Sudah jadi | Struktur + relasi jelas (termasuk lesson_slots, subjects, dapodik_rombel_mappings) |
| **Input kelas manual** | ✅ Sudah jadi | Halaman Classes, form lengkap |
| **Schedule Builder pakai data kelas** | ✅ Sudah jadi | Dari GET /api/academic/classes |
| **Halaman Students** | ✅ Sudah jadi | Route, controller, view, API, paginasi, filter |
| **Dropdown kelas kosong** | ⚠️ Setengah jadi | Fitur jalan; kosong karena **belum ada data kelas** di DB |
| **Dapodik sebagai sumber master kelas** | ⚠️ Setengah jadi | Saat ini Dapodik hanya **sync siswa** + mapping **rombel → class**. Kelas internal tetap **buat manual** di Classes; belum ada “sync rombel → buat kelas otomatis” |

---

## 8. Arsitektur saran: Dapodik sebagai sumber utama master data

Target alur: **DAPODIK → Sync → Classes → Students → Schedule → Attendance**.

### Kondisi saat ini

- **Classes:** input manual; tidak ada sync dari Dapodik.
- **Students:** bisa dari Dapodik (sync siswa + mapping rombel → class_id). class_id bisa NULL (unmapped).
- **Schedule & Attendance:** sudah mengandalkan classes + students.

### Opsi arsitektur

**Opsi A — Kelas tetap manual (minimal change)**  
- Tetap: Classes diisi manual (grade + major + rombel).  
- Dapodik: sync siswa + mapping **dapodik_rombel → class_id**.  
- Sesuai jika: rombel Dapodik banyak dan Anda ingin satu rombel Dapodik = satu kelas internal yang sudah Anda buat manual.

**Opsi B — Sync rombel Dapodik → buat/update kelas (disarankan untuk “Dapodik sumber utama”)**

1. **Tambah sync kelas/rombel dari Dapodik**
   - Endpoint Dapodik: daftar rombongan belajar (rombel) per sekolah.
   - Logic: untuk setiap rombel Dapodik, **buat atau update** satu baris di `classes` (mis. parse nama rombel jadi grade + major + name/rombel), plus **tetap** isi `dapodik_rombel_mappings` (dapodik_rombel → class_id) agar siswa sync bisa map ke kelas yang sama.
   - Aturan: satu `dapodik_rombel` = satu `class_id`; kelas bisa dibuat otomatis dari nama rombel Dapodik (mis. "10 IPA 1" → grade=10, major=IPA, name=1).

2. **Urutan sync disarankan**
   - **Sync rombel/kelas dulu** (baca Dapodik → insert/update `classes` + `dapodik_rombel_mappings`).
   - **Lalu sync siswa** (seperti sekarang); mapping rombel → class_id sudah terisi, sehingga siswa dapat class_id otomatis.

3. **Tetap boleh edit manual**
   - Halaman **Classes** tetap dipakai untuk koreksi/naming (grade, major, rombel, wali).
   - Setelah sync, admin bisa ubah nama/struktur kelas jika perlu.

**Opsi C — Hybrid (seperti B + flag sumber)**  
- Tambah kolom mis. `source` (manual / dapodik) di `classes` supaya bisa bedakan kelas yang dari Dapodik vs yang buat manual.

### Rekomendasi singkat

- **Langkah 1:** Pastikan **ada data kelas** (manual) dulu agar Schedule Builder dan dropdown siswa tidak kosong.  
- **Langkah 2:** Jika ingin Dapodik sebagai sumber utama kelas, tambah **sync rombel Dapodik → classes** (Opsi B); jadwalkan sync rombel dulu, baru sync siswa.  
- **Langkah 3:** Tetap pakai **Dapodik Sync** yang sudah ada untuk siswa dan mapping rombel → class; setelah kelas bisa dari Dapodik, mapping itu akan mengisi class_id siswa tanpa perlu input kelas manual dulu.

Dengan ini, arsitektur bergerak ke: **DAPODIK → Sync (rombel + siswa) → Classes & Students terisi → Schedule (manual di builder) → Attendance**.
