1 Commits

11 changed files with 582 additions and 895 deletions

View File

@@ -1,401 +1,377 @@
<?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;
/** /**
* UsersController constructor. * Class UsersController
* *
* Initializes the user property with the authenticated user. * This controller is responsible for managing user within the application.
*/
public function __construct()
{
// Mengatur middleware auth
$this->middleware('auth');
// Mengatur user setelah middleware auth dijalankan
$this->middleware(function ($request, $next) {
$this->user = Auth::user();
return $next($request);
});
}
/**
* Display a listing of the resource.
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @package Modules\Usermanagement\Http\Controllers
* @throws \Illuminate\Auth\Access\AuthorizationException
*/ */
public function index() class UsersController extends Controller
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.read')) { /**
abort(403, 'Sorry! You are not allowed to view users.'); * @var \Illuminate\Contracts\Auth\Authenticatable|null
*/
protected $user;
/**
* UsersController constructor.
*
* Initializes the user property with the authenticated user.
*/
public function __construct()
{
// Mengatur middleware auth
$this->middleware('auth');
// Mengatur user setelah middleware auth dijalankan
$this->middleware(function ($request, $next) {
$this->user = Auth::user();
return $next($request);
});
} }
return view('usermanagement::users.index'); /**
} * 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 /**
* * Process support datatables ajax request.
* @return \Illuminate\Http\JsonResponse *
* @throws \Illuminate\Auth\Access\AuthorizationException * @param \Illuminate\Http\Request $request
*/ *
public function dataForDatatables(Request $request) * @return \Illuminate\Http\JsonResponse
{ * @throws \Illuminate\Auth\Access\AuthorizationException
if (is_null($this->user) || !$this->user->can('usermanagement.read')) { */
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([ return response()->json([
'message' => 'Sorry! You are not allowed to view users.', 'draw' => $request->get('draw'),
'success' => false 'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]); ]);
} }
$query = User::query()->with(['branches', 'roles']); /**
* 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.');
}
if (!$this->user->hasRole('administrator')) { $user = User::find($id);
$query->whereHas('roles', function ($q) { $roles = Role::all();
$q->where('name', '!=', 'administrator'); if(!$this->user->hasRole('administrator')){
}); $roles = $roles->where('name', '!=', 'administrator');
}
$branches = Branch::all();
return view('usermanagement::users.create', compact('user', 'roles', 'branches'));
} }
if ($request->has('search') && !empty($request->get('search'))) { /**
* 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) {
$q->where('name', 'like', '%' . $search . '%') return Excel::download(new UsersExport($search), 'users.xlsx');
->orWhere('email', 'like', '%' . $search . '%')
->orWhereHas('branches', function ($qb) use ($search) {
$qb->where('name', 'like', '%' . $search . '%');
});
});
} }
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) { public function profile()
$order = $request->get('sortOrder'); {
$column = $request->get('sortField'); $user = Auth::user();
$query->orderBy($column, $order); return view('usermanagement::users.profile', compact('user'));
} }
$totalRecords = $query->count(); public function updateProfile(Request $request)
{
$user = Auth::user();
$page = $request->get('page', 1); $validatedData = $request->validate([
$size = $request->get('size', 10); 'name' => 'required|string|max:255',
$offset = ($page - 1) * $size; 'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
'sign' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
]);
$query->skip($offset)->take($size); $user->name = $validatedData['name'];
$user->email = $validatedData['email'];
$user->nik = $validatedData['nik'];
$filteredRecords = $query->count(); if ($request->hasFile('sign')) {
// Delete old e-sign if exists
$users = $query->get()->map(function ($user) { if ($user->sign) {
$user->branch_names = $user->branches->pluck('name')->join(', '); Storage::disk('public')->delete('signatures/' . $user->id . '/' . $user->sign);
return $user;
});
$pageCount = ceil($totalRecords / $size);
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);
} }
$branches = $request->input('branches', []); $sign = $request->file('sign');
$signName = time() . '.' . $sign->getClientOriginalExtension();
$user->branches()->sync($branches); // Make sure the directory exists
Storage::disk('public')->makeDirectory('signatures/' . $user->id);
if (!empty($branches)) { // Store the file
$firstBranchId = $branches[0]; $sign->storeAs('signatures/' . $user->id, $signName, 'public');
$user->sign = $signName;
$user->branch_id = $firstBranchId;
$user->save();
}
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'); $user->save();
$signName = time() . '.' . $sign->getClientOriginalExtension();
// Make sure the directory exists return redirect()->route('users.profile')->with('success', 'Profile updated successfully.');
Storage::disk('public')->makeDirectory('signatures/' . $user->id);
// Store the file
$sign->storeAs('signatures/' . $user->id, $signName, 'public');
$user->sign = $signName;
} }
$user->save(); 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.',
]);
return redirect()->route('users.profile')->with('success', 'Profile updated successfully.'); if ($validator->fails()) {
} return back()->withErrors($validator)->withInput();
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.']);
} }
$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.');
} }
return redirect()->route('users.index')->with('success', 'User updated 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.');
}
}

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',
]; ];
} }
@@ -102,15 +102,4 @@ class User extends Authenticatable
{ {
return $this->hasMany(Appointment::class, 'admin_id'); return $this->hasMany(Appointment::class, 'admin_id');
} }
public function appointment_cabangs()
{
return $this->hasMany(Appointment::class, 'admin_id');
}
public function branches()
{
return $this->belongsToMany(Branch::class, 'user_branches', 'user_id', 'branch_id')
->withTimestamps();
}
} }

View File

@@ -1,35 +0,0 @@
<?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)
->withTimestamps();
}
public function branch()
{
return $this->belongsTo(Branch::class);
}
}

View File

@@ -4,7 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
return new class extends Migration { return new class extends Migration
{
/** /**
* Run the migrations. * Run the migrations.
*/ */
@@ -45,12 +46,8 @@ return new class extends Migration {
*/ */
public function down(): void public function down(): void
{ {
Schema::disableForeignKeyConstraints();
Schema::dropIfExists('sessions');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('users'); Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::enableForeignKeyConstraints(); Schema::dropIfExists('sessions');
} }
}; };

View File

@@ -1,34 +0,0 @@
<?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

@@ -28,10 +28,6 @@
public function data() public function data()
{ {
return [ return [
['name' => 'adk'],
['name' => 'basicdata'],
['name' => 'location'],
['name' => 'logs'],
['name' => 'usermanagement'] ['name' => 'usermanagement']
]; ];
} }

View File

@@ -1,63 +1,65 @@
<?php <?php
namespace Modules\Usermanagement\Database\Seeders; namespace Modules\Usermanagement\Database\Seeders;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
use Modules\Usermanagement\Models\PermissionGroup; use Modules\Usermanagement\Models\PermissionGroup;
use Spatie\Permission\Models\Permission; use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Role;
class PermissionsSeeder extends Seeder class PermissionsSeeder extends Seeder
{
public function run()
{ {
$data = $this->data(); /**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$data = $this->data();
foreach ($data as $value) { foreach ($data as $value) {
$permission = Permission::updateOrCreate([
'name' => $value['name'],
'guard_name' => 'web' // or 'api
], [
'permission_group_id' => $value['group']
]);
$permission = Permission::updateOrCreate([ $roles = Role::all();
'name' => $value['name'], foreach ($roles as $role) {
'guard_name' => 'web', $role->givePermissionTo($permission);
], [ }
'permission_group_id' => $value['group_id'],
'module' => $value['module'],
]);
foreach (Role::all() as $role) {
$role->givePermissionTo($permission);
}
}
}
public function data()
{
$data = [];
$groups = PermissionGroup::all();
foreach ($groups as $group) {
foreach ($this->crudActions($group->name) as $action) {
$data[] = [
'name' => $action,
'group_id' => $group->id,
'module' => $group->name,
];
} }
} }
return $data; public function data()
} {
$data = [];
// list of model permission
$groups = PermissionGroup::all();
public function crudActions($name) foreach ($groups as $group) {
{ foreach ($this->crudActions($group->name) as $action) {
$actions = ['create', 'read', 'update', 'delete', 'export', 'authorize', 'report', 'restore']; $data[] = ['name' => $action, 'group' => $group->id];
$result = []; }
}
foreach ($actions as $value) { return $data;
$result[] = $name . '.' . $value;
} }
return $result; public function crudActions($name)
{
$actions = [];
// list of permission actions
$crud = ['create', 'read', 'update', 'delete','export', 'authorize', 'report','restore'];
foreach ($crud as $value) {
$actions[] = $name . '.' . $value;
}
return $actions;
}
} }
}

View File

@@ -1,38 +0,0 @@
<?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,7 +6,6 @@ use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Modules\Usermanagement\Models\User; use Modules\Usermanagement\Models\User;
use Modules\Usermanagement\Database\Seeders\RolesSeeder; use Modules\Usermanagement\Database\Seeders\RolesSeeder;
use Spatie\Permission\Models\Role;
class UsersSeeder extends Seeder class UsersSeeder extends Seeder
{ {
@@ -18,179 +17,26 @@ class UsersSeeder extends Seeder
$roleSeeder = new RolesSeeder(); $roleSeeder = new RolesSeeder();
$rolesData = $roleSeeder->data(); $rolesData = $roleSeeder->data();
/**
* ==================================================
* STEP 0: Pastikan semua roles dari RolesSeeder sudah dibuat di tabel roles
* ==================================================
*/
foreach ($rolesData as $roleData) { foreach ($rolesData as $roleData) {
Role::firstOrCreate( if ($roleData['name'] === 'administrator') {
['name' => $roleData['name']], $user = User::firstOrCreate(
['guard_name' => 'web'] ['email' => $roleData['name'] . '@ag.co.id'],
); [
} 'name' => $roleData['name'],
'password' => Hash::make('bagbag'),
'branch_id' => 1,
'nik' => '000000',
'email_verified_at' => now(),
]
);
/** $role = \Spatie\Permission\Models\Role::firstOrCreate(
* ================================================== ['name' => $roleData['name']],
* Helper function untuk membuat user ['guard_name' => 'web']
* ================================================== );
*/
$createUser = function ($roleName, $branchId = null, $includeBranchInEmail = true, $includeBranchInName = true) {
$email = $roleName . ($includeBranchInEmail && $branchId ? $branchId : '') . '@ag.co.id';
$name = ucfirst($roleName);
if ($includeBranchInName && $branchId) {
$name .= ' ' . $branchId;
}
$user = User::firstOrCreate(
['email' => $email],
[
'name' => $name,
'password' => Hash::make('bagbag'),
'branch_id' => $branchId,
'nik' => rand(100000, 999999),
'email_verified_at' => now(),
]
);
$role = Role::where('name', $roleName)->first();
if ($role) {
$user->assignRole($role); $user->assignRole($role);
} }
};
/**
* ==================================================
* STEP 1: Buat user per role (branch_id = 1)
* ==================================================
* - Tanpa angka "1" di email
* - Tanpa angka "1" di nama
*/
foreach ($rolesData as $roleData) {
$roleName = $roleData['name'];
$createUser($roleName, 1, false, false); // tanpa 1 di email & nama
} }
/**
* ==================================================
* STEP 2: Jalankan logic lama (user per cabang)
* ==================================================
*/
$branchLuar = [
24,
25,
29,
35,
37,
41,
42,
45,
46,
50,
71,
74,
77,
82,
84,
85,
88,
90,
91,
93,
97,
107,
108,
111,
112,
113,
114,
115
];
$branchDalam = [
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
14,
15,
17,
18,
22,
23,
53,
55,
58,
60,
61,
66,
70,
95,
96,
98,
100,
105
];
$kpno = 6;
// LEGAL
foreach ($branchLuar as $branchId) {
$createUser('legal', $branchId);
}
$createUser('legal', $kpno);
// SPV LEGAL
$createUser('spvlegal', $kpno);
// USER CABANG
foreach (array_merge($branchLuar, $branchDalam) as $branchId) {
$createUser('cabang', $branchId);
}
// ADMIN DOKUMEN
foreach ($branchLuar as $branchId) {
$createUser('admindokumen', $branchId);
}
$createUser('admindokumen', $kpno);
// ADMIN KREDIT
$createUser('adminkredit', $kpno);
// AUDITOR
foreach ($branchLuar as $branchId) {
$createUser('auditor', $branchId);
}
$createUser('auditor', $kpno);
/**
* ==================================================
* NEW STEP: BRANCHDIRECTOR UNTUK SEMUA BRANCH
* ==================================================
*/
foreach (array_merge($branchLuar, $branchDalam) as $branchId) {
$createUser('branchdirector', $branchId);
}
$createUser('branchdirector', $kpno);
/**
* ==================================================
* NEW STEP: SO ADMIN DOKUMEN UNTUK SEMUA BRANCH
* ==================================================
*/
foreach (array_merge($branchLuar, $branchDalam) as $branchId) {
$createUser('soadmindokumen', $branchId);
}
// tetap buat juga untuk KPNO
$createUser('soadmindokumen', $kpno);
} }
} }

View File

@@ -6,197 +6,188 @@
@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 class="card-body grid gap-5">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Name
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('name') border-danger @enderror" type="text" name="name"
value="{{ $user->name ?? '' }}">
@error('name')
<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">
Email
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="w-full input @error('email') border-danger @enderror" type="email" name="email"
value="{{ $user->email ?? '' }}">
@error('email')
<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">
NIK
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="w-full input @error('nik') border-danger @enderror" type="number" name="nik"
value="{{ $user->nik ?? '' }}">
@error('nik')
<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">
Branch
</label>
<div class="flex flex-wrap items-baseline w-full">
<select class="input tomselect w-full @error('branches') border-danger @enderror" name="branches[]"
id="branches" multiple>
<option value="">-- Select Branch --</option>
@foreach ($branches as $branch)
<option value="{{ $branch->id }}"
{{ isset($user) && $user->branches->pluck('id')->contains($branch->id) ? 'selected' : '' }}>
{{ $branch->name }}
</option>
@endforeach
</select>
@error('branches')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
@if (isset($user->id))
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
E-Sign
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="file-input" type="file" name="sign" value="">
</div>
</div>
@endif
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Password
</label>
<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">
<input placeholder="Password" type="password" name="password" />
<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>
</div> <div class="card-body grid gap-5">
@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 class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
</div> <label class="form-label max-w-56">
</div> Name
<div class="switch switch-sm"> </label>
@if (isset($user)) <div class="flex flex-wrap items-baseline w-full">
<input <input class="input @error('name') border-danger @enderror" type="text" name="name" value="{{ $user->name ?? '' }}">
{{ in_array($role->name, $user->roles->pluck('name')->toArray()) ? 'checked' : '' }} @error('name')
name="roles" type="radio" value="{{ $role->name }}"> <em class="alert text-danger text-sm">{{ $message }}</em>
@else @enderror
<input name="roles" type="radio" value="{{ $role->name }}">
@endif
</div> </div>
</div> </div>
@endforeach <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
</div> <label class="form-label max-w-56">
</div> Email
</div> </label>
<div class="flex flex-wrap items-baseline w-full">
<input class="w-full input @error('email') border-danger @enderror" type="email" name="email" value="{{ $user->email ?? '' }}">
@error('email')
<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">
NIK
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="w-full input @error('nik') border-danger @enderror" type="number" name="nik" value="{{ $user->nik ?? '' }}">
@error('nik')
<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">
Branch
</label>
<div class="flex flex-wrap items-baseline w-full">
<select class="input tomselect w-full @error('branch_id') border-danger @enderror" name="branch_id" id="branch_id">
<option value="">Pilih Branch</option>
@if(isset($branches))
@foreach($branches as $row)
@if(isset($user))
<option value="{{ $row->id }}" {{ isset($user->branch_id) && $user->branch_id == $row->id?'selected' : '' }}>
{{ $row->name }}
</option>
@else
<option value="{{ $row->id }}">
{{ $row->name }}
</option>
@endif
@endforeach
@endif
</select>
@error('branch_id')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
@if(isset($user->id))
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
E-Sign
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="file-input" type="file" name="sign" value="">
</div>
</div>
@endif
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Password
</label>
<div class="flex justify-end"> <div class="flex flex-wrap items-baseline w-full">
<button type="submit" class="btn btn-primary"> <div class="input @error('password') border-danger @enderror" data-toggle-password="true" data-toggle-password-permanent="true">
Save <input placeholder="Password" type="password" name="password"/>
</button> <div class="btn btn-icon" data-toggle-password-trigger="true">
</div> <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> </div>
</form> </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>
</form>
</div> </div>
@endsection @endsection

View File

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