1 Commits
new_adk ... lpj

Author SHA1 Message Date
Daeng Deni Mardaeni
9af784ad57 Add Initial Role For LPJ Online Module 2024-10-27 15:47:27 +07:00
44 changed files with 1067 additions and 4286 deletions

View File

@@ -21,8 +21,7 @@ class PermissionExport implements WithColumnFormatting, WithHeadings, FromCollec
}
public function map($row): array{
// Convert the array to a collection before using pluck
$role = collect($row->roles)->pluck('name')->toArray();
$role = $row->roles->pluck('name')->toArray();
return [
$row->id,
$row->name,

View File

@@ -1,66 +0,0 @@
<?php
namespace Modules\Usermanagement\Exports;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Modules\Usermanagement\Models\Position;
class PositionExport implements WithColumnFormatting, WithHeadings, FromCollection, WithMapping
{
protected $search;
public function __construct($search = null)
{
$this->search = $search;
}
public function collection()
{
$query = Position::query();
if (!empty($this->search)) {
$search = $this->search;
$query->where(function ($q) use ($search) {
$q->whereRaw('LOWER(code) LIKE ?', ['%' . strtolower($search) . '%'])
->orWhereRaw('LOWER(name) LIKE ?', ['%' . strtolower($search) . '%'])
->orWhereRaw('CAST(level AS TEXT) LIKE ?', ['%' . $search . '%']);
});
}
return $query->get();
}
public function map($row): array
{
return [
$row->id,
$row->code,
$row->name,
$row->level,
$row->created_at
];
}
public function headings(): array
{
return [
'ID',
'Code',
'Name',
'Tingkat Jabatan',
'Created At'
];
}
public function columnFormats(): array
{
return [
'A' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER,
'D' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER,
'E' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
];
}
}

View File

@@ -11,15 +11,13 @@ use Modules\Usermanagement\Models\Role;
class RolesExport implements WithColumnFormatting, WithHeadings, FromCollection, withMapping
{
public function collection(){
return Role::with('position')->get();
return Role::all();
}
public function map($row): array{
return [
$row->id,
$row->name,
$row->position ? $row->position->name : '-',
$row->position ? $row->position->level : '-',
$row->created_at
];
}
@@ -27,8 +25,6 @@ class RolesExport implements WithColumnFormatting, WithHeadings, FromCollection,
return [
'ID',
'Role',
'Position',
'Tingkat Jabatan',
'Created At'
];
}
@@ -36,8 +32,7 @@ class RolesExport implements WithColumnFormatting, WithHeadings, FromCollection,
public function columnFormats(): array{
return [
'A' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER,
'D' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER,
'E' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
'C' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
];
}
}

View File

@@ -9,21 +9,10 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Modules\Usermanagement\Models\User;
class UsersExport implements WithColumnFormatting, WithHeadings, FromCollection, WithMapping
class UsersExport implements WithColumnFormatting, WithHeadings, FromCollection, withMapping
{
protected $search;
public function __construct($search = null)
{
$this->search = $search;
}
public function collection(){
return User::query()
->when($this->search, function ($query) {
$query->whereAny(['name','email'],'like','%'.$this->search.'%');
})
->get();
return User::all();
}
public function map($row): array{
@@ -32,8 +21,7 @@ class UsersExport implements WithColumnFormatting, WithHeadings, FromCollection,
$row->name,
$row->email,
$row->nik,
$row->branch?->name,
$row->roles?->pluck('name')->implode(', '),
$row->branch->name,
$row->created_at
];
}
@@ -44,7 +32,6 @@ class UsersExport implements WithColumnFormatting, WithHeadings, FromCollection,
'Email',
'NIK',
'Branch',
'Roles',
'Created At'
];
}
@@ -52,7 +39,7 @@ class UsersExport implements WithColumnFormatting, WithHeadings, FromCollection,
public function columnFormats(): array{
return [
'A' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER,
'G' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
'F' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
];
}
}

View File

@@ -1,30 +0,0 @@
<?php
use Illuminate\Support\Facades\Auth;
if (!function_exists('check_permission')) {
function check_permission(string $permission, bool $abort = true): bool
{
$user = Auth::user();
if (!$user || !$user->can($permission)) {
if ($abort) {
abort(403, 'Unauthorized');
}
return false;
}
return true;
}
}
if (!function_exists('user_has_role')) {
function user_has_role(array $roles): bool
{
$user = Auth::user();
if (!$user) return false;
return $user->roles->pluck('name')->intersect($roles)->isNotEmpty();
}
}

View File

@@ -23,24 +23,7 @@
/**
* @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);
});
}
public $user;
/**
* Display a listing of the resource.
@@ -51,8 +34,8 @@
public function index()
{
// Check if the authenticated user has the required permission to view permissions
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
abort(403, 'Sorry! You are not allowed to view permissions.');
if (is_null($this->user) || !$this->user->can('permissions.view')) {
//abort(403, 'Sorry! You are not allowed to view permissions.');
}
// Return the view for displaying the permissions
@@ -70,8 +53,8 @@
public function store(PermissionRequest $request)
{
// Check if the authenticated user has the required permission to store permissions
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create permissions.');
if (is_null($this->user) || !$this->user->can('permissions.store')) {
//abort(403, 'Sorry! You are not allowed to store permissions.');
}
$validate = $request->validated();
@@ -87,8 +70,7 @@
$group_name . '.delete',
$group_name . '.export',
$group_name . '.authorize',
$group_name . '.report',
$group_name . '.restore'
$group_name . '.report'
];
foreach ($data as $permission) {
@@ -115,14 +97,24 @@
public function create()
{
// Check if the authenticated user has the required permission to create permissions
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create permissions.');
if (is_null($this->user) || !$this->user->can('permissions.create')) {
//abort(403, 'Sorry! You are not allowed to create permissions.');
}
// Return the view for creating a new role
return view('usermanagement::permissions.create');
}
public function show($id){
// Check if the authenticated user has the required permission to view permissions
if (is_null($this->user) ||!$this->user->can('permissions.view')) {
//abort(403, 'Sorry! You are not allowed to view permissions.');
}
// Return the view for editing the role
return view('usermanagement::permissions.create');
}
/**
* Show the form for editing the specified resource.
*
@@ -134,8 +126,8 @@
public function edit($id)
{
// Check if the authenticated user has the required permission to edit permissions
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to edit permissions.');
if (is_null($this->user) || !$this->user->can('permissions.edit')) {
//abort(403, 'Sorry! You are not allowed to edit permissions.');
}
$permission = PermissionGroup::find($id);
@@ -158,8 +150,8 @@
public function update(PermissionRequest $request, $id)
{
// Check if the authenticated user has the required permission to update permissions
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to update permissions.');
if (is_null($this->user) || !$this->user->can('permissions.update')) {
//abort(403, 'Sorry! You are not allowed to update permissions.');
}
$validated = $request->validated();
@@ -181,8 +173,7 @@
$group_name . '.delete',
$group_name . '.export',
$group_name . '.authorize',
$group_name . '.report',
$group_name . '.restore'
$group_name . '.report'
];
$i = 0;
@@ -211,8 +202,8 @@
public function destroy($id)
{
// Check if the authenticated user has the required permission to delete permissions
if (is_null($this->user) || !$this->user->can('usermanagement.delete')) {
return response()->json(['message' => 'Sorry! You are not allowed to delete permissions.','success' => false]);
if (is_null($this->user) || !$this->user->can('permissions.delete')) {
//abort(403, 'Sorry! You are not allowed to delete permissions.');
}
$permission = PermissionGroup::find($id);
@@ -223,7 +214,7 @@
}
// Redirect back to the permissions index with a success message
return response()->json(['message' => 'Permission deleted successfully.','success' => true]);
echo json_encode(['message' => 'Permission deleted successfully.', 'success' => true]);
}
/**
@@ -237,7 +228,7 @@
public function restore($id)
{
// Check if the authenticated user has the required permission to restore permissions
if (is_null($this->user) || !$this->user->can('usermanagement.restore')) {
if (is_null($this->user) || !$this->user->can('permissions.restore')) {
abort(403, 'Sorry! You are not allowed to restore permissions.');
}
@@ -266,8 +257,8 @@
*/
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 permissions.','success' => false]);
if (is_null($this->user) || !$this->user->can('permissions.view')) {
//abort(403, 'Sorry! You are not allowed to view users.');
}
// Retrieve data from the database
@@ -276,7 +267,9 @@
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where('name', 'like', '%' . $search . '%');
$query->where(function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%");
});
}
// Apply sorting if provided
@@ -301,11 +294,14 @@
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->get();
$data = $data->map(function ($permission) {
// Get the data for the current page
$permissions = $query->get();
$permissions = $permissions->map(function ($permission) {
$permission->roles = $permission->roles($permission);
return $permission;
});
@@ -323,17 +319,12 @@
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
'data' => $permissions,
]);
}
public function export()
{
// Check if the authenticated user has the required permission to export permissions
if (is_null($this->user) || !$this->user->can('usermanagement.export')) {
abort(403, 'Sorry! You are not allowed to export permissions.');
}
return Excel::download(new PermissionExport, 'permissions.xlsx');
}
}

View File

