diff --git a/app/Config/Routes.php b/app/Config/Routes.php index b92c716..5857cb1 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -26,6 +26,7 @@ $routes->delete('api/users/(:num)', '\App\Modules\Auth\Controllers\UserControlle $routes->get('login', 'LoginController::index'); $routes->post('logout', 'LogoutController::index'); $routes->get('dashboard', 'DashboardPageController::index', ['filter' => 'dashboard_page_auth']); +$routes->get('dashboard/profile', 'DashboardPageController::profile', ['filter' => 'dashboard_page_auth']); $routes->get('dashboard/attendance/reports', 'DashboardPageController::attendanceReports', ['filter' => 'dashboard_page_auth']); $routes->get('dashboard/attendance/report/(:num)', 'DashboardPageController::attendanceReport/$1', ['filter' => 'dashboard_page_auth']); $routes->get('dashboard/schedule/today', 'DashboardPageController::scheduleToday', ['filter' => 'dashboard_page_auth']); diff --git a/app/Controllers/DashboardPageController.php b/app/Controllers/DashboardPageController.php index ab2cf7c..f843b80 100644 --- a/app/Controllers/DashboardPageController.php +++ b/app/Controllers/DashboardPageController.php @@ -25,6 +25,23 @@ class DashboardPageController extends BaseController return $this->response->setBody(view('layouts/main', $data)); } + /** + * GET /dashboard/profile + * Halaman profile user: info akun + form ganti password. + */ + public function profile(): ResponseInterface + { + $authService = new AuthService(); + $user = $authService->currentUser(); + + $data = [ + 'user' => $user, + 'content' => view('dashboard/profile'), + ]; + + return $this->response->setBody(view('layouts/main', $data)); + } + /** * GET /dashboard/attendance/reports * Attendance reports index: pick date, list schedules, link to report per schedule. diff --git a/app/Database/Seeds/AuthSeeder.php b/app/Database/Seeds/AuthSeeder.php index ddebb22..d89d4e1 100644 --- a/app/Database/Seeds/AuthSeeder.php +++ b/app/Database/Seeds/AuthSeeder.php @@ -15,21 +15,37 @@ class AuthSeeder extends Seeder ['role_code' => 'GURU_MAPEL', 'role_name' => 'Guru Mata Pelajaran'], ['role_code' => 'ORANG_TUA', 'role_name' => 'Orang Tua'], ]; - $this->db->table('roles')->insertBatch($roles); + + foreach ($roles as $role) { + $exists = $this->db->table('roles')->where('role_code', $role['role_code'])->countAllResults(); + if ($exists === 0) { + $this->db->table('roles')->insert($role); + } + } + + $adminEmail = env('ADMIN_EMAIL', 'admin@example.com'); + $adminPassword = env('ADMIN_PASSWORD', 'admin123'); + + $userExists = $this->db->table('users')->where('email', $adminEmail)->countAllResults(); + if ($userExists > 0) { + return; + } $this->db->table('users')->insert([ 'name' => 'Admin', - 'email' => 'admin@example.com', - 'password_hash' => password_hash('admin123', PASSWORD_DEFAULT), + 'email' => $adminEmail, + 'password_hash' => password_hash($adminPassword, PASSWORD_DEFAULT), 'is_active' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]); $adminId = $this->db->insertID(); - $adminRoleId = $this->db->table('roles')->where('role_code', 'ADMIN')->get()->getRow()->id; - $this->db->table('user_roles')->insert([ - 'user_id' => $adminId, - 'role_id' => $adminRoleId, - ]); + $adminRole = $this->db->table('roles')->where('role_code', 'ADMIN')->get()->getRow(); + if ($adminRole) { + $this->db->table('user_roles')->insert([ + 'user_id' => $adminId, + 'role_id' => $adminRole->id, + ]); + } } } diff --git a/app/Modules/Auth/Controllers/AuthController.php b/app/Modules/Auth/Controllers/AuthController.php index bdb432b..f891a89 100644 --- a/app/Modules/Auth/Controllers/AuthController.php +++ b/app/Modules/Auth/Controllers/AuthController.php @@ -62,4 +62,41 @@ class AuthController extends BaseApiController } return $this->successResponse($user, 'Current user'); } + + /** + * POST /api/auth/change-password + * Body: { "current_password": "", "new_password": "" } + * User can only change their own password. + */ + public function changePassword(): ResponseInterface + { + $user = $this->authService->currentUser(); + if (!$user) { + return $this->errorResponse('Not authenticated', null, null, 401); + } + + $input = $this->request->getJSON(true); + $currentPassword = $input['current_password'] ?? ''; + $newPassword = $input['new_password'] ?? ''; + + if ($currentPassword === '' || $newPassword === '') { + return $this->errorResponse('Current password and new password are required', null, null, 400); + } + + if (strlen($newPassword) < 6) { + return $this->errorResponse('New password must be at least 6 characters', null, null, 400); + } + + $ok = $this->authService->changePassword( + (int) $user['id'], + $currentPassword, + $newPassword + ); + + if (!$ok) { + return $this->errorResponse('Current password is incorrect', null, null, 400); + } + + return $this->successResponse(null, 'Password changed successfully'); + } } diff --git a/app/Modules/Auth/Routes.php b/app/Modules/Auth/Routes.php index 764ebcf..09f6f8f 100644 --- a/app/Modules/Auth/Routes.php +++ b/app/Modules/Auth/Routes.php @@ -14,4 +14,5 @@ $routes->group('api/auth', ['namespace' => 'App\Modules\Auth\Controllers'], func $routes->post('login', 'AuthController::login'); $routes->post('logout', 'AuthController::logout'); $routes->get('me', 'AuthController::me'); + $routes->post('change-password', 'AuthController::changePassword'); }); diff --git a/app/Modules/Auth/Services/AuthService.php b/app/Modules/Auth/Services/AuthService.php index e4432e6..cd13d31 100644 --- a/app/Modules/Auth/Services/AuthService.php +++ b/app/Modules/Auth/Services/AuthService.php @@ -67,8 +67,9 @@ class AuthService public function currentUser(): ?array { $session = session(); - $userId = $session->get(self::SESSION_USER_ID); - if (!$userId) { + $rawUserId = $session->get(self::SESSION_USER_ID); + $userId = (int) $rawUserId; + if ($userId <= 0) { return null; } @@ -81,6 +82,31 @@ class AuthService return $this->userWithRoles($user); } + /** + * Change password for the given user. Verifies current password first. + * + * @param int $userId + * @param string $currentPassword + * @param string $newPassword + * @return bool True on success, false if current password wrong or user not found + */ + public function changePassword(int $userId, string $currentPassword, string $newPassword): bool + { + $user = $this->userModel->find($userId); + if (!$user || !$user->isActive()) { + return false; + } + + if (!password_verify($currentPassword, $user->password_hash)) { + return false; + } + + $hash = password_hash($newPassword, PASSWORD_DEFAULT); + $this->userModel->update($userId, ['password_hash' => $hash]); + + return true; + } + /** * Build user array with roles (no password). */ diff --git a/app/Views/dashboard/profile.php b/app/Views/dashboard/profile.php new file mode 100644 index 0000000..a4d05a5 --- /dev/null +++ b/app/Views/dashboard/profile.php @@ -0,0 +1,130 @@ + +
+
+

Profil Akun

+

Informasi akun dan pengaturan ganti password.

+
+ + +
+

Informasi Akun

+
+
+
Nama
+
+
+
+
Email
+
+
+
+
Role
+
+
+
+
+ + +
+

Ganti Password

+
+
+ + +
+
+ + +
+
+ + + +
+ +
+
+
+ +
+ + diff --git a/app/Views/partials/sidebar.php b/app/Views/partials/sidebar.php index b11146f..04a7b91 100644 --- a/app/Views/partials/sidebar.php +++ b/app/Views/partials/sidebar.php @@ -21,10 +21,14 @@ function nav_is_active(string $currentUri, string $path, bool $exact = true): bo SMAN 1 Garut