Update User
This commit is contained in:
@@ -5,6 +5,9 @@ namespace App\Http\Controllers\Web\Konfigurasi;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\Profile;
|
||||||
|
use App\Enums\Role;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
@@ -14,47 +17,112 @@ class UserController extends Controller
|
|||||||
{
|
{
|
||||||
$query = User::orderBy('created_at', 'desc');
|
$query = User::orderBy('created_at', 'desc');
|
||||||
|
|
||||||
if ($request->search) {
|
$tab = $request->get('tab', 'active');
|
||||||
$query->where('name', 'like', '%' . $request->search . '%')
|
if ($tab === 'pending') {
|
||||||
->orWhere('email', 'like', '%' . $request->search . '%');
|
$query->whereNull('email_verified_at');
|
||||||
|
} else {
|
||||||
|
$query->whereNotNull('email_verified_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->filled('search')) {
|
||||||
|
$search = $request->search;
|
||||||
|
$query->where(function($q) use ($search) {
|
||||||
|
$q->where('name', 'like', '%' . $search . '%')
|
||||||
|
->orWhere('email', 'like', '%' . $search . '%');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->filled('role')) {
|
||||||
|
$query->where('role', $request->role);
|
||||||
}
|
}
|
||||||
|
|
||||||
$users = $query->paginate($request->per_page ?? 10);
|
$users = $query->paginate($request->per_page ?? 10);
|
||||||
|
|
||||||
return view('master.users.index', compact('users'));
|
$totalUsers = User::count();
|
||||||
|
$activeUsers = User::whereNotNull('email_verified_at')->count();
|
||||||
|
$pendingUsers = User::whereNull('email_verified_at')->count();
|
||||||
|
|
||||||
|
$roles = Role::cases();
|
||||||
|
|
||||||
|
return view('konfigurasi.user.index', compact('users', 'totalUsers', 'activeUsers', 'pendingUsers', 'roles', 'tab'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function verify($id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$user = User::findOrFail($id);
|
||||||
|
$user->email_verified_at = now();
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'User berhasil diverifikasi dan diaktifkan.'
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Gagal verifikasi user.'
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'name' => 'required|string|max:255',
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'email' => 'required|email|unique:users,email|max:255',
|
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||||||
'password' => 'required|min:6',
|
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||||
'role' => 'required|string|max:255',
|
'role' => ['required', 'string'],
|
||||||
|
], [
|
||||||
|
'password.confirmed' => 'Konfirmasi password tidak cocok!',
|
||||||
|
'password.min' => 'Password minimal 8 karakter!',
|
||||||
|
'email.unique' => 'Email sudah terdaftar.',
|
||||||
|
'required' => 'Kolom :attribute wajib diisi.'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return response()->json(['status' => 'error', 'errors' => $validator->errors()], 422);
|
return response()->json(['status' => 'error', 'errors' => $validator->errors()], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
User::create([
|
try {
|
||||||
|
DB::transaction(function () use ($request) {
|
||||||
|
|
||||||
|
$user = User::create([
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
'email' => $request->email,
|
'email' => $request->email,
|
||||||
'password' => Hash::make($request->password), // Enkripsi password
|
'password' => Hash::make($request->password),
|
||||||
'role' => $request->role,
|
'role' => $request->role,
|
||||||
|
'created_by' => auth()->id(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response()->json(['status' => 'success', 'message' => 'User berhasil ditambahkan']);
|
Profile::create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'role' => $request->role,
|
||||||
|
'nama' => $request->name,
|
||||||
|
'email' => $request->email,
|
||||||
|
'created_by' => auth()->id(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'User baru berhasil ditambahkan.'
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Gagal menyimpan data: ' . $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show($id)
|
public function show($id)
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$user = User::findOrFail($id);
|
$user = User::findOrFail($id);
|
||||||
|
|
||||||
return response()->json(['status' => 'success', 'data' => $user]);
|
return response()->json(['status' => 'success', 'data' => $user]);
|
||||||
} catch (\Exception $e) {
|
|
||||||
return response()->json(['status' => 'error', 'message' => 'User tidak ditemukan'], 404);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request, $id)
|
public function update(Request $request, $id)
|
||||||
@@ -62,9 +130,6 @@ class UserController extends Controller
|
|||||||
$user = User::findOrFail($id);
|
$user = User::findOrFail($id);
|
||||||
|
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'name' => 'required|string|max:255',
|
|
||||||
'email' => 'required|email|max:255|unique:users,email,' . $id, // Pengecualian email unik untuk ID ini
|
|
||||||
'role' => 'required|string|max:255',
|
|
||||||
'password' => 'nullable|min:6',
|
'password' => 'nullable|min:6',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -72,15 +137,7 @@ class UserController extends Controller
|
|||||||
return response()->json(['status' => 'error', 'errors' => $validator->errors()], 422);
|
return response()->json(['status' => 'error', 'errors' => $validator->errors()], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->name = $request->name;
|
|
||||||
$user->email = $request->email;
|
|
||||||
$user->role = $request->role;
|
|
||||||
|
|
||||||
// Update password HANYA JIKA diisi
|
|
||||||
if ($request->filled('password')) {
|
|
||||||
$user->password = Hash::make($request->password);
|
$user->password = Hash::make($request->password);
|
||||||
}
|
|
||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
return response()->json(['status' => 'success', 'message' => 'User berhasil diupdate']);
|
return response()->json(['status' => 'success', 'message' => 'User berhasil diupdate']);
|
||||||
|
|||||||
+2
-1
@@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
|
use App\Enums\Role;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
@@ -32,7 +33,7 @@ class User extends Authenticatable
|
|||||||
return [
|
return [
|
||||||
'email_verified_at' => 'datetime',
|
'email_verified_at' => 'datetime',
|
||||||
'password' => 'hashed',
|
'password' => 'hashed',
|
||||||
'role' => \App\Enums\Role::class,
|
'role' => Role::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasAddUser" aria-labelledby="offcanvasAddUserLabel">
|
||||||
|
<div class="offcanvas-header border-bottom">
|
||||||
|
<h5 id="offcanvasAddUserLabel" class="offcanvas-title">Tambah User Baru</h5>
|
||||||
|
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
|
||||||
|
<form class="add-new-user pt-0" id="formAddUser" action="{{ route('users.store') }}" method="POST">
|
||||||
|
@csrf
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label" for="add-user-fullname">Nama Lengkap</label>
|
||||||
|
<input type="text" class="form-control" id="add-user-fullname" placeholder="John Doe" name="name" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label" for="add-user-email">Email</label>
|
||||||
|
<input type="email" id="add-user-email" class="form-control" placeholder="john.doe@example.com" name="email" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label" for="add-user-role">User Role</label>
|
||||||
|
<select id="add-user-role" class="form-select" name="role" required>
|
||||||
|
<option value="">Pilih Role</option>
|
||||||
|
@foreach($roles as $roleEnum)
|
||||||
|
<option value="{{ $roleEnum->value }}">{{ ucfirst(str_replace('_', ' ', $roleEnum->value)) }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4 form-password-toggle">
|
||||||
|
<label class="form-label" for="add-user-password">Password</label>
|
||||||
|
<div class="input-group input-group-merge">
|
||||||
|
<input type="password" id="add-user-password" name="password" class="form-control" placeholder="············" required />
|
||||||
|
<span class="input-group-text cursor-pointer" onclick="togglePassword(this)">
|
||||||
|
<i class="ti tabler-eye-off"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4 form-password-toggle">
|
||||||
|
<label class="form-label" for="add-user-password-confirm">Konfirmasi Password</label>
|
||||||
|
<div class="input-group input-group-merge">
|
||||||
|
<input type="password" id="add-user-password-confirm" name="password_confirmation" class="form-control" placeholder="············" required />
|
||||||
|
<span class="input-group-text cursor-pointer" onclick="togglePassword(this)">
|
||||||
|
<i class="ti tabler-eye-off"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 d-flex gap-3">
|
||||||
|
<button type="submit" class="btn btn-primary flex-grow-1 data-submit">Simpan</button>
|
||||||
|
<button type="reset" class="btn btn-label-secondary flex-grow-1" data-bs-dismiss="offcanvas">Batal</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasEdit" aria-labelledby="offcanvasEditLabel">
|
||||||
|
<div class="offcanvas-header border-bottom">
|
||||||
|
<h5 id="offcanvasEditLabel" class="offcanvas-title">Ubah Password User</h5>
|
||||||
|
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
|
||||||
|
<form id="formEdit" method="POST">
|
||||||
|
@csrf
|
||||||
|
@method('PUT')
|
||||||
|
|
||||||
|
<div class="alert alert-label-info mb-6" role="alert">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<span class="ti tabler-user ti-xs me-2"></span>
|
||||||
|
<span class="fw-medium text-heading">Nama:</span>
|
||||||
|
<span class="ms-auto fw-bold" id="editUserName"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-2">
|
||||||
|
|
||||||
|
<div class="d-flex align-items-center mt-2">
|
||||||
|
<span class="ti tabler-mail ti-xs me-2"></span>
|
||||||
|
<span class="fw-medium text-heading">Email:</span>
|
||||||
|
<span class="ms-auto" id="editEmail"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4 form-password-toggle">
|
||||||
|
<label class="form-label" for="edit-password">Password Baru</label>
|
||||||
|
<div class="input-group input-group-merge">
|
||||||
|
<input type="password" id="edit-password" name="password" class="form-control" placeholder="············" required />
|
||||||
|
<span class="input-group-text cursor-pointer" onclick="togglePassword(this)">
|
||||||
|
<i class="ti tabler-eye-off"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6 form-password-toggle">
|
||||||
|
<label class="form-label" for="edit-password-confirm">Konfirmasi Password Baru</label>
|
||||||
|
<div class="input-group input-group-merge">
|
||||||
|
<input type="password" id="edit-password-confirm" name="password_confirmation" class="form-control" placeholder="············" required />
|
||||||
|
<span class="input-group-text cursor-pointer" onclick="togglePassword(this)">
|
||||||
|
<i class="ti tabler-eye-off"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-3">
|
||||||
|
<button type="submit" class="btn btn-primary flex-grow-1">Update Password</button>
|
||||||
|
<button type="button" class="btn btn-label-secondary flex-grow-1" data-bs-dismiss="offcanvas">Batal</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,445 @@
|
|||||||
|
@extends('layout.main')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
|
|
||||||
|
<div class="row g-6 mb-6">
|
||||||
|
<div class="col-sm-6 col-xl-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-start justify-content-between">
|
||||||
|
<div class="content-left">
|
||||||
|
<span class="text-heading">Total Users</span>
|
||||||
|
<div class="d-flex align-items-center my-1">
|
||||||
|
<h4 class="mb-0 me-2">{{ number_format($totalUsers ?? 0) }}</h4>
|
||||||
|
</div>
|
||||||
|
<small class="mb-0">All registered accounts</small>
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<span class="avatar-initial rounded bg-label-primary">
|
||||||
|
<i class="icon-base ti tabler-users icon-26px"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 col-xl-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-start justify-content-between">
|
||||||
|
<div class="content-left">
|
||||||
|
<span class="text-heading">Active Users</span>
|
||||||
|
<div class="d-flex align-items-center my-1">
|
||||||
|
<h4 class="mb-0 me-2">{{ number_format($activeUsers ?? 0) }}</h4>
|
||||||
|
</div>
|
||||||
|
<small class="mb-0">Verified Emails</small>
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<span class="avatar-initial rounded bg-label-success">
|
||||||
|
<i class="icon-base ti tabler-user-check icon-26px"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 col-xl-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-start justify-content-between">
|
||||||
|
<div class="content-left">
|
||||||
|
<span class="text-heading">Pending Users</span>
|
||||||
|
<div class="d-flex align-items-center my-1">
|
||||||
|
<h4 class="mb-0 me-2">{{ number_format($pendingUsers ?? 0) }}</h4>
|
||||||
|
</div>
|
||||||
|
<small class="mb-0">Unverified Emails</small>
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<span class="avatar-initial rounded bg-label-warning">
|
||||||
|
<i class="icon-base ti tabler-user-search icon-26px"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="nav nav-pills mb-4" role="tablist">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {{ $tab == 'active' ? 'active' : '' }}" href="{{ route('users.index', ['tab' => 'active', 'role' => request('role'), 'search' => request('search')]) }}">
|
||||||
|
<i class="ti tabler-user-check me-1"></i> Active Users
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {{ $tab == 'pending' ? 'active' : '' }}" href="{{ route('users.index', ['tab' => 'pending', 'role' => request('role'), 'search' => request('search')]) }}">
|
||||||
|
<i class="ti tabler-clock me-1"></i> Pending Verifications
|
||||||
|
@if(($pendingUsers ?? 0) > 0)
|
||||||
|
<span class="badge rounded-pill badge-center h-px-20 w-px-20 bg-danger ms-1">{{ $pendingUsers }}</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="card-title mb-0">Daftar Pengguna</h5>
|
||||||
|
<button class="btn btn-sm btn-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasAddUser">
|
||||||
|
<i class="ti tabler-plus me-1"></i> Add New User
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="GET" action="{{ route('users.index') }}" id="filterForm">
|
||||||
|
<input type="hidden" name="tab" value="{{ $tab }}">
|
||||||
|
<div class="card-body border-bottom pt-4">
|
||||||
|
<div class="row g-3 align-items-center">
|
||||||
|
<div class="col-md-auto">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<label class="me-2 text-nowrap">Show</label>
|
||||||
|
<select name="per_page" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
|
||||||
|
<option value="10" {{ request('per_page') == 10 ? 'selected' : '' }}>10</option>
|
||||||
|
<option value="25" {{ request('per_page') == 25 ? 'selected' : '' }}>25</option>
|
||||||
|
<option value="50" {{ request('per_page') == 50 ? 'selected' : '' }}>50</option>
|
||||||
|
</select>
|
||||||
|
<label class="ms-2 text-nowrap">entries</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3">
|
||||||
|
<select name="role" class="form-select form-select-sm" onchange="this.form.submit()">
|
||||||
|
<option value="">-- All Roles --</option>
|
||||||
|
@foreach($roles as $roleEnum)
|
||||||
|
<option value="{{ $roleEnum->value }}" {{ request('role') == $roleEnum->value ? 'selected' : '' }}>
|
||||||
|
{{ ucfirst(str_replace('_', ' ', $roleEnum->value)) }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-auto ms-auto">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<input type="text" name="search" class="form-control" placeholder="Search name or email..." value="{{ request('search') }}">
|
||||||
|
<button type="submit" class="btn btn-primary"><i class="ti tabler-search"></i></button>
|
||||||
|
@if(request()->anyFilled(['search', 'role']))
|
||||||
|
<a href="{{ route('users.index', ['tab' => $tab]) }}" class="btn btn-label-secondary" title="Reset Filters"><i class="ti tabler-refresh"></i></a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="table-responsive text-nowrap">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead class="border-top">
|
||||||
|
<tr>
|
||||||
|
<th>No</th>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Role</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse($users as $index => $user)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $users->firstItem() + $index }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="d-flex justify-content-start align-items-center user-name">
|
||||||
|
<div class="avatar-wrapper me-3">
|
||||||
|
<div class="avatar avatar-sm">
|
||||||
|
<span class="avatar-initial rounded-circle bg-label-dark">{{ strtoupper(substr($user->name, 0, 2)) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<span class="fw-medium text-heading">{{ $user->name }}</span>
|
||||||
|
<small class="text-muted">{{ $user->email }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@php
|
||||||
|
$roleKey = ($user->role instanceof \App\Enums\Role) ? $user->role->value : $user->role;
|
||||||
|
$roleColors = [
|
||||||
|
'super_admin' => 'bg-label-danger',
|
||||||
|
'admin' => 'bg-label-warning',
|
||||||
|
'fasilitator' => 'bg-label-info',
|
||||||
|
'petani' => 'bg-label-success',
|
||||||
|
];
|
||||||
|
$color = $roleColors[$roleKey] ?? 'bg-label-secondary';
|
||||||
|
@endphp
|
||||||
|
<span class="badge {{ $color }}">
|
||||||
|
{{ ucfirst(str_replace('_', ' ', $roleKey)) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@if($user->email_verified_at)
|
||||||
|
<span class="badge bg-label-success">Verified</span>
|
||||||
|
@else
|
||||||
|
<span class="badge bg-label-warning">Pending</span>
|
||||||
|
<form action="{{ route('users.verify', $user->id) }}" method="POST" class="d-inline">
|
||||||
|
@csrf
|
||||||
|
@method('PATCH')
|
||||||
|
<button type="button" class="btn btn-xs btn-outline-success ms-2 btn-verify">Verify</button>
|
||||||
|
</form>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill edit-record" title="Edit" data-id="{{ $user->id }}"><i class="ti tabler-edit ti-md"></i></a>
|
||||||
|
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill delete-record" data-id="{{ $user->id }}" data-name="{{ $user->name }}" title="Delete"><i class="ti tabler-trash ti-md"></i></a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr><td colspan="5" class="text-center py-4">No users found.</td></tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-footer d-flex justify-content-center justify-content-md-end border-top">
|
||||||
|
{{ $users->appends(request()->query())->links('pagination::bootstrap-5') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@include('konfigurasi.user.add')
|
||||||
|
@include('konfigurasi.user.edit')
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
const csrfToken = "{{ csrf_token() }}";
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$(document).on('click', '.btn-verify', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const form = $(this).closest('form');
|
||||||
|
const url = form.attr('action');
|
||||||
|
const formData = new FormData(form[0]);
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Verifikasi User?',
|
||||||
|
text: "User ini akan diaktifkan dan dapat login ke dalam sistem.",
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya, Verifikasi!',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-success me-3',
|
||||||
|
cancelButton: 'btn btn-label-secondary'
|
||||||
|
},
|
||||||
|
buttonsStyling: false
|
||||||
|
}).then(function (result) {
|
||||||
|
if (result.value) {
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
type: 'POST',
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
success: function (res) {
|
||||||
|
if (res.status === 'success') {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Berhasil!',
|
||||||
|
text: res.message,
|
||||||
|
showConfirmButton: false,
|
||||||
|
timer: 1500
|
||||||
|
}).then(() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({ icon: 'error', title: 'Gagal!', text: res.message });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
let errorMsg = xhr.responseJSON ? xhr.responseJSON.message : 'Terjadi kesalahan pada server.';
|
||||||
|
Swal.fire({ icon: 'error', title: 'Error!', text: errorMsg });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const offcanvasEditEl = document.getElementById('offcanvasEdit');
|
||||||
|
let bsOffcanvasEdit;
|
||||||
|
if (offcanvasEditEl) {
|
||||||
|
bsOffcanvasEdit = new bootstrap.Offcanvas(offcanvasEditEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('click', '.edit-record', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const id = $(this).data('id');
|
||||||
|
const offcanvasEditEl = document.getElementById('offcanvasEdit');
|
||||||
|
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
$(this).html('<span class="spinner-border spinner-border-sm" role="status"></span>');
|
||||||
|
const btn = $(this);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: `/users/${id}`,
|
||||||
|
type: 'GET',
|
||||||
|
success: function (res) {
|
||||||
|
btn.html('<i class="ti tabler-edit ti-md"></i>');
|
||||||
|
|
||||||
|
if (res.status === 'success') {
|
||||||
|
const user = res.data;
|
||||||
|
|
||||||
|
$('#editUserName').text(user.name);
|
||||||
|
$('#editEmail').text(user.email);
|
||||||
|
$('#formEdit').attr('action', `/users/${user.id}`);
|
||||||
|
$('#formEdit')[0].reset();
|
||||||
|
|
||||||
|
const bsOffcanvasEdit = bootstrap.Offcanvas.getOrCreateInstance(offcanvasEditEl);
|
||||||
|
bsOffcanvasEdit.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
btn.html('<i class="ti tabler-edit ti-md"></i>');
|
||||||
|
Swal.fire({ icon: 'error', title: 'Error', text: 'Gagal mengambil data user.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#formEdit').on('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const form = $(this);
|
||||||
|
const submitBtn = form.find('button[type="submit"]');
|
||||||
|
|
||||||
|
submitBtn.prop('disabled', true).text('Menyimpan...');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: form.attr('action'),
|
||||||
|
type: 'POST',
|
||||||
|
data: form.serialize(),
|
||||||
|
success: function (res) {
|
||||||
|
if (res.status === 'success') {
|
||||||
|
bootstrap.Offcanvas.getInstance(document.getElementById('offcanvasEdit')).hide();
|
||||||
|
Swal.fire({ icon: 'success', title: 'Berhasil!', text: res.message, showConfirmButton: false, timer: 1500 })
|
||||||
|
.then(() => window.location.reload());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
submitBtn.prop('disabled', false).text('Update Password');
|
||||||
|
let errorMsg = xhr.responseJSON?.message || 'Gagal mengupdate password.';
|
||||||
|
if (xhr.responseJSON?.errors) {
|
||||||
|
errorMsg = Object.values(xhr.responseJSON.errors).map(err => err.join(', ')).join('<br>');
|
||||||
|
}
|
||||||
|
Swal.fire({ icon: 'error', title: 'Gagal!', html: errorMsg });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.delete-record', function () {
|
||||||
|
const id = $(this).data('id');
|
||||||
|
const name = $(this).data('name');
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Hapus Pengguna?',
|
||||||
|
text: `Apakah kamu yakin ingin menghapus ${name}?`,
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya, hapus!',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
|
||||||
|
buttonsStyling: false
|
||||||
|
}).then(function (result) {
|
||||||
|
if (result.value) {
|
||||||
|
$.ajax({
|
||||||
|
url: `/users/${id}`, // Sesuaikan dengan route destroy kamu
|
||||||
|
type: 'DELETE',
|
||||||
|
headers: { 'X-CSRF-TOKEN': csrfToken },
|
||||||
|
success: function (res) {
|
||||||
|
Swal.fire({ icon: 'success', title: 'Berhasil dihapus!', text: res.message, showConfirmButton: false, timer: 1500 })
|
||||||
|
.then(() => window.location.reload());
|
||||||
|
},
|
||||||
|
error: () => Swal.fire({ icon: 'error', title: 'Error!', text: 'Failed to delete data.' })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
|
||||||
|
function togglePassword(element) {
|
||||||
|
const input = element.parentElement.querySelector('input');
|
||||||
|
const icon = element.querySelector('i');
|
||||||
|
if (input.type === 'password') {
|
||||||
|
input.type = 'text';
|
||||||
|
icon.className = 'ti ti-eye tabler-eye';
|
||||||
|
} else {
|
||||||
|
input.type = 'password';
|
||||||
|
icon.className = 'ti ti-eye-off tabler-eye-off';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formAddUser = document.getElementById('formAddUser');
|
||||||
|
const offcanvasElement = document.getElementById('offcanvasAddUser');
|
||||||
|
|
||||||
|
if (formAddUser && offcanvasElement) {
|
||||||
|
|
||||||
|
formAddUser.addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let formData = new FormData(this);
|
||||||
|
let submitBtn = this.querySelector('button[type="submit"]');
|
||||||
|
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
let originalText = submitBtn.innerHTML;
|
||||||
|
submitBtn.innerHTML = 'Menyimpan...';
|
||||||
|
|
||||||
|
fetch(this.action, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.innerHTML = originalText;
|
||||||
|
|
||||||
|
if (data.status === 'success') {
|
||||||
|
let bsOffcanvas = bootstrap.Offcanvas.getInstance(offcanvasElement) || new bootstrap.Offcanvas(offcanvasElement);
|
||||||
|
bsOffcanvas.hide();
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Berhasil!',
|
||||||
|
text: data.message,
|
||||||
|
showConfirmButton: false,
|
||||||
|
timer: 1500
|
||||||
|
}).then(() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let errorMsg = data.message || 'Terjadi kesalahan.';
|
||||||
|
if (data.errors) {
|
||||||
|
errorMsg = Object.values(data.errors).map(err => err.join(', ')).join('<br>');
|
||||||
|
}
|
||||||
|
Swal.fire({ icon: 'error', title: 'Gagal!', html: errorMsg });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.innerHTML = originalText;
|
||||||
|
Swal.fire({ icon: 'error', title: 'Error', text: 'Koneksi bermasalah atau terjadi kesalahan server.' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
+2
-4
@@ -6,7 +6,6 @@ use Illuminate\Support\Facades\Route;
|
|||||||
Route::get('/login', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'login'])->name('login');
|
Route::get('/login', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'login'])->name('login');
|
||||||
Route::get('/register', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'register'])->name('register');
|
Route::get('/register', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'register'])->name('register');
|
||||||
Route::post('/create-account', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'createAccount'])->name('auth.createAccount');
|
Route::post('/create-account', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'createAccount'])->name('auth.createAccount');
|
||||||
Route::patch('/users/{user}/verify', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'verify'])->name('users.verify');
|
|
||||||
Route::post('/authenticate', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'authenticate'])->name('auth.authenticate');
|
Route::post('/authenticate', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'authenticate'])->name('auth.authenticate');
|
||||||
Route::post('/logout', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'logout'])->name('logout');
|
Route::post('/logout', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'logout'])->name('logout');
|
||||||
|
|
||||||
@@ -21,9 +20,8 @@ Route::middleware('auth')
|
|||||||
Route::get('/akselerasi', [App\Http\Controllers\Web\Dashboard\AkselerasiController::class, 'index'])->name('dashboard.akselerasi');
|
Route::get('/akselerasi', [App\Http\Controllers\Web\Dashboard\AkselerasiController::class, 'index'])->name('dashboard.akselerasi');
|
||||||
|
|
||||||
// User
|
// User
|
||||||
Route::get('/users', [App\Http\Controllers\Web\Konfigurasi\UserController::class, 'index'])->name('users.index');
|
Route::resource('users', App\Http\Controllers\Web\Konfigurasi\UserController::class);
|
||||||
Route::get('/api/users-list', [App\Http\Controllers\Web\Konfigurasi\UserController::class, 'getUsers'])->name('api.users.list');
|
Route::patch('/users/{user}/verify', [App\Http\Controllers\Web\Konfigurasi\UserController::class, 'verify'])->name('users.verify');
|
||||||
// });
|
|
||||||
|
|
||||||
// Provinsi
|
// Provinsi
|
||||||
Route::get('/provinsi', [App\Http\Controllers\Web\Master\ProvinsiController::class, 'index'])->name('provinsi.index');
|
Route::get('/provinsi', [App\Http\Controllers\Web\Master\ProvinsiController::class, 'index'])->name('provinsi.index');
|
||||||
|
|||||||
Reference in New Issue
Block a user