@@ -1,291 +0,0 @@
<?php
namespace Modules\Usermanagement\Http\Controllers;
use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Usermanagement\Exports\PositionExport;
use Modules\Usermanagement\Http\Requests\PositionRequest;
use Modules\Usermanagement\Models\Position;
/**
* Class PositionsController
*
* This controller is responsible for managing positions within the application.
*
* @package Modules\Usermanagement\Http\Controllers
*/
class PositionsController extends Controller
{
/**
* @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);
});
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index()
{
// Check if the authenticated user has the required permission to view positions
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
abort(403, 'Sorry! You are not allowed to view positions.');
}
// Fetch all positions from the database
$positions = Position::all();
// Return the view for displaying the positions
return view('usermanagement::positions.index', compact('positions'));
}
/**
* Store a newly created resource in storage.
*
* @param \Modules\Usermanagement\Http\Requests\PositionRequest $request
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(PositionRequest $request)
{
// Check if the authenticated user has the required permission to store positions
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create positions.');
}
// Get validated data
$validated = $request->validated();
try {
// If no errors, save the position to the database
$position = Position::create($validated);
// Redirect to the positions index page with a success message
return redirect()->route('users.positions.index')
->with('success', 'Position created successfully.');
} catch (Exception $e) {
// If an error occurs, redirect back with an error message
return redirect()->back()
->with('error', 'An error occurred while creating the position: ' . $e->getMessage())
->withInput();
}
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function create()
{
// Check if the authenticated user has the required permission to create positions
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create positions.');
}
// Return the view for creating a new position
return view('usermanagement::positions.create');
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function edit($id)
{
// Check if the authenticated user has the required permission to edit positions
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to edit positions.');
}
// Find the position by ID
$position = Position::findOrFail($id);
// Return the view for editing the position
return view('usermanagement::positions.create', compact('position'));
}
/**
* Update the specified resource in storage.
*
* @param \Modules\Usermanagement\Http\Requests\PositionRequest $request
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(PositionRequest $request, $id)
{
// Check if the authenticated user has the required permission to update positions
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to update positions.');
}
// Find the position by ID
$position = Position::findOrFail($id);
// Get validated data
$validated = $request->validated();
try {
// If no errors, update the position in the database
$position->update($validated);
// Redirect to the positions index page with a success message
return redirect()->route('users.positions.index')
->with('success', 'Position updated successfully.');
} catch (Exception $e) {
// If an error occurs, redirect back with an error message
return redirect()->back()
->with('error', 'An error occurred while updating the position: ' . $e->getMessage())
->withInput();
}
}
/**
* Remove the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($id)
{
// Check if the authenticated user has the required permission to delete positions
if (is_null($this->user) || !$this->user->can('usermanagement.delete')) {
return response()->json(['message' => 'Sorry! You are not allowed to delete positions.','success' => false]);
}
// Find the position by ID
$position = Position::findOrFail($id);
// Check if the position has associated roles
if ($position->roles()->count() > 0) {
return redirect()->route('users.positions.index')
->with('error', 'Cannot delete position because it has associated roles.');
}
try {
// If no errors, delete the position from the database
$position->delete();
// Redirect to the positions index page with a success message
return redirect()->route('users.positions.index')
->with('success', 'Position deleted successfully.');
} catch (Exception $e) {
// If an error occurs, redirect back with an error message
return redirect()->route('users.positions.index')
->with('error', 'An error occurred while deleting the position: ' . $e->getMessage());
}
}
/**
* Process support datatables ajax request.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function dataForDatatables(Request $request)
{
// Check if the authenticated user has the required permission to view positions
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
return response()->json(['message' => 'Sorry! You are not allowed to view positions.','success' => false]);
}
// Retrieve data from the database
$query = Position::query();
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->whereAny(['code', 'name', 'level'], '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->get();
// Calculate the page count
$size = $request->get('size', 10); // Default to 10 if not set
$pageCount = $size > 0 ? ceil($totalRecords / $size) : 0;
// Calculate the current page number
$currentPage = $request->get('page', 1); // Default to page 1 if not set
// 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,
]);
}
/**
* Export positions to Excel.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function export(Request $request)
{
// Check if the authenticated user has the required permission to export positions
if (is_null($this->user) || !$this->user->can('usermanagement.export')) {
abort(403, 'Sorry! You are not allowed to export positions.');
}
// Get search parameter from request
$search = $request->get('search');
return Excel::download(new PositionExport($search), 'positions.xlsx');
}
}

View File

@@ -10,9 +10,7 @@
use Modules\Usermanagement\Http\Requests\RoleRequest;
use Modules\Usermanagement\Models\Permission;
use Modules\Usermanagement\Models\PermissionGroup;
use Modules\Usermanagement\Models\Position;
use Modules\Usermanagement\Models\Role;
use Exception;
/**
* Class RolesController
@@ -26,24 +24,20 @@
/**
* @var \Illuminate\Contracts\Auth\Authenticatable|null
*/
protected $user;
public $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();
$this->user = Auth::guard('web')->user();
return $next($request);
});
}
}*/
/**
* Display a listing of the resource.
@@ -54,8 +48,8 @@
public function index()
{
// Check if the authenticated user has the required permission to view roles
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
abort(403, 'Sorry! You are not allowed to view roles.');
if (is_null($this->user) || !$this->user->can('roles.view')) {
//abort(403, 'Sorry! You are not allowed to view roles.');
}
// Fetch all roles from the database
@@ -76,8 +70,8 @@
public function store(RoleRequest $request)
{
// Check if the authenticated user has the required permission to store roles
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to store roles.');
if (is_null($this->user) || !$this->user->can('roles.store')) {
//abort(403, 'Sorry! You are not allowed to store roles.');
}
$validated = $request->validated();
@@ -93,20 +87,16 @@
$role = Role::find($role->id);
try{
$role->syncPermissions($permissions);
} catch (Exception $e) {
return redirect()
->route('users.roles.index')
->with('error', 'Failed to sync permissions: ' . $e->getMessage());
} catch (\Exception $e) {
echo json_encode(['message' => $e->getMessage(), 'success']);exit;
}
}
// Redirect back to the roles index with a success message
return redirect()->route('users.roles.index')->with('success', 'Role created successfully.');
} catch (Exception $e) {
} catch (\Exception $e) {
// Redirect back to the roles index with an error message
return redirect()
->route('users.roles.index')
->with('error', 'Failed to create role. Please try again.');
return redirect()->route('users.roles.index')->with('error', 'Failed to create role. Please try again.');
}
}
}
@@ -120,14 +110,37 @@
public function create()
{
// Check if the authenticated user has the required permission to create roles
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create roles.');
if (is_null($this->user) || !$this->user->can('roles.create')) {
//abort(403, 'Sorry! You are not allowed to create roles.');
}
$permissiongroups = PermissionGroup::all();
$positions = Position::all();
// Return the view for creating a new role
return view('usermanagement::roles.create', compact('permissiongroups', 'positions'));
return view('usermanagement::roles.create',compact('permissiongroups'));
}
/**
* Display the specified resource.
*
* @param int $id
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show($id)
{
// Check if the authenticated user has the required permission to view roles
if (is_null($this->user) || !$this->user->can('roles.view')) {
abort(403, 'Sorry! You are not allowed to view roles.');
}
// Fetch the specified role from the database
$role = Role::find($id);
// Return the view for displaying the role
return view('usermanagement::roles.show', compact('role'));
}
/**
@@ -141,17 +154,16 @@
public function edit($id)
{
// Check if the authenticated user has the required permission to edit roles
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to edit roles.');
if (is_null($this->user) || !$this->user->can('roles.edit')) {
//abort(403, 'Sorry! You are not allowed to edit roles.');
}
// Fetch the specified role from the database
$role = Role::find($id);
$permissions = Permission::all();
$permissiongroups = PermissionGroup::all();
$positions = Position::all();
// Return the view for editing the role
return view('usermanagement::roles.create', compact('role', 'permissions', 'permissiongroups', 'positions'));
return view('usermanagement::roles.create', compact('role','permissions','permissiongroups'));
}
@@ -168,8 +180,8 @@
public function update(RoleRequest $request, $id)
{
// Check if the authenticated user has the required permission to update roles
if (is_null($this->user) || !$this->user->can('usermanagement.update')) {
abort(403, 'Sorry! You are not allowed to update roles.');
if (is_null($this->user) || !$this->user->can('roles.update')) {
//abort(403, 'Sorry! You are not allowed to update roles.');
}
$validated = $request->validated();
@@ -185,21 +197,17 @@
$role = Role::find($role->id);
try{
$role->syncPermissions($permissions);
} catch (Exception $e) {
return redirect()
->route('users.roles.index')
->with('error', 'Failed to sync permissions: ' . $e->getMessage());
} catch (\Exception $e) {
echo json_encode(['message' => $e->getMessage(), 'success']);exit;
}
}
// Redirect back to the roles index with a success message
return redirect()->route('users.roles.index')->with('success', 'Role updated successfully.');
} catch (Exception $e) {
} catch (\Exception $e) {
// Redirect back to the roles index with an error message
return redirect()
->route('users.roles.index')
->with('error', 'Failed to update role. Please try again.');
return redirect()->route('users.roles.index')->with('error', 'Failed to update role. Please try again.');
}
}
}
@@ -212,23 +220,21 @@
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function destroy($id)
{
// Check if the authenticated user has the required permission to delete currencies
if (is_null($this->user) || !$this->user->can('usermanagement.delete')) {
return response()->json(['success' => false, 'message' => 'Sorry! You are not allowed to delete roles.'], 403);
// Check if the authenticated user has the required permission to delete roles
if (is_null($this->user) || !$this->user->can('roles.delete')) {
//abort(403, 'Sorry! You are not allowed to delete roles.');
}
try {
// Delete from database
$currency = Role::find($id);
$currency->delete();
// Fetch the specified role from the database
$role = Role::find($id);
return response()->json(['success' => true, 'message' => 'Role deleted successfully.']);
} catch (Exception $e) {
return response()->json(['success' => false, 'message' => 'Failed to delete role.']);
}
// Delete the role
$role->delete();
// Redirect back to the roles index with a success message
echo json_encode(['message' => 'Role deleted successfully.', 'success' => true]);
}
/**
@@ -242,7 +248,7 @@
public function restore($id)
{
// Check if the authenticated user has the required permission to restore roles
if (is_null($this->user) || !$this->user->can('usermanagement.restore')) {
if (is_null($this->user) || !$this->user->can('roles.restore')) {
abort(403, 'Sorry! You are not allowed to restore roles.');
}
@@ -266,25 +272,18 @@
*/
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 roles.','success' => false]);
if (is_null($this->user) || !$this->user->can('roles.view')) {
//abort(403, 'Sorry! You are not allowed to view users.');
}
// Retrieve data from the database
$query = Role::query();
if(!$this->user->hasRole('administrator')){
$query->where('name', '!=', 'administrator');
}
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%')
->orWhereHas('position', function ($query) use ($search) {
$query->whereAny(['name', 'level'], 'like','%'.$search.'%');
});
$q->where('name', 'LIKE', "%$search%");
});
}
@@ -292,29 +291,11 @@
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
if ($column === 'position_name') {
$query->leftJoin('positions', 'roles.position_id', '=', 'positions.id')
->orderBy('positions.name', $order)
->select('roles.*'); // Select only from roles table to avoid column conflicts
} else if ($column === 'level') {
$query->leftJoin('positions', 'roles.position_id', '=', 'positions.id')
->orderBy('positions.level', $order)
->select('roles.*'); // Select only from roles table to avoid column conflicts
} else {
if ($column === 'name') {
$query->orderBy('roles.name', $order);
} else {
$query->orderBy($column, $order);
}
}
}
// Create a copy of the query for counting
$countQuery = clone $query;
// Get the total count of records (without joins to avoid duplicates)
$totalRecords = Role::count();
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
@@ -325,18 +306,17 @@
$query->skip($offset)->take($size);
}
// Get the filtered count of records - use distinct to avoid duplicates from joins
$filteredRecords = $countQuery->distinct()->count('roles.id');
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->with('position')->get();
$roles = $query->get();
// Calculate the page count - ensure we don't divide by zero
$pageSize = $request->get('size', 10); // Default to 10 if not provided
$pageCount = $pageSize > 0 ? ceil($totalRecords / $pageSize) : 0;
// Calculate the page count
$pageCount = ceil($totalRecords/$request->get('size'));
// Calculate the current page number
$currentPage = $request->get('page') ?: 1;
$currentPage = 0 + 1;
// Return the response data as a JSON object
return response()->json([
@@ -346,16 +326,12 @@
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
'data' => $roles,
]);
}
public function export()
{
if (is_null($this->user) || !$this->user->can('usermanagement.export')) {
abort(403, 'Sorry! You are not allowed to export roles.');
}
return Excel::download(new RolesExport, 'roles.xlsx');
}
}

View File

