# 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**.