diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php
index c157693..52b9ce9 100644
--- a/app/Http/Controllers/Admin/UserController.php
+++ b/app/Http/Controllers/Admin/UserController.php
@@ -43,6 +43,7 @@ class UserController extends Controller
'name' => ['required', 'string', 'max:255'],
'username' => ['required', 'string', 'max:255', 'unique:users,username'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'],
+ 'phone' => ['nullable', 'string', 'max:20', 'regex:/^[0-9+\-\s()]*$/'],
'password' => ['required', 'string', 'min:6'],
'roles' => ['required', 'array', 'min:1'],
'roles.*' => ['in:admin,bk,wali_kelas,staff,other,kepala_sekolah,waka_kesiswaan,ketua_tim'],
@@ -67,6 +68,7 @@ class UserController extends Controller
'name' => ['required', 'string', 'max:255'],
'username' => ['required', 'string', 'max:255', 'unique:users,username,' . $user->id],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,' . $user->id],
+ 'phone' => ['nullable', 'string', 'max:20', 'regex:/^[0-9+\-\s()]*$/'],
'password' => ['nullable', 'string', 'min:6'],
'roles' => ['required', 'array', 'min:1'],
'roles.*' => ['in:admin,bk,wali_kelas,staff,other,kepala_sekolah,waka_kesiswaan,ketua_tim'],
diff --git a/app/Models/User.php b/app/Models/User.php
index 31cc434..3054c00 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -13,7 +13,7 @@ class User extends Authenticatable
public const ROLES = ['admin', 'bk', 'wali_kelas', 'staff', 'other', 'kepala_sekolah', 'waka_kesiswaan', 'ketua_tim'];
protected $fillable = [
- 'name', 'username', 'email', 'password', 'role', 'roles', 'is_active',
+ 'name', 'username', 'email', 'phone', 'password', 'role', 'roles', 'is_active',
];
protected $hidden = ['password', 'remember_token'];
diff --git a/bootstrap/app.php b/bootstrap/app.php
index 09f2091..cc4d5f0 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -60,6 +60,10 @@ return Application::configure(basePath: dirname(__DIR__))
if ($e instanceof \Illuminate\Auth\AuthenticationException) {
return null;
}
+ // Biarkan default menangani: redirect balik + pesan error validasi
+ if ($e instanceof \Illuminate\Validation\ValidationException) {
+ return null;
+ }
\Illuminate\Support\Facades\Log::error('Unhandled exception: ' . get_class($e) . ' - ' . $e->getMessage() . ' @ ' . $request->path());
return $htmlView('errors.500', 500, $request);
});
diff --git a/database/migrations/2026_08_03_092708_add_phone_to_users_table.php b/database/migrations/2026_08_03_092708_add_phone_to_users_table.php
new file mode 100644
index 0000000..3c93f47
--- /dev/null
+++ b/database/migrations/2026_08_03_092708_add_phone_to_users_table.php
@@ -0,0 +1,28 @@
+string('phone', 20)->nullable()->after('email');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('phone');
+ });
+ }
+};
diff --git a/resources/views/users/index.blade.php b/resources/views/users/index.blade.php
index 422db37..41aeea8 100644
--- a/resources/views/users/index.blade.php
+++ b/resources/views/users/index.blade.php
@@ -77,6 +77,9 @@
{{ $user->name }}
{{ $user->email }}
+ @if ($user->phone)
+
{{ $user->phone }}
+ @endif
@@ -119,7 +122,7 @@
-
+
+
+
+
+
+
+ Nomor untuk kontak & notifikasi (opsional)
+
{{-- Password --}}
@@ -331,7 +344,7 @@
function userManager() {
return {
modalOpen: false, isEditing: false, editId: null,
- formName: '', formUsername: '', formEmail: '', formPassword: '', formRoles: ['bk'], formActive: true,
+ formName: '', formUsername: '', formEmail: '', formPhone: '', formPassword: '', formRoles: ['bk'], formActive: true,
showPassword: false,
get roleInfo() {
const map = {
@@ -349,12 +362,12 @@ function userManager() {
},
openCreate() {
this.isEditing = false; this.editId = null;
- this.formName = ''; this.formUsername = ''; this.formEmail = ''; this.formPassword = '';
+ this.formName = ''; this.formUsername = ''; this.formEmail = ''; this.formPhone = ''; this.formPassword = '';
this.formRoles = ['bk']; this.formActive = true; this.showPassword = false; this.modalOpen = true;
},
- openEdit(id, name, username, email, rolesCsv, active) {
+ openEdit(id, name, username, email, phone, rolesCsv, active) {
this.isEditing = true; this.editId = id;
- this.formName = name; this.formUsername = username; this.formEmail = email;
+ this.formName = name; this.formUsername = username; this.formEmail = email; this.formPhone = phone;
this.formPassword = '';
this.formRoles = rolesCsv ? rolesCsv.split(',') : ['bk'];
this.formActive = active; this.showPassword = false; this.modalOpen = true;
diff --git a/tests/Feature/UserManagementTest.php b/tests/Feature/UserManagementTest.php
new file mode 100644
index 0000000..fb8e86c
--- /dev/null
+++ b/tests/Feature/UserManagementTest.php
@@ -0,0 +1,70 @@
+admin = User::factory()->create(['roles' => ['admin']]);
+ }
+
+ public function test_admin_menambah_user_dengan_nomor_hp(): void
+ {
+ $this->actingAs($this->admin)
+ ->post('/users', [
+ 'name' => 'Guru Baru',
+ 'username' => 'gurubaru',
+ 'email' => 'gurubaru@sekolah.sch.id',
+ 'phone' => '081234567890',
+ 'password' => 'rahasia123',
+ 'roles' => ['bk'],
+ 'is_active' => 1,
+ ])
+ ->assertRedirect();
+
+ $user = User::where('email', 'gurubaru@sekolah.sch.id')->firstOrFail();
+ $this->assertSame('081234567890', $user->phone);
+ }
+
+ public function test_admin_mengupdate_nomor_hp_user(): void
+ {
+ $user = User::factory()->create(['roles' => ['bk'], 'phone' => '081111111111']);
+
+ $this->actingAs($this->admin)
+ ->put("/users/{$user->id}", [
+ 'name' => $user->name,
+ 'username' => $user->username,
+ 'email' => $user->email,
+ 'phone' => '082222222222',
+ 'roles' => ['bk'],
+ 'is_active' => 1,
+ ])
+ ->assertRedirect();
+
+ $this->assertSame('082222222222', $user->refresh()->phone);
+ }
+
+ public function test_nomor_hp_tidak_boleh_karakter_aneh(): void
+ {
+ $this->actingAs($this->admin)
+ ->post('/users', [
+ 'name' => 'Guru Aneh',
+ 'username' => 'guruaneh',
+ 'email' => 'guruaneh@sekolah.sch.id',
+ 'phone' => 'abc; drop table',
+ 'password' => 'rahasia123',
+ 'roles' => ['bk'],
+ ])
+ ->assertSessionHasErrors('phone');
+
+ $this->assertDatabaseMissing('users', ['username' => 'guruaneh']);
+ }
+}
|