@@ -6,16 +6,12 @@ use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Basicdata\Models\Branch;
use Modules\Lpj\Models\Branch;
use Modules\Usermanagement\Exports\UsersExport;
use Modules\Usermanagement\Http\Requests\User as UserRequest;
use Modules\Usermanagement\Models\Role;
use Modules\Usermanagement\Models\User;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
/**
* Class UsersController
@@ -29,24 +25,20 @@ class UsersController extends Controller
/**
* @var \Illuminate\Contracts\Auth\Authenticatable|null
*/
protected $user;
public $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);
});
}
// public function __construct()
// {
// $this->middleware(function ($request, $next) {
// $this->user = Auth::guard('web')->user();
// return $next($request);
// });
// }
/**
* Display a listing of the resource.
@@ -56,8 +48,8 @@ class UsersController extends Controller
*/
public function index()
{
if (is_null($this->user) || !$this->user->can('usermanagement.read')) {
abort(403, 'Sorry! You are not allowed to view users.');
if (is_null($this->user) || !$this->user->can('users.view')) {
//abort(403, 'Sorry! You are not allowed to view users.');
}
return view('usermanagement::users.index');
@@ -73,61 +65,61 @@ class UsersController extends Controller
*/
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
]);
if (is_null($this->user) || !$this->user->can('users.view')) {
//abort(403, 'Sorry! You are not allowed to view users.');
}
$query = User::query()->with(['branches', 'roles']);
if (!$this->user->hasRole('administrator')) {
$query->whereHas('roles', function ($q) {
$q->where('name', '!=', 'administrator');
});
}
// Retrieve data from the database
$query = User::query();
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%')
->orWhere('email', 'like', '%' . $search . '%')
->orWhereHas('branches', function ($qb) use ($search) {
$qb->where('name', 'like', '%' . $search . '%');
});
$q
->where('name', 'LIKE', "%$search%")
->orWhere('email', 'LIKE', "%$search%");
});
}
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
$totalRecords = $query->count();
$page = $request->get('page', 1);
$size = $request->get('size', 10);
$offset = ($page - 1) * $size;
// 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 total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('start') && $request->has('length')) {
$start = $request->get('start');
$length = $request->get('length');
$query->skip($start)->take($length);
}
// Get the filtered count of records
$filteredRecords = $query->count();
$users = $query->get()->map(function ($user) {
$user->branch_names = $user->branches->pluck('name')->join(', ');
return $user;
});
// Get the data for the current page
$users = $query->with('branch')->get();
$pageCount = ceil($totalRecords / $size);
// 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' => $page,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $users,
]);
@@ -143,19 +135,61 @@ class UsersController extends Controller
*/
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 (is_null($this->user) || !$this->user->can('users.edit')) {
//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'));
}
/**
* 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('users.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.');
}
/**
* Remove the specified resource from storage.
*
@@ -166,14 +200,14 @@ class UsersController extends Controller
*/
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]);
if (is_null($this->user) || !$this->user->can('users.delete')) {
//abort(403, 'Sorry! You are not allowed to delete users.');
}
$user = User::find($id);
$user->delete();
return response()->json(['message' => 'User deleted successfully.', 'success' => true]);
echo json_encode(['message' => 'User deleted successfully.', 'success' => true]);
}
/**
@@ -186,7 +220,7 @@ class UsersController extends Controller
*/
public function restore($id)
{
if (is_null($this->user) || !$this->user->can('usermanagement.restore')) {
if (is_null($this->user) || !$this->user->can('users.restore')) {
abort(403, 'Sorry! You are not allowed to restore users.');
}
@@ -209,31 +243,15 @@ class UsersController extends Controller
*/
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', []);
$user->branches()->sync($branches);
if (!empty($branches)) {
$firstBranchId = $branches[0];
$user->branch_id = $firstBranchId;
$user->save();
}
return redirect()->route('users.index')->with('success', 'User created successfully.');
}
}
@@ -249,28 +267,18 @@ class UsersController extends Controller
*/
public function create()
{
if (is_null($this->user) || !$this->user->can('usermanagement.create')) {
abort(403, 'Sorry! You are not allowed to create a user.');
if (is_null($this->user) || !$this->user->can('users.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)
public function export()
{
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');
return Excel::download(new UsersExport, 'users.xlsx');
}
public function profile()
@@ -279,123 +287,4 @@ class UsersController extends Controller
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

@@ -1,39 +0,0 @@
<?php
namespace Modules\Usermanagement\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PositionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'name' => 'required|string',
'level' => 'required|integer',
];
if ($this->method() === 'PUT') {
$rules['code'] = 'required|string|unique:positions,code,' . $this->id;
} else {
$rules['code'] = 'required|string|unique:positions,code';
}
return $rules;
}
}

View File

@@ -23,7 +23,6 @@
$rules = [
'guard_names' => 'required|string|in:web,api',
'position_id' => 'nullable|exists:positions,id',
];
if ($this->method() === 'PUT') {
@@ -42,3 +41,6 @@
]);
}
}

View File

@@ -6,7 +6,7 @@
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
use Mattiverse\Userstamps\Traits\Userstamps;
use Wildside\Userstamps\Userstamps;
/**

View File

@@ -2,14 +2,13 @@
namespace Modules\Usermanagement\Models;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Permission\Models\Permission as SpatiePermission;
class Permission extends SpatiePermission
{
use LogsActivity, SoftDeletes;
use LogsActivity;
/**
* Retrieve the activity log options for this permission.

View File

@@ -12,17 +12,6 @@
'slug'
];
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
if (!$model->slug) {
$model->slug = \Str::slug($model->name);
}
});
}
/**
* Retrieves all permissions associated with a given permission group ID.
*

View File

@@ -1,42 +0,0 @@
<?php
namespace Modules\Usermanagement\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
class Position extends Model
{
use SoftDeletes, LogsActivity;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'code',
'name',
'level',
];
/**
* Retrieve the activity log options for this position.
*
* @return LogOptions The activity log options.
*/
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()->logAll()->useLogName('User Management|Positions : ');
}
/**
* Get the roles associated with this position.
*/
public function roles()
{
return $this->hasMany(Role::class);
}
}

View File

@@ -11,17 +11,6 @@
{
use softDeletes, LogsActivity;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'guard_name',
'position_id',
];
/**
* Retrieve the activity log options for this role.
*
@@ -33,11 +22,4 @@
return LogOptions::defaults()->logAll()->useLogName('User Management|Roles : ');
}
/**
* Get the position that owns the role.
*/
public function position()
{
return $this->belongsTo(Position::class);
}
}

View File

@@ -2,14 +2,12 @@
namespace Modules\Usermanagement\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Modules\Basicdata\Models\Branch;
use Modules\Adk\Models\Appointment;
use Modules\Lpj\Models\Branch;
use Spatie\Permission\Traits\HasRoles;
use Mattiverse\Userstamps\Traits\Userstamps;
use Wildside\Userstamps\Userstamps;
/**
* Class User
@@ -26,7 +24,7 @@ use Mattiverse\Userstamps\Traits\Userstamps;
*/
class User extends Authenticatable
{
use HasFactory, Notifiable, Userstamps, HasRoles, softDeletes;
use Notifiable, Userstamps, HasRoles, softDeletes;
protected $guard_name = ['web'];
@@ -69,7 +67,8 @@ class User extends Authenticatable
*
* @return array<string, string>
*/
protected function casts(): array
protected function casts()
: array
{
return [
'email_verified_at' => 'datetime',
@@ -78,39 +77,8 @@ class User extends Authenticatable
];
}
public function branch()
{
public function branch(){
return $this->belongsTo(Branch::class);
}
/**
* Create a new factory instance for the model.
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
protected static function newFactory()
{
return \Modules\Usermanagement\Database\Factories\UserFactory::new();
}
/**
* Get all of the appointments for the User
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function appointments()
{
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

@@ -1,34 +0,0 @@
<?php
namespace Modules\Usermanagement\Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
use Modules\Usermanagement\Models\User;
class UserFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = User::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => bcrypt('password'), // Default password for testing
'remember_token' => Str::random(10),
'nik' => $this->faker->unique()->numerify('##########'),
];
}
}

View File

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

View File

@@ -3,14 +3,14 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Modules\Basicdata\Models\Branch;
use Modules\Lpj\Models\Branch;
return new class extends Migration {
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
: void
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('nik')->nullable()->after('email');
@@ -21,8 +21,7 @@
/**
* Reverse the migrations.
*/
public function down()
: void
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('nik');

View File

@@ -1,31 +0,0 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('positions', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('code')->unique();
$table->string('name');
$table->integer('level');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('positions');
}
};

View File

@@ -1,45 +0,0 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
$tableNames = config('permission.table_names');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
Schema::table($tableNames['roles'], function (Blueprint $table) {
$table->unsignedBigInteger('position_id')->nullable()->after('guard_name');
$table->foreign('position_id')
->references('id')
->on('positions')
->onDelete('set null');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
$tableNames = config('permission.table_names');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
}
Schema::table($tableNames['roles'], function (Blueprint $table) {
$table->dropForeign(['position_id']);
$table->dropColumn('position_id');
});
}
};

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,11 +28,13 @@
public function data()
{
return [
['name' => 'adk'],
['name' => 'basicdata'],
['name' => 'location'],
['name' => 'logs'],
['name' => 'usermanagement']
['name' => 'usermanagement'],
['name' => 'basic-data'],
['name' => 'permohonan'],
['name' => 'admin'],
['name' => 'senior-officer'],
['name' => 'penilai'],
['name' => 'surveyor']
];
}
}

View File

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

View File

@@ -27,7 +27,13 @@
public function data()
{
return [
['name' => 'administrator']
['name' => 'administrator'],
['name' => 'admin'],
['name' => 'senior-officer'],
['name' => 'penilai'],
['name' => 'surveyor'],
['name' => 'pemohon-ao'],
['name' => 'pemohon-eo']
];
}
}

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

