feat: Implement user-branch relationship and update user management views

- Added a many-to-many relationship between users and branches in User model.
- Updated user creation and editing views to support multiple branch selection.
- Modified user index view to display associated branches.
- Created UserBranch model to manage user-branch associations.
- Added migration for user_branches table with foreign key constraints.
- Implemented seeder to populate user_branches based on existing user branch data.

Run this command:
- php artisan migrate
- php artisan module:seed Usermanagement --class=UserBranchesSeeder
This commit is contained in:
Sholahuddin Al Ayubi
2025-12-11 17:20:34 +07:00
parent f3872e0665
commit 0b28760f41
7 changed files with 666 additions and 531 deletions

View File

@@ -1,377 +1,389 @@
<?php <?php
namespace Modules\Usermanagement\Http\Controllers; namespace Modules\Usermanagement\Http\Controllers;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Exception; use Exception;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Facades\Excel; use Maatwebsite\Excel\Facades\Excel;
use Modules\Basicdata\Models\Branch; use Modules\Basicdata\Models\Branch;
use Modules\Usermanagement\Exports\UsersExport; use Modules\Usermanagement\Exports\UsersExport;
use Modules\Usermanagement\Http\Requests\User as UserRequest; use Modules\Usermanagement\Http\Requests\User as UserRequest;
use Modules\Usermanagement\Models\Role; use Modules\Usermanagement\Models\Role;
use Modules\Usermanagement\Models\User; use Modules\Usermanagement\Models\User;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
/**
* Class UsersController
*
* This controller is responsible for managing user within the application.
*
* @package Modules\Usermanagement\Http\Controllers
*/
class UsersController extends Controller
{
/**
* @var \Illuminate\Contracts\Auth\Authenticatable|null
*/
protected $user;
/** /**
* Class UsersController * UsersController constructor.
* *
* This controller is responsible for managing user within the application. * Initializes the user property with the authenticated user.
*
* @package Modules\Usermanagement\Http\Controllers
*/ */
class UsersController extends Controller public function __construct()
{ {
/** // Mengatur middleware auth
* @var \Illuminate\Contracts\Auth\Authenticatable|null $this->middleware('auth');
*/
protected $user;
/** // Mengatur user setelah middleware auth dijalankan
* UsersController constructor. $this->middleware(function ($request, $next) {
* $this->user = Auth::user();
* Initializes the user property with the authenticated user. return $next($request);
*/ });
public function __construct() }
{
// Mengatur middleware auth
$this->middleware('auth');
// Mengatur user setelah middleware auth dijalankan /**
$this->middleware(function ($request, $next) { * Display a listing of the resource.
$this->user = Auth::user(); *
return $next($request); * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index()
{
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
abort(403, 'Sorry! You are not allowed to view users.');
}
return view('usermanagement::users.index');
}
/**
* Process support datatables ajax request.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function dataForDatatables(Request $request)
{
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
return response()->json([
'message' => 'Sorry! You are not allowed to view users.',
'success' => false
]);
}
$query = User::query()->with(['branches', 'roles']);
if (!$this->user->hasRole('administrator')) {
$query->whereHas('roles', function ($q) {
$q->where('name', '!=', 'administrator');
}); });
} }
/** if ($request->has('search') && !empty($request->get('search'))) {
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index()
{
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
abort(403, 'Sorry! You are not allowed to view users.');
}
return view('usermanagement::users.index');
}
/**
* Process support datatables ajax request.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function dataForDatatables(Request $request)
{
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
return response()->json(['message' => 'Sorry! You are not allowed to view users.','success' => false]);
}
// Retrieve data from the database
$query = User::query();
if(!$this->user->hasRole('administrator')){
$query->whereHas('roles', function($q){
$q->where('name', '!=', 'administrator');
});
}
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->whereAny(['name', 'email'], 'like', '%'.$search.'%');
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->with(['branch', 'roles'])->get();
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
// Calculate the current page number
$currentPage = 0 + 1;
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function edit($id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to edit users.');
}
$user = User::find($id);
$roles = Role::all();
if(!$this->user->hasRole('administrator')){
$roles = $roles->where('name', '!=', 'administrator');
}
$branches = Branch::all();
return view('usermanagement::users.create', compact('user', 'roles', 'branches'));
}
/**
* Remove the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function destroy($id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.delete')) {
return response()->json(['message' => 'Sorry! You are not allowed to delete users.','success' => false]);
}
$user = User::find($id);
$user->delete();
return response()->json(['message' => 'User deleted successfully.', 'success' => true]);
}
/**
* Restore the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function restore($id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.restore')) {
abort(403, 'Sorry! You are not allowed to restore users.');
}
$user = User::withTrashed()->find($id);
$user->restore();
return redirect()->route('users.index')->with('success', 'User restored successfully.');
}
/**
* Store a newly created resource in storage.
*
* This function handles the creation of a new user in the application. It validates the incoming request data,
* creates a new user record in the database, and redirects the user to the users index page with a success message.
*
* @param \Modules\Usermanagement\Http\Requests\User $request The incoming request containing the user data.
*
* @return \Illuminate\Http\RedirectResponse Redirects to the users index page with a success message upon successful creation.
* @return \Illuminate\Http\RedirectResponse Redirects to the users create page upon validation failure.
*/
public function store(UserRequest $request)
{
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create a user.');
}
$validated = $request->validated();
if ($validated) {
$user = User::create($validated);
if ($user) {
if ($request->roles) {
$user->assignRole($request->roles);
}
return redirect()->route('users.index')->with('success', 'User created successfully.');
}
}
return redirect()->route('users.create');
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create()
{
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create a user.');
}
$roles = Role::all();
if(!$this->user->hasRole('administrator')){
$roles = $roles->where('name', '!=', 'administrator');
}
$branches = Branch::all();
return view('usermanagement::users.create', compact('roles', 'branches'));
}
public function export(Request $request)
{
if (is_null($this->user) || !$this->user->can('usermanagement.export')) {
abort(403, 'Sorry! You are not allowed to export users.');
}
// Get search parameter from request
$search = $request->get('search'); $search = $request->get('search');
$query->where(function ($q) use ($search) {
return Excel::download(new UsersExport($search), 'users.xlsx'); $q->where('name', 'like', '%' . $search . '%')
->orWhere('email', 'like', '%' . $search . '%')
->orWhereHas('branches', function ($qb) use ($search) {
$qb->where('name', 'like', '%' . $search . '%');
});
});
} }
public function profile() if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
{ $order = $request->get('sortOrder');
$user = Auth::user(); $column = $request->get('sortField');
return view('usermanagement::users.profile', compact('user')); $query->orderBy($column, $order);
} }
public function updateProfile(Request $request) $totalRecords = $query->count();
{
$user = Auth::user();
$validatedData = $request->validate([ $page = $request->get('page', 1);
'name' => 'required|string|max:255', $size = $request->get('size', 10);
'email' => 'required|string|email|max:255|unique:users,email,' . $user->id, $offset = ($page - 1) * $size;
'sign' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
]);
$user->name = $validatedData['name']; $query->skip($offset)->take($size);
$user->email = $validatedData['email'];
$user->nik = $validatedData['nik'];
if ($request->hasFile('sign')) { $filteredRecords = $query->count();
// Delete old e-sign if exists
if ($user->sign) {
Storage::disk('public')->delete('signatures/' . $user->id . '/' . $user->sign);
}
$sign = $request->file('sign'); $users = $query->get()->map(function ($user) {
$signName = time() . '.' . $sign->getClientOriginalExtension(); $user->branch_names = $user->branches->pluck('name')->join(', ');
return $user;
});
// Make sure the directory exists $pageCount = ceil($totalRecords / $size);
Storage::disk('public')->makeDirectory('signatures/' . $user->id);
// Store the file
$sign->storeAs('signatures/' . $user->id, $signName, 'public');
$user->sign = $signName;
}
$user->save();
return redirect()->route('users.profile')->with('success', 'Profile updated successfully.');
}
public function changePassword(Request $request)
{
$validator = Validator::make($request->all(), [
'current_password' => 'required',
'password' => 'required|string|min:8|confirmed',
], [
'password_confirmation' => 'The new password confirmation does not match.',
]);
if ($validator->fails()) {
return back()->withErrors($validator)->withInput();
}
$user = Auth::user();
if (!Hash::check($request->current_password, $user->password)) {
return back()->withErrors(['current_password' => 'The current password is incorrect.']);
}
$user->password = Hash::make($request->password);
$user->save();
return redirect()->route('users.profile')->with('success', 'Password changed successfully.');
}
/**
* Update the specified resource in storage.
*
* @param \Modules\Usermanagement\Http\Requests\User $request
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(UserRequest $request, $id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to update users.');
}
$validated = $request->validated();
if ($validated) {
try {
$user = User::find($id);
if ($request->hasFile('sign')) {
$sign = $request->file('sign');
$signName = time() . '.' . $sign->getClientOriginalExtension();
$sign->storeAs(
'public/signatures/' . $user->id . '/',
$signName,
);
$validated['sign'] = $signName;
}
$user->update($validated);
if ($request->roles) {
$user->roles()->detach();
$user->assignRole($request->roles);
}
} catch (Exception $e) {
return redirect()->back()->withErrors(['error' => 'Failed to update user. Please try again.']);
}
}
return redirect()->route('users.index')->with('success', 'User updated successfully.');
}
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $page,
'totalCount' => $totalRecords,
'data' => $users,
]);
} }
/**
* Show the form for editing the specified resource.
*
* @param int $id
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function edit($id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to edit users.');
}
$user = User::find($id);
$roles = Role::all();
if (!$this->user->hasRole('administrator')) {
$roles = $roles->where('name', '!=', 'administrator');
}
$branches = Branch::all();
return view('usermanagement::users.create', compact('user', 'roles', 'branches'));
}
/**
* Remove the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function destroy($id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.delete')) {
return response()->json(['message' => 'Sorry! You are not allowed to delete users.', 'success' => false]);
}
$user = User::find($id);
$user->delete();
return response()->json(['message' => 'User deleted successfully.', 'success' => true]);
}
/**
* Restore the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function restore($id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.restore')) {
abort(403, 'Sorry! You are not allowed to restore users.');
}
$user = User::withTrashed()->find($id);
$user->restore();
return redirect()->route('users.index')->with('success', 'User restored successfully.');
}
/**
* Store a newly created resource in storage.
*
* This function handles the creation of a new user in the application. It validates the incoming request data,
* creates a new user record in the database, and redirects the user to the users index page with a success message.
*
* @param \Modules\Usermanagement\Http\Requests\User $request The incoming request containing the user data.
*
* @return \Illuminate\Http\RedirectResponse Redirects to the users index page with a success message upon successful creation.
* @return \Illuminate\Http\RedirectResponse Redirects to the users create page upon validation failure.
*/
public function store(UserRequest $request)
{
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create a user.');
}
$validated = $request->validated();
if ($validated) {
$user = User::create($validated);
if ($user) {
if ($request->roles) {
$user->assignRole($request->roles);
}
return redirect()->route('users.index')->with('success', 'User created successfully.');
}
}
return redirect()->route('users.create');
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create()
{
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create a user.');
}
$roles = Role::all();
if (!$this->user->hasRole('administrator')) {
$roles = $roles->where('name', '!=', 'administrator');
}
$branches = Branch::all();
return view('usermanagement::users.create', compact('roles', 'branches'));
}
public function export(Request $request)
{
if (is_null($this->user) || !$this->user->can('usermanagement.export')) {
abort(403, 'Sorry! You are not allowed to export users.');
}
// Get search parameter from request
$search = $request->get('search');
return Excel::download(new UsersExport($search), 'users.xlsx');
}
public function profile()
{
$user = Auth::user();
return view('usermanagement::users.profile', compact('user'));
}
public function updateProfile(Request $request)
{
$user = Auth::user();
$validatedData = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
'sign' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
]);
$user->name = $validatedData['name'];
$user->email = $validatedData['email'];
$user->nik = $validatedData['nik'];
if ($request->hasFile('sign')) {
// Delete old e-sign if exists
if ($user->sign) {
Storage::disk('public')->delete('signatures/' . $user->id . '/' . $user->sign);
}
$sign = $request->file('sign');
$signName = time() . '.' . $sign->getClientOriginalExtension();
// Make sure the directory exists
Storage::disk('public')->makeDirectory('signatures/' . $user->id);
// Store the file
$sign->storeAs('signatures/' . $user->id, $signName, 'public');
$user->sign = $signName;
}
$user->save();
return redirect()->route('users.profile')->with('success', 'Profile updated successfully.');
}
public function changePassword(Request $request)
{
$validator = Validator::make($request->all(), [
'current_password' => 'required',
'password' => 'required|string|min:8|confirmed',
], [
'password_confirmation' => 'The new password confirmation does not match.',
]);
if ($validator->fails()) {
return back()->withErrors($validator)->withInput();
}
$user = Auth::user();
if (!Hash::check($request->current_password, $user->password)) {
return back()->withErrors(['current_password' => 'The current password is incorrect.']);
}
$user->password = Hash::make($request->password);
$user->save();
return redirect()->route('users.profile')->with('success', 'Password changed successfully.');
}
/**
* Update the specified resource in storage.
*
* @param \Modules\Usermanagement\Http\Requests\User $request
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(UserRequest $request, $id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to update users.');
}
$validated = $request->validated();
if ($validated) {
try {
$user = User::findOrFail($id);
// Handle file upload e-sign
if ($request->hasFile('sign')) {
$sign = $request->file('sign');
$signName = time() . '.' . $sign->getClientOriginalExtension();
// Simpan file ke storage
$sign->storeAs(
'public/signatures/' . $user->id . '/',
$signName
);
$validated['sign'] = $signName;
}
// Update data user
$user->update($validated);
// Update roles
if ($request->roles) {
$user->roles()->detach();
$user->assignRole($request->roles);
}
$user->branches()->sync($request->input('branches', []));
$branchIds = $user->branches()->pluck('branches.id')->toArray();
} catch (Exception $e) {
Log::error('Failed to update user: ' . $e->getMessage());
return redirect()->back()->withErrors(['error' => 'Failed to update user. Please try again.']);
}
}
return redirect()->route('users.index')->with('success', 'User updated successfully.');
}
}

View File

@@ -73,8 +73,8 @@ class User extends Authenticatable
{ {
return [ return [
'email_verified_at' => 'datetime', 'email_verified_at' => 'datetime',
'password' => 'hashed', 'password' => 'hashed',
'id' => 'string', 'id' => 'string',
]; ];
} }
@@ -107,4 +107,9 @@ class User extends Authenticatable
{ {
return $this->hasMany(Appointment::class, 'admin_id'); return $this->hasMany(Appointment::class, 'admin_id');
} }
public function branches()
{
return $this->belongsToMany(Branch::class, 'user_branches', 'user_id', 'branch_id');
}
} }

34
app/Models/UserBranch.php Normal file
View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\Usermanagement\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Modules\Usermanagement\Models\User;
use Modules\Basicdata\Models\Branch;
// use Modules\Usermanagement\Database\Factories\UserBranchFactory;
class UserBranch extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*/
protected $table = 'user_branches';
protected $fillable = [
'user_id',
'branch_id',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function branch()
{
return $this->belongsTo(Branch::class);
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('user_branches', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('branch_id');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('branch_id')->references('id')->on('branches')->onDelete('cascade');
$table->unique(['user_id', 'branch_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('user_branches');
}
};

View File

@@ -0,0 +1,38 @@
<?php
namespace Modules\Usermanagement\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Modules\Usermanagement\Models\User;
class UserBranchesSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$users = User::all();
foreach ($users as $user) {
if ($user->branch_id) {
$exists = DB::table('user_branches')
->where('user_id', $user->id)
->where('branch_id', $user->branch_id)
->exists();
if (!$exists) {
DB::table('user_branches')->insert([
'user_id' => $user->id,
'branch_id' => $user->branch_id,
'created_at' => now(),
'updated_at' => now(),
]);
}
}
}
$this->command->info('User branches seeded successfully.');
}
}

View File

@@ -6,188 +6,197 @@
@section('content') @section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto"> <div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
@if(isset($user->id)) @if (isset($user->id))
<form action="{{ route('users.update', $user->id) }}" method="POST" enctype="multipart/form-data"> <form action="{{ route('users.update', $user->id) }}" method="POST" enctype="multipart/form-data">
<input type="hidden" name="id" value="{{ $user->id }}"> <input type="hidden" name="id" value="{{ $user->id }}">
@method('PUT') @method('PUT')
@else @else
<form method="POST" action="{{ route('users.store') }}"> <form method="POST" action="{{ route('users.store') }}">
@endif @endif
@csrf @csrf
<div class="card pb-2.5"> <div class="card pb-2.5">
<div class="card-header" id="basic_settings"> <div class="card-header" id="basic_settings">
<h3 class="card-title"> <h3 class="card-title">
{{ isset($user->id) ? 'Edit' : 'Add' }} User {{ isset($user->id) ? 'Edit' : 'Add' }} User
</h3> </h3>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<label class="switch switch-sm"> <label class="switch switch-sm">
<span class="switch-label"> <span class="switch-label">
Public Profile Public Profile
</span> </span>
<input checked="" name="check" type="checkbox" value="1"> <input checked="" name="check" type="checkbox" value="1">
</label> </label>
</div> </div>
</div> </div>
<div class="card-body grid gap-5"> <div class="card-body grid gap-5">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56"> <label class="form-label max-w-56">
Name Name
</label> </label>
<div class="flex flex-wrap items-baseline w-full"> <div class="flex flex-wrap items-baseline w-full">
<input class="input @error('name') border-danger @enderror" type="text" name="name" value="{{ $user->name ?? '' }}"> <input class="input @error('name') border-danger @enderror" type="text" name="name"
@error('name') value="{{ $user->name ?? '' }}">
<em class="alert text-danger text-sm">{{ $message }}</em> @error('name')
@enderror <em class="alert text-danger text-sm">{{ $message }}</em>
</div> @enderror
</div> </div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> </div>
<label class="form-label max-w-56"> <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
Email <label class="form-label max-w-56">
</label> Email
<div class="flex flex-wrap items-baseline w-full"> </label>
<input class="w-full input @error('email') border-danger @enderror" type="email" name="email" value="{{ $user->email ?? '' }}"> <div class="flex flex-wrap items-baseline w-full">
@error('email') <input class="w-full input @error('email') border-danger @enderror" type="email" name="email"
<em class="alert text-danger text-sm">{{ $message }}</em> value="{{ $user->email ?? '' }}">
@enderror @error('email')
</div> <em class="alert text-danger text-sm">{{ $message }}</em>
</div> @enderror
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> </div>
<label class="form-label max-w-56"> </div>
NIK <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
</label> <label class="form-label max-w-56">
<div class="flex flex-wrap items-baseline w-full"> NIK
<input class="w-full input @error('nik') border-danger @enderror" type="number" name="nik" value="{{ $user->nik ?? '' }}"> </label>
@error('nik') <div class="flex flex-wrap items-baseline w-full">
<em class="alert text-danger text-sm">{{ $message }}</em> <input class="w-full input @error('nik') border-danger @enderror" type="number" name="nik"
@enderror value="{{ $user->nik ?? '' }}">
</div> @error('nik')
</div> <em class="alert text-danger text-sm">{{ $message }}</em>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> @enderror
<label class="form-label max-w-56"> </div>
Branch </div>
</label> <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<div class="flex flex-wrap items-baseline w-full"> <label class="form-label max-w-56">
<select class="input tomselect w-full @error('branch_id') border-danger @enderror" name="branch_id" id="branch_id"> Branch
<option value="">Pilih Branch</option> </label>
@if(isset($branches)) <div class="flex flex-wrap items-baseline w-full">
@foreach($branches as $row) <select class="input tomselect w-full @error('branches') border-danger @enderror" name="branches[]"
@if(isset($user)) id="branches" multiple>
<option value="{{ $row->id }}" {{ isset($user->branch_id) && $user->branch_id == $row->id?'selected' : '' }}> <option value="">-- Select Branch --</option>
{{ $row->name }} @foreach ($branches as $branch)
</option> <option value="{{ $branch->id }}"
@else {{ isset($user) && $user->branches->pluck('id')->contains($branch->id) ? 'selected' : '' }}>
<option value="{{ $row->id }}"> {{ $branch->name }}
{{ $row->name }} </option>
</option> @endforeach
@endif </select>
@endforeach @error('branches')
@endif <em class="alert text-danger text-sm">{{ $message }}</em>
</select> @enderror
@error('branch_id') </div>
<em class="alert text-danger text-sm">{{ $message }}</em> </div>
@enderror @if (isset($user->id))
</div> <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
</div> <label class="form-label max-w-56">
@if(isset($user->id)) E-Sign
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> </label>
<label class="form-label max-w-56"> <div class="flex flex-wrap items-baseline w-full">
E-Sign <input class="file-input" type="file" name="sign" value="">
</label> </div>
<div class="flex flex-wrap items-baseline w-full"> </div>
<input class="file-input" type="file" name="sign" value=""> @endif
</div> <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
</div> <label class="form-label max-w-56">
@endif Password
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> </label>
<label class="form-label max-w-56">
Password
</label>
<div class="flex flex-wrap items-baseline w-full"> <div class="flex flex-wrap items-baseline w-full">
<div class="input @error('password') border-danger @enderror" data-toggle-password="true" data-toggle-password-permanent="true"> <div class="input @error('password') border-danger @enderror" data-toggle-password="true"
<input placeholder="Password" type="password" name="password"/> data-toggle-password-permanent="true">
<div class="btn btn-icon" data-toggle-password-trigger="true"> <input placeholder="Password" type="password" name="password" />
<i class="ki-outline ki-eye toggle-password-active:hidden"></i> <div class="btn btn-icon" data-toggle-password-trigger="true">
<i class="ki-outline ki-eye-slash hidden toggle-password-active:block"></i> <i class="ki-outline ki-eye toggle-password-active:hidden"></i>
</div> <i class="ki-outline ki-eye-slash hidden toggle-password-active:block"></i>
</div>
@error('password')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Password Confirmation
</label>
<div class="flex flex-wrap items-baseline w-full">
<div class="input @error('password_confirmation') border-danger @enderror" data-toggle-password="true" data-toggle-password-permanent="true">
<input placeholder="Password Confirmation" type="password" name="password_confirmation"/>
<div class="btn btn-icon" data-toggle-password-trigger="true">
<i class="ki-outline ki-eye toggle-password-active:hidden"></i>
<i class="ki-outline ki-eye-slash hidden toggle-password-active:block"></i>
</div>
</div>
@error('password_confirmation')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Role
</label>
<div class="flex flex-wrap items-baseline w-full">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-5 py-5 lg:py-7.5 w-full">
@foreach($roles as $role)
<div class="rounded-xl border p-4 flex items-center justify-between gap-2.5">
<div class="flex items-center gap-3.5">
<div class="relative size-[45px] shrink-0">
<svg class="w-full h-full stroke-gray-300 fill-gray-100" fill="none" height="48" viewBox="0 0 44 48" width="44" xmlns="http://www.w3.org/2000/svg">
<path d="M16 2.4641C19.7128 0.320509 24.2872 0.320508 28 2.4641L37.6506 8.0359C41.3634 10.1795 43.6506 14.141 43.6506
18.4282V29.5718C43.6506 33.859 41.3634 37.8205 37.6506 39.9641L28 45.5359C24.2872 47.6795 19.7128 47.6795 16 45.5359L6.34937
39.9641C2.63655 37.8205 0.349365 33.859 0.349365 29.5718V18.4282C0.349365 14.141 2.63655 10.1795 6.34937 8.0359L16 2.4641Z" fill="">
</path>
<path d="M16.25 2.89711C19.8081 0.842838 24.1919 0.842837 27.75 2.89711L37.4006 8.46891C40.9587 10.5232 43.1506 14.3196 43.1506
18.4282V29.5718C43.1506 33.6804 40.9587 37.4768 37.4006 39.5311L27.75 45.1029C24.1919 47.1572 19.8081 47.1572 16.25 45.1029L6.59937
39.5311C3.04125 37.4768 0.849365 33.6803 0.849365 29.5718V18.4282C0.849365 14.3196 3.04125 10.5232 6.59937 8.46891L16.25 2.89711Z" stroke="">
</path>
</svg>
<div class="absolute leading-none left-2/4 top-2/4 -translate-y-2/4 -translate-x-2/4">
<i class="ki-filled ki-category text-lg text-gray-500">
</i>
</div>
</div>
<div class="flex flex-col gap-1">
<span class="flex items-center gap-1.5 leading-none font-medium text-sm text-gray-900">
{{ $role->name }}
</span>
<span class="text-2sm text-gray-700">
</span>
</div>
</div>
<div class="switch switch-sm">
@if(isset($user))
<input {{ in_array($role->name,$user->roles->pluck("name")->toArray()) ? 'checked' : '' }} name="roles" type="radio" value="{{ $role->name }}">
@else
<input name="roles" type="radio" value="{{ $role->name }}">
@endif
</div>
</div>
@endforeach
</div>
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</div> </div>
</div> </div>
</form> @error('password')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Password Confirmation
</label>
<div class="flex flex-wrap items-baseline w-full">
<div class="input @error('password_confirmation') border-danger @enderror"
data-toggle-password="true" data-toggle-password-permanent="true">
<input placeholder="Password Confirmation" type="password" name="password_confirmation" />
<div class="btn btn-icon" data-toggle-password-trigger="true">
<i class="ki-outline ki-eye toggle-password-active:hidden"></i>
<i class="ki-outline ki-eye-slash hidden toggle-password-active:block"></i>
</div>
</div>
@error('password_confirmation')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Role
</label>
<div class="flex flex-wrap items-baseline w-full">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-5 py-5 lg:py-7.5 w-full">
@foreach ($roles as $role)
<div class="rounded-xl border p-4 flex items-center justify-between gap-2.5">
<div class="flex items-center gap-3.5">
<div class="relative size-[45px] shrink-0">
<svg class="w-full h-full stroke-gray-300 fill-gray-100" fill="none"
height="48" viewBox="0 0 44 48" width="44"
xmlns="http://www.w3.org/2000/svg">
<path
d="M16 2.4641C19.7128 0.320509 24.2872 0.320508 28 2.4641L37.6506 8.0359C41.3634 10.1795 43.6506 14.141 43.6506
18.4282V29.5718C43.6506 33.859 41.3634 37.8205 37.6506 39.9641L28 45.5359C24.2872 47.6795 19.7128 47.6795 16 45.5359L6.34937
39.9641C2.63655 37.8205 0.349365 33.859 0.349365 29.5718V18.4282C0.349365 14.141 2.63655 10.1795 6.34937 8.0359L16 2.4641Z"
fill="">
</path>
<path
d="M16.25 2.89711C19.8081 0.842838 24.1919 0.842837 27.75 2.89711L37.4006 8.46891C40.9587 10.5232 43.1506 14.3196 43.1506
18.4282V29.5718C43.1506 33.6804 40.9587 37.4768 37.4006 39.5311L27.75 45.1029C24.1919 47.1572 19.8081 47.1572 16.25 45.1029L6.59937
39.5311C3.04125 37.4768 0.849365 33.6803 0.849365 29.5718V18.4282C0.849365 14.3196 3.04125 10.5232 6.59937 8.46891L16.25 2.89711Z"
stroke="">
</path>
</svg>
<div
class="absolute leading-none left-2/4 top-2/4 -translate-y-2/4 -translate-x-2/4">
<i class="ki-filled ki-category text-lg text-gray-500">
</i>
</div>
</div>
<div class="flex flex-col gap-1">
<span
class="flex items-center gap-1.5 leading-none font-medium text-sm text-gray-900">
{{ $role->name }}
</span>
<span class="text-2sm text-gray-700">
</span>
</div>
</div>
<div class="switch switch-sm">
@if (isset($user))
<input
{{ in_array($role->name, $user->roles->pluck('name')->toArray()) ? 'checked' : '' }}
name="roles" type="radio" value="{{ $role->name }}">
@else
<input name="roles" type="radio" value="{{ $role->name }}">
@endif
</div>
</div>
@endforeach
</div>
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</div>
</div>
</form>
</div> </div>
@endsection @endsection

View File

@@ -160,7 +160,10 @@
branch: { branch: {
title: 'Branch', title: 'Branch',
render: (item, data) => { render: (item, data) => {
return data.branch?.name || '-'; if (data.branches && data.branches.length > 0) {
return data.branches.map(b => b.name).join(', ');
}
return '-';
}, },
}, },
role: { role: {