diff --git a/app/Components/User/Manager.php b/app/Components/User/Manager.php new file mode 100644 index 0000000..baf87e0 --- /dev/null +++ b/app/Components/User/Manager.php @@ -0,0 +1,141 @@ +allRoles = Role::all()->toArray(); + } + + public function updatedPerPage(int $value): void + { + $this->resetPage(); + } + + public function updatedSearch(string $value): void + { + $this->resetPage(); + } + + #[On('userSaved')] + public function showIndex(): void + { + $this->mode = 'index'; + $this->resetPage(); + $this->resetForm(); + } + + public function showForm(?int $id = null): void + { + $this->mode = 'form'; + $this->editingId = $id; + + if ($id) { + $user = User::with('roles')->findOrFail($id); + $this->fullname = $user->fullname; + $this->email = $user->email; + $this->roles = $user->roles->pluck('name')->toArray(); + } else { + $this->resetForm(); + } + } + + protected function resetForm(): void + { + $this->fullname = ''; + $this->email = ''; + $this->password = ''; + $this->password_confirmation = ''; + $this->roles = []; + $this->editingId = null; + } + + public function delete(int $id): void + { + User::findOrFail($id)->delete(); + session()->flash('message', 'User deleted.'); + $this->resetPage(); + } + + public function save(): void + { + $rules = [ + 'fullname' => ['required', 'string', 'max:255'], + 'email' => ['required', 'email', 'max:255', Rule::unique('users', 'email')->ignore($this->editingId)], + 'roles' => ['nullable', 'array'], + 'roles.*' => ['string', Rule::exists('roles', 'name')], + ]; + + if (! $this->editingId) { + $rules['password'] = ['required', 'string', 'min:6', 'confirmed']; + } elseif ($this->password) { + $rules['password'] = ['string', 'min:6', 'confirmed']; + } + + $this->validate($rules); + + $user = $this->editingId + ? User::findOrFail($this->editingId) + : new User; + + $user->fullname = $this->fullname; + $user->email = $this->email; + + if ($this->password) { + $user->password = $this->password; + } + + $user->save(); + $user->syncRoles($this->roles); + + session()->flash('message', 'User saved.'); + $this->dispatch('userSaved'); + } + + public function render() + { + $users = User::with('roles') + ->when( + $this->search, + fn($q) => $q + ->where('fullname', 'like', "%{$this->search}%") + ->orWhere('email', 'like', "%{$this->search}%") + ) + ->orderByDesc('id') + ->paginate($this->perPage); + + return view('components.user.manager', [ + 'users' => $users, + 'allRoles' => collect($this->allRoles), + 'title' => $this->mode === 'index' + ? 'Users' + : ($this->editingId ? 'Edit User' : 'Create User'), + ]); + } +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..f8d1206 --- /dev/null +++ b/app/Http/Controllers/UserController.php @@ -0,0 +1,13 @@ + 'hashed', ]; } + // Mutator to hash the password using md5 before saving. + // Note: Using md5 for passwords is insecure. Consider using bcrypt or Argon2 instead. + public function setPasswordAttribute($value) + { + if ($value !== null && $value !== '') { + $this->attributes['password'] = md5($value); + } + } } diff --git a/resources/views/admin/users/manager.blade.php b/resources/views/admin/users/manager.blade.php new file mode 100644 index 0000000..f257ac2 --- /dev/null +++ b/resources/views/admin/users/manager.blade.php @@ -0,0 +1,5 @@ +@extends('layouts.app') + +@section('content') + +@endsection diff --git a/resources/views/components/skeleton-table.blade.php b/resources/views/components/skeleton-table.blade.php index df6bee8..eb9865b 100644 --- a/resources/views/components/skeleton-table.blade.php +++ b/resources/views/components/skeleton-table.blade.php @@ -1,4 +1,10 @@ -@props(['columns', 'rows', 'height']) +{{-- resources/views/components/skeleton-table.blade.php --}} +@props([ + 'columns', + 'rows', + 'height', + 'pages' => 3, // Default to 3 pages if not specified +])
@@ -23,4 +29,11 @@
+ + {{-- Skeleton pagination --}} +
+ @for ($i = 0; $i < $pages; $i++) +
+ @endfor +
diff --git a/resources/views/components/user/manager.blade.php b/resources/views/components/user/manager.blade.php new file mode 100644 index 0000000..44033d8 --- /dev/null +++ b/resources/views/components/user/manager.blade.php @@ -0,0 +1,184 @@ +{{-- resources/views/components/user/manager.blade.php --}} +
+
+
+ {{-- Header --}} +
+
{{ $title }}
+ @if ($mode === 'index') +
+
+ +
+
+ +
+ +
+ @else + + @endif +
+ + {{-- Body --}} +
+ @if (session()->has('message')) +
{{ session('message') }}
+ @endif + + @if ($mode === 'index') + {{-- Skeleton with pagination placeholders --}} + + + {{-- Actual table --}} +
+ + + + + + + + + + + + + @forelse($users as $u) + + + + + + + + + @empty + + + + @endforelse + +
IDFull NameEmailRolePhoneActions
{{ $u->id }}{{ $u->fullname }}{{ $u->email }}{{ $u->roles->pluck('name')->join(', ') }}{{ $u->phone }} + + +
No users found.
+
+ + {{-- Real pagination --}} +
+ @if ($users->lastPage() > 1) + {{ $users->links() }} + @endif +
+ @else + {{-- Form create/edit --}} + +
+
+
+ + + @error('fullname') +
{{ $message }}
+ @enderror +
+
+ + + @error('email') +
{{ $message }}
+ @enderror +
+
+ + + @error('password') +
{{ $message }}
+ @enderror +
+
+ + +
+
+ + + @error('roles') +
{{ $message }}
+ @enderror +
+
+ + +
+
+
+ @endif +
+
+
+ + @script + + @endscript +
diff --git a/resources/views/layouts/partials/sidebar.blade.php b/resources/views/layouts/partials/sidebar.blade.php index 814ffbb..dcdc421 100644 --- a/resources/views/layouts/partials/sidebar.blade.php +++ b/resources/views/layouts/partials/sidebar.blade.php @@ -76,7 +76,7 @@ diff --git a/routes/web.php b/routes/web.php index 23d6328..da41cf9 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,6 +5,7 @@ use App\Http\Controllers\Auth\LoginController; use App\Http\Controllers\DashboardController; use App\Http\Controllers\PermissionController; use App\Http\Controllers\RoleController; +use App\Http\Controllers\UserController; Route::get('dashboard', [DashboardController::class, 'index']) ->name('dashboard') @@ -12,10 +13,13 @@ Route::get('dashboard', [DashboardController::class, 'index']) Route::middleware('auth')->group(function () { // Permissions - Route::get('/permissions', [PermissionController::class, 'manager']) - ->name('permissions.index'); + Route::get('/permissions', [PermissionController::class, 'manager'])->name('permissions.index'); // Roles - Route::get('/roles', [RoleController::class, 'manager']) - ->name('roles.index'); + Route::get('/roles', [RoleController::class, 'manager'])->name('roles.index'); + + //User + Route::middleware('auth')->group(function () { + Route::get('/users', [UserController::class, 'manager'])->name('users.index'); + }); });