@@ -2,195 +2,32 @@
namespace Modules\Usermanagement\Database\Seeders;
use Faker\Generator;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use Modules\Usermanagement\Models\User;
use Modules\Usermanagement\Database\Seeders\RolesSeeder;
use Spatie\Permission\Models\Role;
class UsersSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run(): void
public function run(Generator $faker)
{
$roleSeeder = new RolesSeeder();
$rolesData = $roleSeeder->data();
$roles = Role::all();
/**
* ==================================================
* STEP 0: Pastikan semua roles dari RolesSeeder sudah dibuat di tabel roles
* ==================================================
*/
foreach ($rolesData as $roleData) {
Role::firstOrCreate(
['name' => $roleData['name']],
['guard_name' => 'web']
);
}
/**
* ==================================================
* Helper function untuk membuat user
* ==================================================
*/
$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),
foreach ($roles as $role) {
$user = User::create([
'name' => $role->name,
'email' => $role->name . '@lpj.id',
'password' => Hash::make('lpj'),
'email_verified_at' => now(),
]
);
]);
$role = Role::where('name', $roleName)->first();
if ($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

@@ -8,9 +8,7 @@
"providers": [
"Modules\\Usermanagement\\Providers\\UsermanagementServiceProvider"
],
"files": [
"app/Helpers/RolePermission.php"
],
"files": [],
"menu": {
"main": [],
"master": [],
@@ -18,7 +16,7 @@
{
"title": "User Management",
"path": "users",
"icon": "ki-filled ki-users text-lg text-primary",
"icon": "ki-filled ki-users text-lg",
"classes": "",
"attributes": [],
"permission": "",
@@ -36,16 +34,6 @@
"administrator"
]
},
{
"title": "Positions",
"path": "users.positions",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator"
]
},
{
"title": "Roles",
"path": "users.roles",

View File

@@ -7,10 +7,8 @@
@section('content')
<div class="container-fluid">
<div class="grid">
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="5"
data-datatable-state-save="true" id="permissions-table"
data-api-url="{{ route('users.permissions.datatables') }}">
<div class="flex-wrap py-5 card-header">
<div class="card card-grid min-w-full" data-datatable="false" data-datatable-page-size="5" data-datatable-state-save="true" id="permissions-table" data-api-url="{{ route('users.permissions.datatables') }}">
<div class="card-header py-5 flex-wrap">
<h3 class="card-title">
List of Permissions
</h3>
@@ -21,19 +19,38 @@
</label>
</div>
<div class="flex flex-wrap gap-2.5 lg:gap-5">
<div class="flex flex-wrap gap-2.5">
<select class="select select-sm w-28">
<option value="1">
Active
</option>
<option value="2">
Disabled
</option>
<option value="2">
Pending
</option>
</select>
<select class="select select-sm w-28">
<option value="desc">
Latest
</option>
<option value="asc">
Oldest
</option>
</select>
<button class="btn btn-sm btn-outline btn-primary">
<i class="ki-filled ki-setting-4"> </i> <Filters></Filters>
</button>
<div class="h-[24px] border border-r-gray-200"> </div>
<a class="btn btn-sm btn-light" href="{{ route('users.permissions.export') }}"> Export to Excel
</a>
<a class="btn btn-sm btn-primary" href="{{ route('users.permissions.create') }}"> Add Permission
</a>
<a class="btn btn-sm btn-light" href="{{ route('users.permissions.export') }}"> Export to Excel </a>
<a class="btn btn-sm btn-primary" href="{{ route('users.permissions.create') }}"> Add Permission </a>
</div>
</div>
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
data-datatable-table="true">
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
@@ -52,14 +69,12 @@
</thead>
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
<div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
page
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per page
</div>
<div class="flex gap-4 items-center">
<div class="flex items-center gap-4">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
@@ -161,7 +176,7 @@
searchInput.addEventListener('input', function () {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
dataTable.goPage(1);
});
</script>
@endpush

View File

@@ -1,68 +0,0 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render(request()->route()->getName()) }}
@endsection
@section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<form action="{{ isset($position->id) ? route('users.positions.update', $position->id) : route('users.positions.store') }}" method="POST" id="position_form">
@csrf
@if(isset($position->id))
<input type="hidden" name="id" value="{{ $position->id }}">
@method('PUT')
@endif
<div class="card pb-2.5">
<div class="card-header" id="basic_settings">
<h3 class="card-title">
{{ isset($position->id) ? 'Edit' : 'Add' }} Position
</h3>
<div class="flex items-center gap-2">
<a href="{{ route('users.positions.index') }}" class="btn btn-xs btn-info">Back</a>
</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">
Code
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('code') border-danger @enderror" type="text" name="code" value="{{ $position->code ?? '' }}">
@error('code')
<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">
Name
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('name') border-danger @enderror" type="text" name="name" value="{{ $position->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">
Level
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('level') border-danger @enderror" type="number" name="level" value="{{ $position->level ?? '' }}">
@error('level')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</div>
</div>
</form>
</div>
@endsection

View File

@@ -1,172 +0,0 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('users.positions') }}
@endsection
@section('content')
<div class="container-fluid">
<div class="grid">
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="5"
data-datatable-state-save="true" id="positions-table" data-api-url="{{ route('users.positions.datatables') }}">
<div class="flex-wrap py-5 card-header">
<h3 class="card-title">
List of Positions
</h3>
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="flex">
<label class="input input-sm"> <i class="ki-filled ki-magnifier"> </i>
<input placeholder="Search positions" id="search" type="text" value="">
</label>
</div>
<div class="flex flex-wrap gap-2.5 lg:gap-5">
<div class="h-[100%] border border-r-gray-200"> </div>
<a class="btn btn-sm btn-light" id="export-btn" href="{{ route('users.positions.export') }}">
Export to Excel </a>
<a class="btn btn-sm btn-primary" href="{{ route('users.positions.create') }}"> Add Position
</a>
</div>
</div>
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" />
</th>
<th class="min-w-[150px]" data-datatable-column="code">
<span class="sort"> <span class="sort-label"> Code </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[250px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Name </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[100px]" data-datatable-column="level">
<span class="sort"> <span class="sort-label"> Tingkat Jabatan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
</tr>
</thead>
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
page
</div>
<div class="flex gap-4 items-center">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script type="text/javascript">
function deleteData(data) {
Swal.fire({
title: 'Are you sure?',
text: "You won't be able to revert this!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!'
}).then((result) => {
if (result.isConfirmed) {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
});
$.ajax(`positions/${data}`, {
type: 'DELETE'
}).then((response) => {
swal.fire('Deleted!', 'Position has been deleted.', 'success').then(() => {
window.location.reload();
});
}).catch((error) => {
console.error('Error:', error);
Swal.fire('Error!', 'An error occurred while deleting the position.', 'error');
});
}
})
}
</script>
<script type="module">
const element = document.querySelector('#positions-table');
const searchInput = document.getElementById('search');
const apiUrl = element.getAttribute('data-api-url');
const dataTableOptions = {
apiEndpoint: apiUrl,
pageSize: 5,
columns: {
select: {
render: (item, data, context) => {
const checkbox = document.createElement('input');
checkbox.className = 'checkbox checkbox-sm';
checkbox.type = 'checkbox';
checkbox.value = data.id.toString();
checkbox.setAttribute('data-datatable-row-check', 'true');
return checkbox.outerHTML.trim();
},
},
code: {
title: 'Code',
},
name: {
title: 'Name',
},
level: {
title: 'Level',
},
actions: {
title: 'Status',
render: (item, data) => {
return `<div class="flex flex-nowrap justify-center">
<a class="btn btn-sm btn-icon btn-clear btn-info" href="positions/${data.id}/edit">
<i class="ki-outline ki-notepad-edit"></i>
</a>
<a onclick="deleteData(${data.id})" class="delete btn btn-sm btn-icon btn-clear btn-danger">
<i class="ki-outline ki-trash"></i>
</a>
</div>`;
},
}
},
};
let dataTable = new KTDataTable(element, dataTableOptions);
const exportBtn = document.getElementById('export-btn');
const baseExportUrl = exportBtn.getAttribute('href');
// Custom search functionality
searchInput.addEventListener('input', function() {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
dataTable.goPage(1);
// Update export URL with search parameter
if (searchValue) {
exportBtn.setAttribute('href', `${baseExportUrl}?search=${encodeURIComponent(searchValue)}`);
} else {
exportBtn.setAttribute('href', baseExportUrl);
}
});
</script>
@endpush

View File

@@ -6,12 +6,14 @@
@section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<form action="{{ isset($role->id) ? route('users.roles.update', $role->id) : route('users.roles.store') }}" method="POST" id="role_form">
@csrf
@if(isset($role->id))
<form action="{{ route('users.roles.update', $role->id) }}" method="POST" id="role_form">
<input type="hidden" name="id" value="{{ $role->id }}">
@method('PUT')
@else
<form method="POST" action="{{ route('users.roles.store') }}">
@endif
@csrf
<div class="card pb-2.5">
<div class="card-header" id="basic_settings">
<h3 class="card-title">
@@ -33,24 +35,6 @@
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Position
</label>
<div class="flex flex-wrap items-baseline w-full">
<select class="select tomselect @error('position_id') border-danger @enderror" name="position_id">
<option value="">Select Position</option>
@foreach($positions as $position)
<option value="{{ $position->id }}" {{ (isset($role) && $role->position_id == $position->id) ? 'selected' : '' }}>
{{ $position->name }} | Tingkat Jabatan: {{ $position->level }}
</option>
@endforeach
</select>
@error('position_id')
<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">
Administrator/Superuser Access

View File

@@ -7,9 +7,8 @@
@section('content')
<div class="container-fluid">
<div class="grid">
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="5"
data-datatable-state-save="true" id="roles-table" data-api-url="{{ route('users.roles.datatables') }}">
<div class="flex-wrap py-5 card-header">
<div class="card card-grid min-w-full" data-datatable="false" data-datatable-page-size="5" data-datatable-state-save="true" id="roles-table" data-api-url="{{ route('users.roles.datatables') }}">
<div class="card-header py-5 flex-wrap">
<h3 class="card-title">
List of Roles
</h3>
@@ -20,7 +19,29 @@
</label>
</div>
<div class="flex flex-wrap gap-2.5 lg:gap-5">
<div class="flex flex-wrap gap-2.5">
<select class="select select-sm w-28">
<option value="1">
Active
</option>
<option value="2">
Disabled
</option>
<option value="2">
Pending
</option>
</select>
<select class="select select-sm w-28">
<option value="desc">
Latest
</option>
<option value="asc">
Oldest
</option>
</select>
<button class="btn btn-sm btn-outline btn-primary">
<i class="ki-filled ki-setting-4"> </i> <Filters></Filters>
</button>
<div class="h-[24px] border border-r-gray-200"> </div>
<a class="btn btn-sm btn-light" href="{{ route('users.roles.export') }}"> Export to Excel </a>
<a class="btn btn-sm btn-primary" href="{{ route('users.roles.create') }}"> Add Role </a>
@@ -29,8 +50,7 @@
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
data-datatable-table="true">
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
@@ -40,27 +60,17 @@
<span class="sort"> <span class="sort-label"> Role </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[200px]" data-datatable-column="position_name">
<span class="sort"> <span class="sort-label"> Position </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[100px]" data-datatable-column="level">
<span class="sort"> <span class="sort-label"> Tingkat Jabatan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
</tr>
</thead>
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
<div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
page
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per page
</div>
<div class="flex gap-4 items-center">
<div class="flex items-center gap-4">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
@@ -128,20 +138,6 @@
name: {
title: 'Role',
},
position_name: {
title: 'Position',
render: (item, data) => {
return data.position ? data.position.name : '-';
},
sortable: true,
},
level: {
title: 'Level',
render: (item, data) => {
return data.position ? data.position.level : '-';
},
sortable: true,
},
actions: {
title: 'Status',
render: (item, data) => {
@@ -163,7 +159,7 @@
searchInput.addEventListener('input', function () {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
dataTable.goPage(1);
});
</script>
@endpush

View File

@@ -35,8 +35,7 @@
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 ?? '' }}">
<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
@@ -47,8 +46,7 @@
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 ?? '' }}">
<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
@@ -59,8 +57,7 @@
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 ?? '' }}">
<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
@@ -71,17 +68,23 @@
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 }}
<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('branches')
@error('branch_id')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
@@ -102,8 +105,7 @@
</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">
<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>
@@ -120,8 +122,7 @@
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">
<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>
@@ -143,31 +144,23 @@
<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
<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="">
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
<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="">
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">
<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">
<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">
@@ -176,13 +169,7 @@
</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
<input {{ in_array($role->name,Auth()->user()->roles->pluck('name')->toArray()) ? 'checked' : '' }} name="roles" type="radio" value="{{ $role->name }}">
</div>
</div>
@endforeach

View File

@@ -7,9 +7,8 @@
@section('content')
<div class="container-fluid">
<div class="grid">
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="5"
data-datatable-state-save="true" id="users-table" data-api-url="{{ route('users.datatables') }}">
<div class="flex-wrap py-5 card-header">
<div class="card card-grid min-w-full" data-datatable="false" data-datatable-page-size="5" data-datatable-state-save="true" id="users-table" data-api-url="{{ route('users.datatables') }}">
<div class="card-header py-5 flex-wrap">
<h3 class="card-title">
List of Users
</h3>
@@ -20,18 +19,38 @@
</label>
</div>
<div class="flex flex-wrap gap-2.5 lg:gap-5">
<div class="flex flex-wrap gap-2.5">
<select class="select select-sm w-28">
<option value="1">
Active
</option>
<option value="2">
Disabled
</option>
<option value="2">
Pending
</option>
</select>
<select class="select select-sm w-28">
<option value="desc">
Latest
</option>
<option value="asc">
Oldest
</option>
</select>
<button class="btn btn-sm btn-outline btn-primary">
<i class="ki-filled ki-setting-4"> </i> Filters
</button>
<div class="h-[24px] border border-r-gray-200"> </div>
<a class="btn btn-sm btn-light" id="export-btn" href="{{ route('users.export') }}"> Export to
Excel </a>
<a class="btn btn-sm btn-light" href="{{ route('users.export') }}"> Export to Excel </a>
<a class="btn btn-sm btn-primary" href="{{ route('users.create') }}"> Add User </a>
</div>
</div>
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
data-datatable-table="true">
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
@@ -53,23 +72,17 @@
<span class="sort"> <span class="sort-label"> Branch </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[185px]" data-datatable-column="role">
<span class="sort"> <span class="sort-label"> Role </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
</tr>
</thead>
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
<div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
page
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per page
</div>
<div class="flex gap-4 items-center">
<div class="flex items-center gap-4">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
@@ -118,20 +131,6 @@
<script type="module">
const element = document.querySelector('#users-table');
const searchInput = document.getElementById('search');
const exportBtn = document.getElementById('export-btn');
// Update export URL with filters
function updateExportUrl() {
let url = new URL(exportBtn.href);
if (searchInput.value) {
url.searchParams.set('search', searchInput.value);
} else {
url.searchParams.delete('search');
}
exportBtn.href = url.toString();
};
const apiUrl = element.getAttribute('data-api-url');
const dataTableOptions = {
@@ -160,17 +159,7 @@
branch: {
title: 'Branch',
render: (item, data) => {
if (data.branches && data.branches.length > 0) {
return data.branches.map(b => b.name).join(', ');
}
return '-';
},
},
role: {
title: 'Role',
render: (item, data) => {
console.log(data);
return data.roles.map(role => role.name).join(', ');
return data.branch.name;
},
},
actions: {
@@ -194,8 +183,7 @@
searchInput.addEventListener('input', function () {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
updateExportUrl();
dataTable.goPage(1);
});
</script>
@endpush

View File

@@ -59,101 +59,6 @@
</div>
<!-- End of Container -->
</div>
<div class="container-fluid mt-8 w-full">
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Edit Profile Form -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Edit Profile</h3>
</div>
<div class="card-body">
<form action="{{ route('users.update-profile') }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<div class="mb-4">
<label for="name" class="form-label">Name</label>
<input type="text" class="input @error('name') border-danger @enderror" id="name" name="name" value="{{ Auth::user()->name }}">
@error('name')
<div class="text-danger mt-2">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label for="email" class="form-label">Email</label>
<input type="email" class="input @error('email') border-danger @enderror" id="email" name="email" value="{{ Auth::user()->email }}">
@error('email')
<div class="text-danger mt-2">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label for="nik" class="form-label">NIK</label>
<input type="text" class="input @error('nik') border-danger @enderror" id="nik" name="nik" value="{{ Auth::user()->nik }}">
@error('nik')
<div class="text-danger mt-2">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label for="sign" class="form-label">E-Sign</label>
<input type="file" class="file-input @error('sign') border-danger @enderror" id="sign" name="sign" accept="image/*">
@if(Auth::user()->sign)
<div class="mt-2">
<p>Current E-Sign:</p>
<img src="{{ asset('storage/signatures/' . Auth::user()->id . '/' . Auth::user()->sign) }}"
alt="E-Sign"
class="mt-2 max-w-xs border border-gray-200 rounded">
</div>
@endif
@error('sign')
<div class="text-danger mt-2">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">Update Profile</button>
</form>
</div>
</div>
<!-- Change Password Form -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Change Password</h3>
</div>
<div class="card-body">
<form action="{{ route('users.change-password') }}" method="POST">
@csrf
@method('PUT')
<div class="mb-4">
<label for="current_password" class="form-label">Current Password</label>
<input type="password" class="input @error('current_password') border-danger @enderror" id="current_password" name="current_password">
@error('current_password')
<div class="text-danger mt-2">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label for="password" class="form-label">New Password</label>
<input type="password" class="input @error('password') border-danger @enderror" id="password" name="password">
@error('password')
<div class="text-danger mt-2">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label for="password_confirmation" class="form-label">Confirm New Password</label>
<input type="password" class="input @error('password_confirmation') border-danger @enderror" id="password_confirmation" name="password_confirmation">
@error('password_confirmation')
<div class="text-danger mt-2">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">Change Password</button>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -53,18 +53,3 @@
$trail->parent('users.permissions');
$trail->push('Edit Permission');
});
Breadcrumbs::for('users.positions', function (BreadcrumbTrail $trail) {
$trail->parent('users');
$trail->push('Positions', route('users.positions.index'));
});
Breadcrumbs::for('users.positions.create', function (BreadcrumbTrail $trail) {
$trail->parent('users.positions');
$trail->push('Add Position', route('users.positions.create'));
});
Breadcrumbs::for('users.positions.edit', function (BreadcrumbTrail $trail) {
$trail->parent('users.positions');
$trail->push('Edit Position');
});

View File

@@ -2,7 +2,6 @@
use Illuminate\Support\Facades\Route;
use Modules\Usermanagement\Http\Controllers\PermissionsController;
use Modules\Usermanagement\Http\Controllers\PositionsController;
use Modules\Usermanagement\Http\Controllers\RolesController;
use Modules\Usermanagement\Http\Controllers\UsersController;
@@ -22,11 +21,6 @@
Route::get('datatables', [UsersController::class, 'dataForDatatables'])->name('datatables');
Route::get('export', [UsersController::class, 'export'])->name('export');
Route::get('profile', [UsersController::class, 'profile'])->name('profile');
Route::put('/profile/update', [UsersController::class, 'updateProfile'])->name('update-profile');
Route::put('/profile/change-password', [UsersController::class, 'changePassword'])->name(
'change-password',
);
});
Route::resource('users', UsersController::class);
@@ -44,11 +38,6 @@
Route::get('export', [PermissionsController ::class, 'export'])->name('export');
});
Route::resource('permissions', PermissionsController::class);
});
});
Route::name('positions.')->prefix('positions')->group(function () {
Route::get('datatables', [PositionsController::class, 'dataForDatatables'])->name('datatables');
Route::get('export', [PositionsController::class, 'export'])->name('export');
});
Route::resource('positions', PositionsController::class);
});
});

View File

@@ -1,474 +0,0 @@
<?php
namespace Modules\Usermanagement\Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Modules\Usermanagement\Models\Permission;
use Modules\Usermanagement\Models\PermissionGroup;
use Modules\Usermanagement\Models\Role;
use Modules\Usermanagement\Models\User;
use Illuminate\Support\Facades\Auth;
class PermissionsControllerTest extends TestCase
{
use RefreshDatabase, WithFaker;
protected $user;
protected $adminRole;
protected $permissionGroup;
/**
* Setup the test environment.
*/
protected function setUp(): void
{
parent::setUp();
// Create admin role
$this->adminRole = Role::create([
'name' => 'Admin',
'guard_name' => 'web'
]);
// Create a permission group for usermanagement permissions
$usermanagementGroup = PermissionGroup::create(['name' => 'Usermanagement']);
// Create permissions
$permissions = [
'usermanagement.create',
'usermanagement.read',
'usermanagement.update',
'usermanagement.delete',
'usermanagement.export',
'usermanagement.store',
'usermanagement.edit',
'usermanagement.view',
'usermanagement.restore'
];
foreach ($permissions as $permission) {
Permission::create([
'name' => $permission,
'guard_name' => 'web',
'permission_group_id' => $usermanagementGroup->id
]);
}
// Assign permissions to admin role
$this->adminRole->givePermissionTo($permissions);
// Create user with admin role
$this->user = User::create([
'name' => 'Admin User',
'email' => 'admin@example.com',
'password' => bcrypt('password')
]);
$this->user->assignRole($this->adminRole);
// Create a test permission group
$this->permissionGroup = PermissionGroup::create(['name' => 'Test Group']);
}
/**
* Test user with permission can view permissions index.
*/
public function test_user_with_permission_can_view_permissions_index(): void
{
$this->actingAs($this->user);
$response = $this->get(route('users.permissions.index'));
$response->assertStatus(200);
$response->assertViewIs('usermanagement::permissions.index');
}
/**
* Test user without permission cannot view permissions index.
*/
public function test_user_without_permission_cannot_view_permissions_index(): void
{
// Create user without permissions
$user = User::create([
'name' => 'Regular User',
'email' => 'user@example.com',
'password' => bcrypt('password')
]);
$this->actingAs($user);
$response = $this->get(route('users.permissions.index'));
$response->assertStatus(403);
}
/**
* Test user with permission can create permission.
*/
public function test_user_with_permission_can_create_permission(): void
{
$this->actingAs($this->user);
$response = $this->get(route('users.permissions.create'));
$response->assertStatus(200);
$response->assertViewIs('usermanagement::permissions.create');
}
/**
* Test user without permission cannot create permission.
*/
public function test_user_without_permission_cannot_create_permission(): void
{
// Create user without permissions
$user = User::create([
'name' => 'Regular User',
'email' => 'user2@example.com',
'password' => bcrypt('password')
]);
$this->actingAs($user);
$response = $this->get(route('users.permissions.create'));
$response->assertStatus(403);
}
/**
* Test user with permission can store permission.
*/
public function test_user_with_permission_can_store_permission(): void
{
$this->actingAs($this->user);
$data = [
'name' => 'TestPermission'
];
$response = $this->post(route('users.permissions.store'), $data);
$response->assertRedirect(route('users.permissions.index'));
$response->assertSessionHas('success');
$this->assertDatabaseHas('permission_groups', ['name' => 'TestPermission']);
// Check if all the required permissions were created
$this->assertDatabaseHas('permissions', ['name' => 'testpermission.create']);
$this->assertDatabaseHas('permissions', ['name' => 'testpermission.read']);
$this->assertDatabaseHas('permissions', ['name' => 'testpermission.update']);
$this->assertDatabaseHas('permissions', ['name' => 'testpermission.delete']);
$this->assertDatabaseHas('permissions', ['name' => 'testpermission.export']);
$this->assertDatabaseHas('permissions', ['name' => 'testpermission.authorize']);
$this->assertDatabaseHas('permissions', ['name' => 'testpermission.report']);
}
/**
* Test user without permission cannot store permission.
*/
public function test_user_without_permission_cannot_store_permission(): void
{
// Create user without permissions
$user = User::create([
'name' => 'Regular User',
'email' => 'user3@example.com',
'password' => bcrypt('password')
]);
$this->actingAs($user);
$data = [
'name' => 'TestPermission2'
];
$response = $this->post(route('users.permissions.store'), $data);
$response->assertStatus(403);
$this->assertDatabaseMissing('permission_groups', ['name' => 'TestPermission2']);
}
/**
* Test user with permission can edit permission.
*/
public function test_user_with_permission_can_edit_permission(): void
{
$this->actingAs($this->user);
$response = $this->get(route('users.permissions.edit', $this->permissionGroup->id));
$response->assertStatus(200);
$response->assertViewIs('usermanagement::permissions.create');
$response->assertViewHas('permission', $this->permissionGroup);
}
/**
* Test user without permission cannot edit permission.
*/
public function test_user_without_permission_cannot_edit_permission(): void
{
// Create user without permissions
$user = User::create([
'name' => 'Regular User',
'email' => 'user4@example.com',
'password' => bcrypt('password')
]);
$this->actingAs($user);
$response = $this->get(route('users.permissions.edit', $this->permissionGroup->id));
$response->assertStatus(403);
}
/**
* Test user with permission can update permission.
*/
public function test_user_with_permission_can_update_permission(): void
{
$this->actingAs($this->user);
// Create permissions for the test group
$permissions = [
'test group.create',
'test group.read',
'test group.update',
'test group.delete',
'test group.export',
'test group.authorize',
'test group.report'
];
foreach ($permissions as $permission) {
Permission::create([
'name' => $permission,
'guard_name' => 'web',
'permission_group_id' => $this->permissionGroup->id
]);
}
$data = [
'name' => 'Updated Group'
];
$response = $this->put(route('users.permissions.update', $this->permissionGroup->id), $data);
$response->assertRedirect(route('users.permissions.index'));
$response->assertSessionHas('success');
$this->assertDatabaseHas('permission_groups', [
'id' => $this->permissionGroup->id,
'name' => 'Updated Group'
]);
// Check if all the permissions were updated
$this->assertDatabaseHas('permissions', ['name' => 'updated group.create']);
$this->assertDatabaseHas('permissions', ['name' => 'updated group.read']);
$this->assertDatabaseHas('permissions', ['name' => 'updated group.update']);
$this->assertDatabaseHas('permissions', ['name' => 'updated group.delete']);
$this->assertDatabaseHas('permissions', ['name' => 'updated group.export']);
$this->assertDatabaseHas('permissions', ['name' => 'updated group.authorize']);
$this->assertDatabaseHas('permissions', ['name' => 'updated group.report']);
}
/**
* Test user without permission cannot update permission.
*/
public function test_user_without_permission_cannot_update_permission(): void
{
// Create user without permissions
$user = User::create([
'name' => 'Regular User',
'email' => 'user5@example.com',
'password' => bcrypt('password')
]);
$this->actingAs($user);
$data = [
'name' => 'Should Not Update'
];
$response = $this->put(route('users.permissions.update', $this->permissionGroup->id), $data);
$response->assertStatus(403);
$this->assertDatabaseMissing('permission_groups', [
'id' => $this->permissionGroup->id,
'name' => 'Should Not Update'
]);
}
/**
* Test user with permission can delete permission.
*/
public function test_user_with_permission_can_delete_permission(): void
{
$this->actingAs($this->user);
$response = $this->delete(route('users.permissions.destroy', $this->permissionGroup->id));
$response->assertJson([
'message' => 'Permission deleted successfully.',
'success' => true
]);
$this->assertSoftDeleted('permission_groups', ['id' => $this->permissionGroup->id]);
}
/**
* Test user without permission cannot delete permission.
*/
public function test_user_without_permission_cannot_delete_permission(): void
{
// Create user without permissions
$user = User::create([
'name' => 'Regular User',
'email' => 'user6@example.com',
'password' => bcrypt('password')
]);
$this->actingAs($user);
$response = $this->delete(route('users.permissions.destroy', $this->permissionGroup->id));
$response->assertStatus(403);
$this->assertDatabaseHas('permission_groups', ['id' => $this->permissionGroup->id, 'deleted_at' => null]);
}
/**
* Test user with permission can restore permission.
*/
public function test_user_with_permission_can_restore_permission(): void
{
$this->actingAs($this->user);
// First delete the permission group
$this->permissionGroup->delete();
$this->assertSoftDeleted('permission_groups', ['id' => $this->permissionGroup->id]);
$response = $this->get(route('users.permissions.restore', $this->permissionGroup->id));
$response->assertRedirect(route('users.permissions.index'));
$response->assertSessionHas('success');
$this->assertDatabaseHas('permission_groups', ['id' => $this->permissionGroup->id, 'deleted_at' => null]);
}
/**
* Test user without permission cannot restore permission.
*/
public function test_user_without_permission_cannot_restore_permission(): void
{
// Create user without permissions
$user = User::create([
'name' => 'Regular User',
'email' => 'user7@example.com',
'password' => bcrypt('password')
]);
// First delete the permission group
$this->permissionGroup->delete();
$this->assertSoftDeleted('permission_groups', ['id' => $this->permissionGroup->id]);
$this->actingAs($user);
$response = $this->get(route('users.permissions.restore', $this->permissionGroup->id));
$response->assertStatus(403);
$this->assertSoftDeleted('permission_groups', ['id' => $this->permissionGroup->id]);
}
/**
* Test user with permission can access datatables data.
*/
public function test_user_with_permission_can_access_datatables_data(): void
{
$this->actingAs($this->user);
$response = $this->getJson(route('users.permissions.datatables') . '?page=1&size=10');
$response->assertStatus(200);
$response->assertJsonStructure([
'draw',
'recordsTotal',
'recordsFiltered',
'pageCount',
'page',
'totalCount',
'data'
]);
}
/**
* Test user without permission cannot access datatables data.
*/
public function test_user_without_permission_cannot_access_datatables_data(): void
{
// Create user without permissions
$user = User::create([
'name' => 'Regular User',
'email' => 'user8@example.com',
'password' => bcrypt('password')
]);
$this->actingAs($user);
$response = $this->getJson(route('users.permissions.datatables') . '?page=1&size=10');
$response->assertStatus(403);
}
/**
* Test datatables search filters permissions correctly.
*/
public function test_datatables_search_filters_permissions_correctly(): void
{
$this->actingAs($this->user);
// Create additional permission groups for testing search
PermissionGroup::create(['name' => 'SearchTest1']);
PermissionGroup::create(['name' => 'SearchTest2']);
PermissionGroup::create(['name' => 'DifferentName']);
$response = $this->getJson(route('users.permissions.datatables') . '?page=1&size=10&search=SearchTest');
$response->assertStatus(200);
$response->assertJsonCount(2, 'data');
$response->assertJsonPath('data.0.name', 'SearchTest1');
$response->assertJsonPath('data.1.name', 'SearchTest2');
}
/**
* Test datatables sorting works correctly.
*/
public function test_datatables_sorting_works_correctly(): void
{
$this->actingAs($this->user);
// Create additional permission groups for testing sorting
PermissionGroup::create(['name' => 'A-Group']);
PermissionGroup::create(['name' => 'Z-Group']);
// Test ascending order
$response = $this->getJson(route('users.permissions.datatables') . '?page=1&size=10&sortField=name&sortOrder=asc');
$response->assertStatus(200);
$response->assertJsonPath('data.0.name', 'A-Group');
// Test descending order
$response = $this->getJson(route('users.permissions.datatables') . '?page=1&size=10&sortField=name&sortOrder=desc');
$response->assertStatus(200);
$response->assertJsonPath('data.0.name', 'Z-Group');
}
/**
* Test export functionality.
*/
public function test_export_functionality(): void
{
$this->actingAs($this->user);
$response = $this->get(route('users.permissions.export'));
$response->assertStatus(200);
$response->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
}
}

View File

@@ -1,344 +0,0 @@
<?php
namespace Modules\Usermanagement\Tests\Feature;
use Tests\TestCase;
use Modules\Usermanagement\Models\Position;
use Modules\Usermanagement\Models\User;
use Modules\Usermanagement\Models\Role;
use Modules\Usermanagement\Models\Permission;
use Modules\Usermanagement\Models\PermissionGroup;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
class PositionsControllerTest extends TestCase
{
use RefreshDatabase;
protected $user;
protected $adminRole;
protected $position;
protected function setUp(): void
{
parent::setUp();
// Create permission group first
$permissionGroup = PermissionGroup::create([
'name' => 'usermanagement',
'slug' => 'usermanagement'
]);
// Create permissions with permission_group_id
Permission::create([
'name' => 'usermanagement.create',
'guard_name' => 'web',
'permission_group_id' => $permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.read',
'guard_name' => 'web',
'permission_group_id' => $permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.update',
'guard_name' => 'web',
'permission_group_id' => $permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.delete',
'guard_name' => 'web',
'permission_group_id' => $permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.export',
'guard_name' => 'web',
'permission_group_id' => $permissionGroup->id
]);
// Create admin role with all permissions
$this->adminRole = Role::create(['name' => 'admin', 'guard_name' => 'web']);
$this->adminRole->givePermissionTo(Permission::all());
// Create a user with admin role
$this->user = User::factory()->create();
$this->user->assignRole($this->adminRole);
// Create a position for testing
$this->position = Position::create([
'code' => 'TEST',
'name' => 'Test Position',
'level' => 1
]);
}
#[Test]
public function user_with_permission_can_view_positions_index()
{
$response = $this->actingAs($this->user)
->get(route('users.positions.index'));
$response->assertStatus(200);
}
#[Test]
public function user_without_permission_cannot_view_positions_index()
{
// Create a role without permissions
$role = Role::create(['name' => 'viewer', 'guard_name' => 'web']);
// Create a user with the viewer role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.positions.index'));
$response->assertStatus(403);
}
#[Test]
public function user_with_permission_can_create_position()
{
$response = $this->actingAs($this->user)
->get(route('users.positions.create'));
$response->assertStatus(200);
}
#[Test]
public function user_without_permission_cannot_create_position()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.positions.create'));
$response->assertStatus(403);
}
#[Test]
public function user_with_permission_can_store_position()
{
$positionData = [
'code' => 'NEW',
'name' => 'New Position',
'level' => 2
];
$response = $this->actingAs($this->user)
->post(route('users.positions.store'), $positionData);
$response->assertRedirect(route('users.positions.index'));
$this->assertDatabaseHas('positions', $positionData);
}
#[Test]
public function user_without_permission_cannot_store_position()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$positionData = [
'code' => 'NEW',
'name' => 'New Position',
'level' => 2
];
$response = $this->actingAs($user)
->post(route('users.positions.store'), $positionData);
$response->assertStatus(403);
$this->assertDatabaseMissing('positions', $positionData);
}
#[Test]
public function user_with_permission_can_edit_position()
{
$response = $this->actingAs($this->user)
->get(route('users.positions.edit', $this->position->id));
$response->assertStatus(200);
}
#[Test]
public function user_without_permission_cannot_edit_position()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.positions.edit', $this->position->id));
$response->assertStatus(403);
}
#[Test]
public function user_with_permission_can_update_position()
{
$updatedData = [
'code' => 'UPD',
'name' => 'Updated Position',
'level' => 3
];
$response = $this->actingAs($this->user)
->put(route('users.positions.update', $this->position->id), $updatedData);
$response->assertRedirect(route('users.positions.index'));
$this->assertDatabaseHas('positions', $updatedData);
}
#[Test]
public function user_without_permission_cannot_update_position()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$updatedData = [
'code' => 'UPD',
'name' => 'Updated Position',
'level' => 3
];
$response = $this->actingAs($user)
->put(route('users.positions.update', $this->position->id), $updatedData);
$response->assertStatus(403);
$this->assertDatabaseMissing('positions', $updatedData);
}
#[Test]
public function user_with_permission_can_delete_position()
{
$response = $this->actingAs($this->user)
->delete(route('users.positions.destroy', $this->position->id));
$response->assertRedirect(route('users.positions.index'));
$this->assertSoftDeleted($this->position);
}
#[Test]
public function user_without_permission_cannot_delete_position()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->delete(route('users.positions.destroy', $this->position->id));
$response->assertStatus(403);
$this->assertDatabaseHas('positions', ['id' => $this->position->id, 'deleted_at' => null]);
}
#[Test]
public function user_with_permission_can_access_datatables_data()
{
$response = $this->actingAs($this->user)
->get(route('users.positions.datatables'));
$response->assertStatus(200);
$response->assertJsonStructure([
'draw',
'recordsTotal',
'recordsFiltered',
'pageCount',
'page',
'totalCount',
'data'
]);
}
#[Test]
public function user_without_permission_cannot_access_datatables_data()
{
// Create a role without permissions
$role = Role::create(['name' => 'viewer', 'guard_name' => 'web']);
// Create a user with the viewer role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.positions.datatables'));
$response->assertStatus(403);
}
#[Test]
public function user_with_permission_can_export_positions()
{
$response = $this->actingAs($this->user)
->get(route('users.positions.export'));
$response->assertStatus(200);
}
#[Test]
public function user_without_permission_cannot_export_positions()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.positions.export'));
$response->assertStatus(403);
}
#[Test]
public function cannot_delete_position_if_it_has_associated_roles()
{
// Create a role associated with the position
$role = Role::create([
'name' => 'Position-Linked Role',
'guard_name' => 'web',
'position_id' => $this->position->id
]);
// Attempt to delete the position
$response = $this->actingAs($this->user)
->delete(route('users.positions.destroy', $this->position->id));
// Assert that the request is redirected back with an error message
$response->assertRedirect(route('users.positions.index'));
$response->assertSessionHas('error');
// Assert that the position still exists in the database (not deleted)
$this->assertDatabaseHas('positions', [
'id' => $this->position->id,
'deleted_at' => null
]);
}
}

View File

@@ -1,467 +0,0 @@
<?php
namespace Modules\Usermanagement\Tests\Feature;
use Tests\TestCase;
use Modules\Usermanagement\Models\Position;
use Modules\Usermanagement\Models\User;
use Modules\Usermanagement\Models\Role;
use Modules\Usermanagement\Models\Permission;
use Modules\Usermanagement\Models\PermissionGroup;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
class RolesControllerTest extends TestCase
{
use RefreshDatabase;
protected $user;
protected $adminRole;
protected $position;
protected $testRole;
protected $permissionGroup;
protected function setUp(): void
{
parent::setUp();
// Create permission group first
$this->permissionGroup = PermissionGroup::create([
'name' => 'usermanagement',
'slug' => 'usermanagement'
]);
// Create permissions with permission_group_id
Permission::create([
'name' => 'usermanagement.create',
'guard_name' => 'web',
'permission_group_id' => $this->permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.read',
'guard_name' => 'web',
'permission_group_id' => $this->permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.update',
'guard_name' => 'web',
'permission_group_id' => $this->permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.delete',
'guard_name' => 'web',
'permission_group_id' => $this->permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.export',
'guard_name' => 'web',
'permission_group_id' => $this->permissionGroup->id
]);
Permission::create([
'name' => 'usermanagement.restore',
'guard_name' => 'web',
'permission_group_id' => $this->permissionGroup->id
]);
// Create admin role with all permissions
$this->adminRole = Role::create(['name' => 'admin', 'guard_name' => 'web']);
$this->adminRole->givePermissionTo(Permission::all());
// Create a user with admin role
$this->user = User::factory()->create();
$this->user->assignRole($this->adminRole);
// Create a position for testing
$this->position = Position::create([
'code' => 'TEST',
'name' => 'Test Position',
'level' => 1
]);
// Create a test role for testing
$this->testRole = Role::create([
'name' => 'test-role',
'guard_name' => 'web',
'position_id' => $this->position->id
]);
$this->testRole->givePermissionTo('usermanagement.read');
}
#[Test]
public function user_with_permission_can_view_roles_index()
{
$response = $this->actingAs($this->user)
->get(route('users.roles.index'));
$response->assertStatus(200);
}
#[Test]
public function user_without_permission_cannot_view_roles_index()
{
// Create a role without permissions
$role = Role::create(['name' => 'viewer', 'guard_name' => 'web']);
// Create a user with the viewer role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.roles.index'));
$response->assertStatus(403);
}
#[Test]
public function user_with_permission_can_create_role()
{
$response = $this->actingAs($this->user)
->get(route('users.roles.create'));
$response->assertStatus(200);
}
#[Test]
public function user_without_permission_cannot_create_role()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.roles.create'));
$response->assertStatus(403);
}
#[Test]
public function user_with_permission_can_store_role()
{
$permissions = Permission::where('name', 'usermanagement.read')->pluck('id')->toArray();
$roleData = [
'name' => 'New Role',
'guard_name' => 'web',
'position_id' => $this->position->id,
'permissions' => $permissions
];
$response = $this->actingAs($this->user)
->post(route('users.roles.store'), $roleData);
$response->assertRedirect(route('users.roles.index'));
$this->assertDatabaseHas('roles', [
'name' => 'New Role',
'position_id' => $this->position->id
]);
// Check if permission was assigned
$newRole = Role::where('name', 'New Role')->first();
$this->assertTrue($newRole->hasPermissionTo('usermanagement.read'));
}
#[Test]
public function user_without_permission_cannot_store_role()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$permissions = Permission::where('name', 'usermanagement.read')->pluck('id')->toArray();
$roleData = [
'name' => 'New Role',
'guard_name' => 'web',
'position_id' => $this->position->id,
'permissions' => $permissions
];
$response = $this->actingAs($user)
->post(route('users.roles.store'), $roleData);
$response->assertStatus(403);
$this->assertDatabaseMissing('roles', ['name' => 'New Role']);
}
#[Test]
public function user_with_permission_can_edit_role()
{
$response = $this->actingAs($this->user)
->get(route('users.roles.edit', $this->testRole->id));
$response->assertStatus(200);
}
#[Test]
public function user_without_permission_cannot_edit_role()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.roles.edit', $this->testRole->id));
$response->assertStatus(403);
}
#[Test]
public function user_with_permission_can_update_role()
{
$permissions = Permission::whereIn('name', ['usermanagement.read', 'usermanagement.update'])->pluck('id')->toArray();
$updatedData = [
'name' => 'Updated Role',
'guard_name' => 'web',
'position_id' => $this->position->id,
'permissions' => $permissions
];
$response = $this->actingAs($this->user)
->put(route('users.roles.update', $this->testRole->id), $updatedData);
$response->assertRedirect(route('users.roles.index'));
$this->assertDatabaseHas('roles', [
'id' => $this->testRole->id,
'name' => 'Updated Role',
'position_id' => $this->position->id
]);
// Check if permissions were updated
$updatedRole = Role::find($this->testRole->id);
$this->assertTrue($updatedRole->hasPermissionTo('usermanagement.read'));
$this->assertTrue($updatedRole->hasPermissionTo('usermanagement.update'));
}
#[Test]
public function user_without_permission_cannot_update_role()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$permissions = Permission::whereIn('name', ['usermanagement.read', 'usermanagement.update'])->pluck('id')->toArray();
$updatedData = [
'name' => 'Updated Role',
'guard_name' => 'web',
'position_id' => $this->position->id,
'permissions' => $permissions
];
$response = $this->actingAs($user)
->put(route('users.roles.update', $this->testRole->id), $updatedData);
$response->assertStatus(403);
$this->assertDatabaseMissing('roles', [
'id' => $this->testRole->id,
'name' => 'Updated Role'
]);
}
#[Test]
public function user_with_permission_can_delete_role()
{
$response = $this->actingAs($this->user)
->delete(route('users.roles.destroy', $this->testRole->id));
// The destroy method returns JSON response
$response->assertJson([
'message' => 'Role deleted successfully.',
'success' => true
]);
$this->assertSoftDeleted($this->testRole);
}
#[Test]
public function user_without_permission_cannot_delete_role()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->delete(route('users.roles.destroy', $this->testRole->id));
$response->assertStatus(403);
$this->assertDatabaseHas('roles', [
'id' => $this->testRole->id,
'deleted_at' => null
]);
}
#[Test]
public function user_with_permission_can_restore_role()
{
// First soft delete the role
$this->testRole->delete();
$this->assertSoftDeleted($this->testRole);
$response = $this->actingAs($this->user)
->get(route('users.roles.restore', $this->testRole->id));
$response->assertRedirect(route('users.roles.index'));
$this->assertDatabaseHas('roles', [
'id' => $this->testRole->id,
'deleted_at' => null
]);
}
#[Test]
public function user_without_permission_cannot_restore_role()
{
// Create a role with only read permission
$role = Role::create(['name' => 'reader', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.read');
// Create a user with the reader role
$user = User::factory()->create();
$user->assignRole($role);
// First soft delete the role
$this->testRole->delete();
$this->assertSoftDeleted($this->testRole);
$response = $this->actingAs($user)
->get(route('users.roles.restore', $this->testRole->id));
$response->assertStatus(403);
$this->assertSoftDeleted($this->testRole);
}
#[Test]
public function user_with_permission_can_access_datatables_data()
{
$response = $this->actingAs($this->user)
->get(route('users.roles.datatables'));
$response->assertStatus(200);
$response->assertJsonStructure([
'draw',
'recordsTotal',
'recordsFiltered',
'pageCount',
'page',
'totalCount',
'data'
]);
}
#[Test]
public function user_without_permission_cannot_access_datatables_data()
{
// Create a role without permissions
$role = Role::create(['name' => 'viewer', 'guard_name' => 'web']);
// Create a user with the viewer role
$user = User::factory()->create();
$user->assignRole($role);
$response = $this->actingAs($user)
->get(route('users.roles.datatables'));
$response->assertStatus(403);
}
#[Test]
public function user_with_permission_can_export_roles()
{
$response = $this->actingAs($this->user)
->get(route('users.roles.export'));
$response->assertStatus(200);
$response->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
}
#[Test]
public function datatables_search_filters_roles_correctly()
{
// Create additional roles for testing search
Role::create([
'name' => 'searchable-role',
'guard_name' => 'web',
'position_id' => $this->position->id
]);
$response = $this->actingAs($this->user)
->get(route('users.roles.datatables', ['search' => 'searchable']));
$response->assertStatus(200);
$responseData = json_decode($response->getContent(), true);
// Check that the search returned the correct role
$this->assertGreaterThan(0, $responseData['recordsFiltered']);
$foundSearchableRole = false;
foreach ($responseData['data'] as $role) {
if ($role['name'] === 'searchable-role') {
$foundSearchableRole = true;
break;
}
}
$this->assertTrue($foundSearchableRole);
}
#[Test]
public function datatables_sorting_works_correctly()
{
// Create additional roles for testing sorting
Role::create([
'name' => 'A-role', // Should come first in ascending order
'guard_name' => 'web',
'position_id' => $this->position->id
]);
Role::create([
'name' => 'Z-role', // Should come last in ascending order
'guard_name' => 'web',
'position_id' => $this->position->id
]);
// Test ascending order
$response = $this->actingAs($this->user)
->get(route('users.roles.datatables', [
'sortField' => 'name',
'sortOrder' => 'asc'
]));
$response->assertStatus(200);
$responseData = json_decode($response->getContent(), true);
// Check that the first role is 'A-role'
$this->assertEquals('A-role', $responseData['data'][0]['name']);
// Test descending order
$response = $this->actingAs($this->user)
->get(route('users.roles.datatables', [
'sortField' => 'name',
'sortOrder' => 'desc'
]));
$response->assertStatus(200);
$responseData = json_decode($response->getContent(), true);
// Check that the first role is 'Z-role'
$this->assertEquals('Z-role', $responseData['data'][0]['name']);
}
}

View File

@@ -1,466 +0,0 @@
<?php
namespace Modules\Usermanagement\Tests\Feature;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
use Modules\Usermanagement\Models\User;
use Modules\Usermanagement\Models\Role;
use Modules\Usermanagement\Models\Permission;
use Modules\Usermanagement\Models\PermissionGroup;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use PHPUnit\Framework\Attributes\Test;
class UsersControllerTest extends TestCase
{
use RefreshDatabase;
protected $user;
protected $adminRole;
protected function setUp(): void
{
parent::setUp();
// Create permission group
$permissionGroup = PermissionGroup::create([
'name' => 'usermanagement',
'slug' => 'usermanagement'
]);
// Create usermanagement.read permission
Permission::create([
'name' => 'usermanagement.read',
'guard_name' => 'web',
'permission_group_id' => $permissionGroup->id
]);
// Create role with usermanagement.read permission
$this->adminRole = Role::create(['name' => 'admin', 'guard_name' => 'web']);
$this->adminRole->givePermissionTo('usermanagement.read');
// Create a user with admin role
// Create a user for testing
$this->user = User::factory()->create([
'name' => 'Original Name',
'email' => 'original@example.com',
'nik' => '123456',
'sign' => 'old-signature.jpg'
]);
// Mock the storage
Storage::fake('public');
$this->user->assignRole($this->adminRole);
// Create test role for assignment to new user
Role::create(['name' => 'operator', 'guard_name' => 'web']);
}
#[Test]
public function should_display_users_index_page_when_user_has_users_view_permission()
{
$response = $this->actingAs($this->user)
->get(route('users.index'));
$response->assertStatus(200);
$response->assertViewIs('usermanagement::users.index');
}
#[Test]
public function should_return_json_response_with_correct_pagination_data_for_datatables()
{
// Create some test users
$testUsers = User::factory()->count(15)->create();
// Set up the request parameters
$requestData = [
'draw' => 1,
'page' => 1, // Changed from 2 to 1 to match the controller logic
'size' => 5,
'search' => '',
'sortField' => 'name',
'sortOrder' => 'asc'
];
// Make the request
$response = $this->actingAs($this->user)
->getJson(route('users.datatables') . '?' . http_build_query($requestData));
// Assert response status and structure
$response->assertStatus(200);
$response->assertJsonStructure([
'draw',
'recordsTotal',
'recordsFiltered',
'pageCount',
'page',
'totalCount',
'data'
]);
// Get total count of users for verification (16 = 15 created + 1 from setup)
$totalUsers = User::count();
// Verify the pagination data
$responseData = $response->json();
$this->assertEquals(1, $responseData['draw']);
$this->assertEquals($totalUsers, $responseData['recordsTotal']);
$this->assertEquals($totalUsers, $responseData['recordsFiltered']);
$this->assertEquals(ceil($totalUsers / $requestData['size']), $responseData['pageCount']);
$this->assertEquals($requestData['page'], $responseData['page']);
// Verify that we have the correct number of users in the response
$this->assertCount(5, $responseData['data']);
// Verify that the data is ordered correctly - get first page of sorted data
$this->assertEquals(
User::orderBy('name', 'asc')->take(5)->pluck('id')->toArray(),
collect($responseData['data'])->pluck('id')->toArray()
);
}
#[Test]
public function should_filter_users_by_search_term_when_search_parameter_is_provided()
{
// Create test users with specific names for testing search
$matchingUser1 = User::factory()->create(['name' => 'Test User One']);
$matchingUser2 = User::factory()->create(['email' => 'test@example.com']);
$nonMatchingUser = User::factory()->create(['name' => 'Different User', 'email' => 'different@example.com']);
// Set up the request parameters with search term
$requestData = [
'draw' => 1,
'page' => 1,
'size' => 10,
'search' => 'test',
'sortField' => 'name',
'sortOrder' => 'asc'
];
// Make the request
$response = $this->actingAs($this->user)
->getJson(route('users.datatables') . '?' . http_build_query($requestData));
// Assert response status and structure
$response->assertStatus(200);
$response->assertJsonStructure([
'draw',
'recordsTotal',
'recordsFiltered',
'pageCount',
'page',
'totalCount',
'data'
]);
// Get the response data
$responseData = $response->json();
// Verify that only matching users are returned
$this->assertEquals(2, $responseData['recordsFiltered']);
// Extract user IDs from the response
$returnedUserIds = collect($responseData['data'])->pluck('id')->toArray();
// Verify the correct users are returned
$this->assertContains($matchingUser1->id, $returnedUserIds);
$this->assertContains($matchingUser2->id, $returnedUserIds);
$this->assertNotContains($nonMatchingUser->id, $returnedUserIds);
}
#[Test]
public function should_correctly_sort_users_when_sortField_and_sortOrder_parameters_are_specified()
{
// Create test users with varying names to test different sort orders
$userA = User::factory()->create(['name' => 'Adam Smith']);
$userB = User::factory()->create(['name' => 'Brian Jones']);
$userC = User::factory()->create(['name' => 'Charlie Brown']);
// Test ascending order
$requestDataAsc = [
'draw' => 1,
'page' => 1,
'size' => 10,
'search' => '',
'sortField' => 'name',
'sortOrder' => 'asc'
];
$responseAsc = $this->actingAs($this->user)
->getJson(route('users.datatables') . '?' . http_build_query($requestDataAsc));
$responseAsc->assertStatus(200);
$responseDataAsc = $responseAsc->json();
// Check if sorted ascending by name
$userIdsAsc = collect($responseDataAsc['data'])->pluck('id')->toArray();
$expectedOrderAsc = User::orderBy('name', 'asc')->pluck('id')->toArray();
$this->assertEquals($expectedOrderAsc, $userIdsAsc);
// Test descending order
$requestDataDesc = [
'draw' => 1,
'page' => 1,
'size' => 10,
'search' => '',
'sortField' => 'name',
'sortOrder' => 'desc'
];
$responseDesc = $this->actingAs($this->user)
->getJson(route('users.datatables') . '?' . http_build_query($requestDataDesc));
$responseDesc->assertStatus(200);
$responseDataDesc = $responseDesc->json();
// Check if sorted descending by name
$userIdsDesc = collect($responseDataDesc['data'])->pluck('id')->toArray();
$expectedOrderDesc = User::orderBy('name', 'desc')->pluck('id')->toArray();
$this->assertEquals($expectedOrderDesc, $userIdsDesc);
// Test sorting by a different field (email)
$requestDataEmail = [
'draw' => 1,
'page' => 1,
'size' => 10,
'search' => '',
'sortField' => 'email',
'sortOrder' => 'asc'
];
$responseEmail = $this->actingAs($this->user)
->getJson(route('users.datatables') . '?' . http_build_query($requestDataEmail));
$responseEmail->assertStatus(200);
$responseDataEmail = $responseEmail->json();
// Check if sorted by email
$userIdsEmail = collect($responseDataEmail['data'])->pluck('id')->toArray();
$expectedOrderEmail = User::orderBy('email', 'asc')->pluck('id')->toArray();
$this->assertEquals($expectedOrderEmail, $userIdsEmail);
}
#[Test]
public function should_successfully_create_a_new_user_and_assign_roles_when_valid_data_is_submitted()
{
// Prepare valid user data
$userData = [
'name' => 'Test User',
'email' => 'test@example.com',
'password' => 'password123',
'password_confirmation' => 'password123',
'nik' => '789234',
'roles' => ['operator']
];
// Submit the request to create a new user
$response = $this->actingAs($this->user)
->post(route('users.store'), $userData);
// Assert redirect to users index page with success message
$response->assertRedirect(route('users.index'));
$response->assertSessionHas('success', 'User created successfully.');
// Assert the user was created in the database
$this->assertDatabaseHas('users', [
'name' => 'Test User',
'email' => 'test@example.com',
'nik' => '789234'
]);
// Assert the user was assigned the correct role
$newUser = User::where('email', 'test@example.com')->first();
$this->assertTrue($newUser->hasRole('operator'));
}
#[Test]
public function should_successfully_update_existing_user_information_and_role_assignments()
{
// Create a test user with admin role
$userToUpdate = User::factory()->create([
'name' => 'Original Name',
'email' => 'originalee@example.com',
'nik' => '987654'
]);
$userToUpdate->assignRole($this->adminRole);
// Create an additional role for the update test
$newRole = Role::create(['name' => 'editor', 'guard_name' => 'web']);
// Prepare update data
$updateData = [
'name' => 'Updated Name',
'email' => 'updated@example.com',
'nik' => '654321',
'roles' => ['operator'] // Change role from admin to operator
];
// Make the request to update the user
$response = $this->actingAs($this->user)
->put(route('users.update', $userToUpdate->id), $updateData);
// Assert redirect to users index page with success message
$response->assertRedirect(route('users.index'));
$response->assertSessionHas('success', 'User updated successfully.');
// Assert the user was updated in the database
$this->assertDatabaseHas('users', [
'id' => $userToUpdate->id,
'name' => 'Updated Name',
'email' => 'updated@example.com',
'nik' => '654321'
]);
// Refresh the user model from database
$userToUpdate->refresh();
// Assert the user has the new role and doesn't have the old role
$this->assertTrue($userToUpdate->hasRole('operator'));
$this->assertFalse($userToUpdate->hasRole('admin'));
}
#[Test]
public function should_delete_a_user_when_the_authenticated_user_has_users_delete_permission()
{
// Create the permission for delete users
$permissionGroup = PermissionGroup::create([
'name' => 'usermanagement',
'slug' => 'usermanagement'
]);
// Create delete permission
Permission::create([
'name' => 'usermanagement.delete',
'guard_name' => 'web',
'permission_group_id' => $permissionGroup->id
]);
// Create role with delete permission
$role = Role::create(['name' => 'manager', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.delete');
// Create an admin user with the role that has delete permission
$adminUser = User::factory()->create();
$adminUser->assignRole($role);
// Create a user to be deleted
$userToDelete = User::factory()->create();
// Make the request to delete the user
$response = $this->actingAs($adminUser)
->delete(route('users.destroy', $userToDelete->id));
// Assert the response is correct
$decodedResponse = json_decode($response->getContent(), true);
$this->assertEquals('User deleted successfully.', $decodedResponse['message']);
$this->assertTrue($decodedResponse['success']);
// Assert the user was soft deleted
$this->assertSoftDeleted('users', ['id' => $userToDelete->id]);
}
#[Test]
public function should_restore_a_soft_deleted_user_when_the_authenticated_user_has_users_restore_permission()
{
// Create permission group
$permissionGroup = PermissionGroup::create([
'name' => 'usermanagement',
'slug' => 'usermanagement'
]);
// Create restore permission
Permission::create([
'name' => 'usermanagement.restore',
'guard_name' => 'web',
'permission_group_id' => $permissionGroup->id
]);
// Create role with restore permission
$role = Role::create(['name' => 'restorer', 'guard_name' => 'web']);
$role->givePermissionTo('usermanagement.restore');
// Create an admin user with the role that has restore permission
$adminUser = User::factory()->create();
$adminUser->assignRole($role);
// Create a user to be restored
$userToRestore = User::factory()->create();
$userToRestore->delete(); // Soft delete the user
// Verify the user is soft-deleted
$this->assertSoftDeleted('users', ['id' => $userToRestore->id]);
// Make the request to restore the user
$response = $this->actingAs($adminUser)
->get(route('users.restore', $userToRestore->id));
// Assert the response redirects to users.index with success message
$response->assertRedirect(route('users.index'));
$response->assertSessionHas('success', 'User restored successfully.');
// Assert the user was restored
$this->assertDatabaseHas('users', [
'id' => $userToRestore->id,
'deleted_at' => null
]);
}
#[Test]
public function should_update_users_profile_including_signature_image_when_valid_data_is_submitted()
{
// Create a fake signature file
$file = UploadedFile::fake()->image('new-signature.jpg');
// Create a fake old signature file in storage
Storage::disk('public')->put(
'signatures/' . $this->user->id . '/old-signature.jpg',
'fake content'
);
// Prepare valid profile data with new signature
$profileData = [
'name' => 'Updated Name',
'email' => 'updated@example.com',
'nik' => '654321',
'sign' => $file
];
// Make the request to update the profile
$response = $this->actingAs($this->user)
->put(route('users.update-profile'), $profileData);
// Assert redirect to profile page with success message
$response->assertRedirect(route('users.profile'));
$response->assertSessionHas('success', 'Profile updated successfully.');
// Assert the user was updated in the database
$this->assertDatabaseHas('users', [
'id' => $this->user->id,
'name' => 'Updated Name',
'email' => 'updated@example.com',
'nik' => '654321',
]);
// Refresh the user model from database
$this->user->refresh();
// Assert that the user has a sign value (any non-empty string)
$this->assertNotEmpty($this->user->sign);
// Debug information
$files = Storage::disk('public')->allFiles('signatures/' . $this->user->id);
// Assert the file has been stored in the expected location
// Use a more flexible check that doesn't rely on the exact filename
$signaturePath = 'signatures/' . $this->user->id;
$this->assertTrue(
Storage::disk('public')->exists($signaturePath . '/' . $this->user->sign),
"Signature file not found at expected location: {$signaturePath}/{$this->user->sign}"
);
// Verify old signature was deleted
Storage::disk('public')->assertMissing('signatures/' . $this->user->id . '/old-signature.jpg');
}
}