3 Commits

37 changed files with 611 additions and 3315 deletions

View File

@@ -21,8 +21,7 @@ class PermissionExport implements WithColumnFormatting, WithHeadings, FromCollec
} }
public function map($row): array{ public function map($row): array{
// Convert the array to a collection before using pluck $role = $row->roles->pluck('name')->toArray();
$role = collect($row->roles)->pluck('name')->toArray();
return [ return [
$row->id, $row->id,
$row->name, $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 class RolesExport implements WithColumnFormatting, WithHeadings, FromCollection, withMapping
{ {
public function collection(){ public function collection(){
return Role::with('position')->get(); return Role::all();
} }
public function map($row): array{ public function map($row): array{
return [ return [
$row->id, $row->id,
$row->name, $row->name,
$row->position ? $row->position->name : '-',
$row->position ? $row->position->level : '-',
$row->created_at $row->created_at
]; ];
} }
@@ -27,8 +25,6 @@ class RolesExport implements WithColumnFormatting, WithHeadings, FromCollection,
return [ return [
'ID', 'ID',
'Role', 'Role',
'Position',
'Tingkat Jabatan',
'Created At' 'Created At'
]; ];
} }
@@ -36,8 +32,7 @@ class RolesExport implements WithColumnFormatting, WithHeadings, FromCollection,
public function columnFormats(): array{ public function columnFormats(): array{
return [ return [
'A' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER, 'A' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER,
'D' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER, 'C' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
'E' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
]; ];
} }
} }

View File

@@ -9,21 +9,10 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use Maatwebsite\Excel\Concerns\WithColumnFormatting; use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Modules\Usermanagement\Models\User; 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(){ public function collection(){
return User::query() return User::all();
->when($this->search, function ($query) {
$query->whereAny(['name','email'],'like','%'.$this->search.'%');
})
->get();
} }
public function map($row): array{ public function map($row): array{
@@ -32,8 +21,7 @@ class UsersExport implements WithColumnFormatting, WithHeadings, FromCollection,
$row->name, $row->name,
$row->email, $row->email,
$row->nik, $row->nik,
$row->branch?->name, $row->branch->name,
$row->roles?->pluck('name')->implode(', '),
$row->created_at $row->created_at
]; ];
} }
@@ -44,7 +32,6 @@ class UsersExport implements WithColumnFormatting, WithHeadings, FromCollection,
'Email', 'Email',
'NIK', 'NIK',
'Branch', 'Branch',
'Roles',
'Created At' 'Created At'
]; ];
} }
@@ -52,7 +39,7 @@ class UsersExport implements WithColumnFormatting, WithHeadings, FromCollection,
public function columnFormats(): array{ public function columnFormats(): array{
return [ return [
'A' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER, '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 * @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);
});
}
/** /**
* Display a listing of the resource. * Display a listing of the resource.
@@ -51,8 +34,8 @@
public function index() public function index()
{ {
// Check if the authenticated user has the required permission to view permissions // Check if the authenticated user has the required permission to view permissions
if (is_null($this->user) || !$this->user->can('usermanagement.read')) { if (is_null($this->user) || !$this->user->can('permissions.view')) {
abort(403, 'Sorry! You are not allowed to view permissions.'); //abort(403, 'Sorry! You are not allowed to view permissions.');
} }
// Return the view for displaying the permissions // Return the view for displaying the permissions
@@ -70,8 +53,8 @@
public function store(PermissionRequest $request) public function store(PermissionRequest $request)
{ {
// Check if the authenticated user has the required permission to store permissions // Check if the authenticated user has the required permission to store permissions
if (is_null($this->user) || !$this->user->can('usermanagement.create')) { if (is_null($this->user) || !$this->user->can('permissions.store')) {
abort(403, 'Sorry! You are not allowed to create permissions.'); //abort(403, 'Sorry! You are not allowed to store permissions.');
} }
$validate = $request->validated(); $validate = $request->validated();
@@ -87,8 +70,7 @@
$group_name . '.delete', $group_name . '.delete',
$group_name . '.export', $group_name . '.export',
$group_name . '.authorize', $group_name . '.authorize',
$group_name . '.report', $group_name . '.report'
$group_name . '.restore'
]; ];
foreach ($data as $permission) { foreach ($data as $permission) {
@@ -115,14 +97,24 @@
public function create() public function create()
{ {
// Check if the authenticated user has the required permission to create permissions // Check if the authenticated user has the required permission to create permissions
if (is_null($this->user) || !$this->user->can('usermanagement.create')) { if (is_null($this->user) || !$this->user->can('permissions.create')) {
abort(403, 'Sorry! You are not allowed to create permissions.'); //abort(403, 'Sorry! You are not allowed to create permissions.');
} }
// Return the view for creating a new role // Return the view for creating a new role
return view('usermanagement::permissions.create'); 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. * Show the form for editing the specified resource.
* *
@@ -134,8 +126,8 @@
public function edit($id) public function edit($id)
{ {
// Check if the authenticated user has the required permission to edit permissions // Check if the authenticated user has the required permission to edit permissions
if (is_null($this->user) || !$this->user->can('usermanagement.update')) { if (is_null($this->user) || !$this->user->can('permissions.edit')) {
abort(403, 'Sorry! You are not allowed to edit permissions.'); //abort(403, 'Sorry! You are not allowed to edit permissions.');
} }
$permission = PermissionGroup::find($id); $permission = PermissionGroup::find($id);
@@ -158,8 +150,8 @@
public function update(PermissionRequest $request, $id) public function update(PermissionRequest $request, $id)
{ {
// Check if the authenticated user has the required permission to update permissions // Check if the authenticated user has the required permission to update permissions
if (is_null($this->user) || !$this->user->can('usermanagement.update')) { if (is_null($this->user) || !$this->user->can('permissions.update')) {
abort(403, 'Sorry! You are not allowed to update permissions.'); //abort(403, 'Sorry! You are not allowed to update permissions.');
} }
$validated = $request->validated(); $validated = $request->validated();
@@ -181,8 +173,7 @@
$group_name . '.delete', $group_name . '.delete',
$group_name . '.export', $group_name . '.export',
$group_name . '.authorize', $group_name . '.authorize',
$group_name . '.report', $group_name . '.report'
$group_name . '.restore'
]; ];
$i = 0; $i = 0;
@@ -211,8 +202,8 @@
public function destroy($id) public function destroy($id)
{ {
// Check if the authenticated user has the required permission to delete permissions // Check if the authenticated user has the required permission to delete permissions
if (is_null($this->user) || !$this->user->can('usermanagement.delete')) { if (is_null($this->user) || !$this->user->can('permissions.delete')) {
return response()->json(['message' => 'Sorry! You are not allowed to delete permissions.','success' => false]); //abort(403, 'Sorry! You are not allowed to delete permissions.');
} }
$permission = PermissionGroup::find($id); $permission = PermissionGroup::find($id);
@@ -223,7 +214,7 @@
} }
// Redirect back to the permissions index with a success message // 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) public function restore($id)
{ {
// Check if the authenticated user has the required permission to restore permissions // 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.'); abort(403, 'Sorry! You are not allowed to restore permissions.');
} }
@@ -266,8 +257,8 @@
*/ */
public function dataForDatatables(Request $request) public function dataForDatatables(Request $request)
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.read')) { if (is_null($this->user) || !$this->user->can('permissions.view')) {
return response()->json(['message' => 'Sorry! You are not allowed to view permissions.','success' => false]); //abort(403, 'Sorry! You are not allowed to view users.');
} }
// Retrieve data from the database // Retrieve data from the database
@@ -276,7 +267,9 @@
// Apply search filter if provided // Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) { if ($request->has('search') && !empty($request->get('search'))) {
$search = $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 // Apply sorting if provided
@@ -301,11 +294,14 @@
// Get the filtered count of records // Get the filtered count of records
$filteredRecords = $query->count(); $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); $permission->roles = $permission->roles($permission);
return $permission; return $permission;
}); });
@@ -323,17 +319,12 @@
'pageCount' => $pageCount, 'pageCount' => $pageCount,
'page' => $currentPage, 'page' => $currentPage,
'totalCount' => $totalRecords, 'totalCount' => $totalRecords,
'data' => $data, 'data' => $permissions,
]); ]);
} }
public function export() 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'); 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\Http\Requests\RoleRequest;
use Modules\Usermanagement\Models\Permission; use Modules\Usermanagement\Models\Permission;
use Modules\Usermanagement\Models\PermissionGroup; use Modules\Usermanagement\Models\PermissionGroup;
use Modules\Usermanagement\Models\Position;
use Modules\Usermanagement\Models\Role; use Modules\Usermanagement\Models\Role;
use Exception;
/** /**
* Class RolesController * Class RolesController
@@ -26,24 +24,20 @@
/** /**
* @var \Illuminate\Contracts\Auth\Authenticatable|null * @var \Illuminate\Contracts\Auth\Authenticatable|null
*/ */
protected $user; public $user;
/** /**
* UsersController constructor. * UsersController constructor.
* *
* Initializes the user property with the authenticated user. * Initializes the user property with the authenticated user.
*/ *
public function __construct() public function __construct()
{ {
// Mengatur middleware auth
$this->middleware('auth');
// Mengatur user setelah middleware auth dijalankan
$this->middleware(function ($request, $next) { $this->middleware(function ($request, $next) {
$this->user = Auth::user(); $this->user = Auth::guard('web')->user();
return $next($request); return $next($request);
}); });
} }*/
/** /**
* Display a listing of the resource. * Display a listing of the resource.
@@ -54,8 +48,8 @@
public function index() public function index()
{ {
// Check if the authenticated user has the required permission to view roles // Check if the authenticated user has the required permission to view roles
if (is_null($this->user) || !$this->user->can('usermanagement.read')) { if (is_null($this->user) || !$this->user->can('roles.view')) {
abort(403, 'Sorry! You are not allowed to view roles.'); //abort(403, 'Sorry! You are not allowed to view roles.');
} }
// Fetch all roles from the database // Fetch all roles from the database
@@ -76,14 +70,14 @@
public function store(RoleRequest $request) public function store(RoleRequest $request)
{ {
// Check if the authenticated user has the required permission to store roles // Check if the authenticated user has the required permission to store roles
if (is_null($this->user) || !$this->user->can('usermanagement.create')) { if (is_null($this->user) || !$this->user->can('roles.store')) {
abort(403, 'Sorry! You are not allowed to store roles.'); //abort(403, 'Sorry! You are not allowed to store roles.');
} }
$validated = $request->validated(); $validated = $request->validated();
if ($validated) { if($validated){
try { try{
// If no errors, save the role to the database // If no errors, save the role to the database
$role = Role::create($validated); $role = Role::create($validated);
@@ -91,22 +85,18 @@
$permissions = Permission::whereIn('id', $permissions)->pluck('name')->toArray(); $permissions = Permission::whereIn('id', $permissions)->pluck('name')->toArray();
if (!empty($permissions)) { if (!empty($permissions)) {
$role = Role::find($role->id); $role = Role::find($role->id);
try { try{
$role->syncPermissions($permissions); $role->syncPermissions($permissions);
} catch (Exception $e) { } catch (\Exception $e) {
return redirect() echo json_encode(['message' => $e->getMessage(), 'success']);exit;
->route('users.roles.index')
->with('error', 'Failed to sync permissions: ' . $e->getMessage());
} }
} }
// Redirect back to the roles index with a success message // Redirect back to the roles index with a success message
return redirect()->route('users.roles.index')->with('success', 'Role created successfully.'); 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 // Redirect back to the roles index with an error message
return redirect() return redirect()->route('users.roles.index')->with('error', 'Failed to create role. Please try again.');
->route('users.roles.index')
->with('error', 'Failed to create role. Please try again.');
} }
} }
} }
@@ -120,14 +110,37 @@
public function create() public function create()
{ {
// Check if the authenticated user has the required permission to create roles // Check if the authenticated user has the required permission to create roles
if (is_null($this->user) || !$this->user->can('usermanagement.create')) { if (is_null($this->user) || !$this->user->can('roles.create')) {
abort(403, 'Sorry! You are not allowed to create roles.'); //abort(403, 'Sorry! You are not allowed to create roles.');
} }
$permissiongroups = PermissionGroup::all(); $permissiongroups = PermissionGroup::all();
$positions = Position::all();
// Return the view for creating a new role // 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) public function edit($id)
{ {
// Check if the authenticated user has the required permission to edit roles // Check if the authenticated user has the required permission to edit roles
if (is_null($this->user) || !$this->user->can('usermanagement.update')) { if (is_null($this->user) || !$this->user->can('roles.edit')) {
abort(403, 'Sorry! You are not allowed to edit roles.'); //abort(403, 'Sorry! You are not allowed to edit roles.');
} }
// Fetch the specified role from the database // Fetch the specified role from the database
$role = Role::find($id); $role = Role::find($id);
$permissions = Permission::all(); $permissions = Permission::all();
$permissiongroups = PermissionGroup::all(); $permissiongroups = PermissionGroup::all();
$positions = Position::all();
// Return the view for editing the role // 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,13 +180,13 @@
public function update(RoleRequest $request, $id) public function update(RoleRequest $request, $id)
{ {
// Check if the authenticated user has the required permission to update roles // Check if the authenticated user has the required permission to update roles
if (is_null($this->user) || !$this->user->can('usermanagement.update')) { if (is_null($this->user) || !$this->user->can('roles.update')) {
abort(403, 'Sorry! You are not allowed to update roles.'); //abort(403, 'Sorry! You are not allowed to update roles.');
} }
$validated = $request->validated(); $validated = $request->validated();
if ($validated) { if($validated){
try { try{
// If no errors, update the role in the database // If no errors, update the role in the database
$role = Role::find($id); $role = Role::find($id);
$role->update($request->all()); $role->update($request->all());
@@ -183,23 +195,19 @@
$permissions = Permission::whereIn('id', $permissions)->pluck('name')->toArray(); $permissions = Permission::whereIn('id', $permissions)->pluck('name')->toArray();
if (!empty($permissions)) { if (!empty($permissions)) {
$role = Role::find($role->id); $role = Role::find($role->id);
try { try{
$role->syncPermissions($permissions); $role->syncPermissions($permissions);
} catch (Exception $e) { } catch (\Exception $e) {
return redirect() echo json_encode(['message' => $e->getMessage(), 'success']);exit;
->route('users.roles.index')
->with('error', 'Failed to sync permissions: ' . $e->getMessage());
} }
} }
// Redirect back to the roles index with a success message // Redirect back to the roles index with a success message
return redirect()->route('users.roles.index')->with('success', 'Role updated successfully.'); 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 // Redirect back to the roles index with an error message
return redirect() return redirect()->route('users.roles.index')->with('error', 'Failed to update role. Please try again.');
->route('users.roles.index')
->with('error', 'Failed to update role. Please try again.');
} }
} }
} }
@@ -212,23 +220,21 @@
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Illuminate\Auth\Access\AuthorizationException
*/ */
public function destroy($id) public function destroy($id)
{ {
// Check if the authenticated user has the required permission to delete currencies // Check if the authenticated user has the required permission to delete roles
if (is_null($this->user) || !$this->user->can('usermanagement.delete')) { if (is_null($this->user) || !$this->user->can('roles.delete')) {
return response()->json(['success' => false, 'message' => 'Sorry! You are not allowed to delete roles.'], 403); //abort(403, 'Sorry! You are not allowed to delete roles.');
} }
try { // Fetch the specified role from the database
// Delete from database $role = Role::find($id);
$currency = Role::find($id);
$currency->delete();
return response()->json(['success' => true, 'message' => 'Role deleted successfully.']); // Delete the role
} catch (Exception $e) { $role->delete();
return response()->json(['success' => false, 'message' => 'Failed to delete role.']);
} // 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) public function restore($id)
{ {
// Check if the authenticated user has the required permission to restore roles // 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.'); abort(403, 'Sorry! You are not allowed to restore roles.');
} }
@@ -266,25 +272,18 @@
*/ */
public function dataForDatatables(Request $request) public function dataForDatatables(Request $request)
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.read')) { if (is_null($this->user) || !$this->user->can('roles.view')) {
return response()->json(['message' => 'Sorry! You are not allowed to view roles.','success' => false]); //abort(403, 'Sorry! You are not allowed to view users.');
} }
// Retrieve data from the database // Retrieve data from the database
$query = Role::query(); $query = Role::query();
if(!$this->user->hasRole('administrator')){
$query->where('name', '!=', 'administrator');
}
// Apply search filter if provided // Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) { if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search'); $search = $request->get('search');
$query->where(function ($q) use ($search) { $query->where(function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%') $q->where('name', 'LIKE', "%$search%");
->orWhereHas('position', function ($query) use ($search) {
$query->whereAny(['name', 'level'], 'like','%'.$search.'%');
});
}); });
} }
@@ -292,29 +291,11 @@
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) { if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder'); $order = $request->get('sortOrder');
$column = $request->get('sortField'); $column = $request->get('sortField');
$query->orderBy($column, $order);
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 // Get the total count of records
$countQuery = clone $query; $totalRecords = $query->count();
// Get the total count of records (without joins to avoid duplicates)
$totalRecords = Role::count();
// Apply pagination if provided // Apply pagination if provided
if ($request->has('page') && $request->has('size')) { if ($request->has('page') && $request->has('size')) {
@@ -325,18 +306,17 @@
$query->skip($offset)->take($size); $query->skip($offset)->take($size);
} }
// Get the filtered count of records - use distinct to avoid duplicates from joins // Get the filtered count of records
$filteredRecords = $countQuery->distinct()->count('roles.id'); $filteredRecords = $query->count();
// Get the data for the current page // 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 // Calculate the page count
$pageSize = $request->get('size', 10); // Default to 10 if not provided $pageCount = ceil($totalRecords/$request->get('size'));
$pageCount = $pageSize > 0 ? ceil($totalRecords / $pageSize) : 0;
// Calculate the current page number // Calculate the current page number
$currentPage = $request->get('page') ?: 1; $currentPage = 0 + 1;
// Return the response data as a JSON object // Return the response data as a JSON object
return response()->json([ return response()->json([
@@ -346,16 +326,12 @@
'pageCount' => $pageCount, 'pageCount' => $pageCount,
'page' => $currentPage, 'page' => $currentPage,
'totalCount' => $totalRecords, 'totalCount' => $totalRecords,
'data' => $data, 'data' => $roles,
]); ]);
} }
public function export() 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'); return Excel::download(new RolesExport, 'roles.xlsx');
} }
} }

View File

@@ -14,7 +14,6 @@
use Modules\Usermanagement\Http\Requests\User as UserRequest; use Modules\Usermanagement\Http\Requests\User as UserRequest;
use Modules\Usermanagement\Models\Role; use Modules\Usermanagement\Models\Role;
use Modules\Usermanagement\Models\User; use Modules\Usermanagement\Models\User;
use Illuminate\Support\Facades\Storage;
/** /**
* Class UsersController * Class UsersController
@@ -25,27 +24,23 @@
*/ */
class UsersController extends Controller class UsersController extends Controller
{ {
/** /**
* @var \Illuminate\Contracts\Auth\Authenticatable|null * @var \Illuminate\Contracts\Auth\Authenticatable|null
*/ */
protected $user; public $user;
/** /**
* UsersController constructor. * UsersController constructor.
* *
* Initializes the user property with the authenticated user. * Initializes the user property with the authenticated user.
*/ */
public function __construct() // public function __construct()
{ // {
// Mengatur middleware auth // $this->middleware(function ($request, $next) {
$this->middleware('auth'); // $this->user = Auth::guard('web')->user();
// return $next($request);
// Mengatur user setelah middleware auth dijalankan // });
$this->middleware(function ($request, $next) { // }
$this->user = Auth::user();
return $next($request);
});
}
/** /**
* Display a listing of the resource. * Display a listing of the resource.
@@ -55,8 +50,8 @@
*/ */
public function index() public function index()
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.read')) { if (is_null($this->user) || !$this->user->can('users.view')) {
abort(403, 'Sorry! You are not allowed to view users.'); //abort(403, 'Sorry! You are not allowed to view users.');
} }
return view('usermanagement::users.index'); return view('usermanagement::users.index');
@@ -72,23 +67,20 @@
*/ */
public function dataForDatatables(Request $request) public function dataForDatatables(Request $request)
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.read')) { if (is_null($this->user) || !$this->user->can('users.view')) {
return response()->json(['message' => 'Sorry! You are not allowed to view users.','success' => false]); //abort(403, 'Sorry! You are not allowed to view users.');
} }
// Retrieve data from the database // Retrieve data from the database
$query = User::query(); $query = User::query();
if(!$this->user->hasRole('administrator')){
$query->whereHas('roles', function($q){
$q->where('name', '!=', 'administrator');
});
}
// Apply search filter if provided // Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) { if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search'); $search = $request->get('search');
$query->whereAny(['name', 'email'], 'like', '%'.$search.'%'); $query->where(function ($q) use ($search) {
$q
->where('name', 'LIKE', "%$search%")->orWhere('email', 'LIKE', "%$search%");
});
} }
// Apply sorting if provided // Apply sorting if provided
@@ -114,7 +106,7 @@
$filteredRecords = $query->count(); $filteredRecords = $query->count();
// Get the data for the current page // Get the data for the current page
$data = $query->with(['branch', 'roles'])->get(); $users = $query->with(['branch', 'roles'])->get();
// Calculate the page count // Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size')); $pageCount = ceil($totalRecords / $request->get('size'));
@@ -130,7 +122,7 @@
'pageCount' => $pageCount, 'pageCount' => $pageCount,
'page' => $currentPage, 'page' => $currentPage,
'totalCount' => $totalRecords, 'totalCount' => $totalRecords,
'data' => $data, 'data' => $users,
]); ]);
} }
@@ -144,15 +136,12 @@
*/ */
public function edit($id) public function edit($id)
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.update')) { if (is_null($this->user) || !$this->user->can('users.edit')) {
abort(403, 'Sorry! You are not allowed to edit users.'); //abort(403, 'Sorry! You are not allowed to edit users.');
} }
$user = User::find($id); $user = User::find($id);
$roles = Role::all(); $roles = Role::all();
if(!$this->user->hasRole('administrator')){
$roles = $roles->where('name', '!=', 'administrator');
}
$branches = Branch::all(); $branches = Branch::all();
return view('usermanagement::users.create', compact('user', 'roles', 'branches')); return view('usermanagement::users.create', compact('user', 'roles', 'branches'));
} }
@@ -167,14 +156,14 @@
*/ */
public function destroy($id) public function destroy($id)
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.delete')) { if (is_null($this->user) || !$this->user->can('users.delete')) {
return response()->json(['message' => 'Sorry! You are not allowed to delete users.','success' => false]); //abort(403, 'Sorry! You are not allowed to delete users.');
} }
$user = User::find($id); $user = User::find($id);
$user->delete(); $user->delete();
return response()->json(['message' => 'User deleted successfully.', 'success' => true]); echo json_encode(['message' => 'User deleted successfully.', 'success' => true]);
} }
/** /**
@@ -187,7 +176,7 @@
*/ */
public function restore($id) 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.'); abort(403, 'Sorry! You are not allowed to restore users.');
} }
@@ -210,10 +199,6 @@
*/ */
public function store(UserRequest $request) 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(); $validated = $request->validated();
if ($validated) { if ($validated) {
@@ -238,28 +223,18 @@
*/ */
public function create() public function create()
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.create')) { if (is_null($this->user) || !$this->user->can('users.create')) {
abort(403, 'Sorry! You are not allowed to create a user.'); //abort(403, 'Sorry! You are not allowed to create a user.');
} }
$roles = Role::all(); $roles = Role::all();
if(!$this->user->hasRole('administrator')){
$roles = $roles->where('name', '!=', 'administrator');
}
$branches = Branch::all(); $branches = Branch::all();
return view('usermanagement::users.create', compact('roles', 'branches')); 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')) { return Excel::download(new UsersExport, 'users.xlsx');
abort(403, 'Sorry! You are not allowed to export users.');
}
// Get search parameter from request
$search = $request->get('search');
return Excel::download(new UsersExport($search), 'users.xlsx');
} }
public function profile() public function profile()
@@ -275,6 +250,7 @@
$validatedData = $request->validate([ $validatedData = $request->validate([
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users,email,' . $user->id, 'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
'nik' => 'required|string|max:255|unique:users,nik,' . $user->id,
'sign' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', 'sign' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
]); ]);
@@ -285,17 +261,12 @@
if ($request->hasFile('sign')) { if ($request->hasFile('sign')) {
// Delete old e-sign if exists // Delete old e-sign if exists
if ($user->sign) { if ($user->sign) {
Storage::disk('public')->delete('signatures/' . $user->id . '/' . $user->sign); Storage::delete('public/signatures/' . $user->id . '/' . $user->sign);
} }
$sign = $request->file('sign'); $sign = $request->file('sign');
$signName = time() . '.' . $sign->getClientOriginalExtension(); $signName = time() . '.' . $sign->getClientOriginalExtension();
$sign->storeAs('public/signatures/' . $user->id, $signName);
// 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->sign = $signName;
} }
@@ -340,8 +311,8 @@
*/ */
public function update(UserRequest $request, $id) public function update(UserRequest $request, $id)
{ {
if (is_null($this->user) || !$this->user->can('usermanagement.update')) { if (is_null($this->user) || !$this->user->can('users.update')) {
abort(403, 'Sorry! You are not allowed to update users.'); //abort(403, 'Sorry! You are not allowed to update users.');
} }
$validated = $request->validated(); $validated = $request->validated();

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

View File

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

View File

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

View File

@@ -12,17 +12,6 @@
'slug' '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. * 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; 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. * Retrieve the activity log options for this role.
* *
@@ -33,11 +22,4 @@
return LogOptions::defaults()->logAll()->useLogName('User Management|Roles : '); 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

@@ -1,105 +1,84 @@
<?php <?php
namespace Modules\Usermanagement\Models; namespace Modules\Usermanagement\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable;
use Illuminate\Notifications\Notifiable; use Modules\Basicdata\Models\Branch;
use Modules\Basicdata\Models\Branch; use Spatie\Permission\Traits\HasRoles;
use Modules\Adk\Models\Appointment; use Wildside\Userstamps\Userstamps;
use Spatie\Permission\Traits\HasRoles;
use Mattiverse\Userstamps\Traits\Userstamps;
/**
* Class User
*
* This class extends the Laravel's Authenticatable class and represents a User in the application.
* It includes traits for using factories, notifications, API tokens, and UUIDs.
*
* @property string $name The name of the user.
* @property string $email The email of the user.
* @property string $password The hashed password of the user.
* @property string $remember_token The token used for "remember me" functionality.
*
* @package App\Models
*/
class User extends Authenticatable
{
use HasFactory, Notifiable, Userstamps, HasRoles, softDeletes;
protected $guard_name = ['web'];
/** /**
* The attributes that are mass assignable. * Class User
* *
* These are the attributes that can be set in bulk during a create or update operation. * This class extends the Laravel's Authenticatable class and represents a User in the application.
* It includes traits for using factories, notifications, API tokens, and UUIDs.
* *
* @var array<int, string> * @property string $name The name of the user.
* @property string $email The email of the user.
* @property string $password The hashed password of the user.
* @property string $remember_token The token used for "remember me" functionality.
*
* @package App\Models
*/ */
protected $fillable = [ class User extends Authenticatable
'name',
'email',
'password',
'nik',
'branch_id',
'profile_photo_path',
'last_login_at',
'last_login_ip',
'sign'
];
/**
* The attributes that should be hidden for serialization.
*
* These are the attributes that will be hidden when the model is converted to an array or JSON.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* This method defines how the attributes should be cast when accessed.
* In this case, 'email_verified_at' is cast to 'datetime', 'password' is cast to 'hashed', and 'id' is cast to 'string'.
*
* @return array<string, string>
*/
protected function casts(): array
{ {
return [ use Notifiable, Userstamps, HasRoles, softDeletes;
'email_verified_at' => 'datetime',
'password' => 'hashed', protected $guard_name = ['web'];
'id' => 'string',
/**
* The attributes that are mass assignable.
*
* These are the attributes that can be set in bulk during a create or update operation.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'nik',
'branch_id',
'profile_photo_path',
'last_login_at',
'last_login_ip',
'sign'
]; ];
/**
* The attributes that should be hidden for serialization.
*
* These are the attributes that will be hidden when the model is converted to an array or JSON.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* This method defines how the attributes should be cast when accessed.
* In this case, 'email_verified_at' is cast to 'datetime', 'password' is cast to 'hashed', and 'id' is cast to 'string'.
*
* @return array<string, string>
*/
protected function casts()
: array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'id' => 'string',
];
}
public function branch(){
return $this->belongsTo(Branch::class);
}
} }
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');
}
}

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

@@ -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

@@ -28,7 +28,13 @@
public function data() public function data()
{ {
return [ return [
['name' => 'usermanagement'] ['name' => 'usermanagement'],
['name' => 'basic-data'],
['name' => 'permohonan'],
['name' => 'admin'],
['name' => 'senior-officer'],
['name' => 'penilai'],
['name' => 'surveyor']
]; ];
} }
} }

View File

@@ -53,7 +53,7 @@
{ {
$actions = []; $actions = [];
// list of permission actions // list of permission actions
$crud = ['create', 'read', 'update', 'delete','export', 'authorize', 'report','restore']; $crud = ['create', 'read', 'update', 'delete','export', 'authorize', 'report'];
foreach ($crud as $value) { foreach ($crud as $value) {

View File

@@ -1,33 +1,33 @@
<?php <?php
namespace Modules\Usermanagement\Database\Seeders; namespace Modules\Usermanagement\Database\Seeders;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Role;
class RolesSeeder extends Seeder class RolesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{ {
/** $data = $this->data();
* Run the database seeds.
*
* @return void
*/
public function run()
{
$data = $this->data();
foreach ($data as $value) { foreach ($data as $value) {
Role::create([ Role::create([
'name' => $value['name'], 'name' => $value,
'guard_name' => 'web', 'guard_name' => 'web',
]); ]);
}
}
public function data()
{
return [
['name' => 'administrator']
];
} }
} }
public function data()
{
return [
'administrator'
];
}
}

View File

@@ -2,41 +2,67 @@
namespace Modules\Usermanagement\Database\Seeders; namespace Modules\Usermanagement\Database\Seeders;
use Faker\Generator;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Modules\Basicdata\Models\Branch;
use Modules\Usermanagement\Models\User; use Modules\Usermanagement\Models\User;
use Modules\Usermanagement\Database\Seeders\RolesSeeder; use Spatie\Permission\Models\Role;
class UsersSeeder extends Seeder class UsersSeeder extends Seeder
{ {
/** /**
* Run the database seeds. * Run the database seeds.
*
* @return void
*/ */
public function run(): void public function run(Generator $faker)
{ {
$roleSeeder = new RolesSeeder(); $roles = Role::all();
$rolesData = $roleSeeder->data();
foreach ($rolesData as $roleData) { foreach ($roles as $role) {
if ($roleData['name'] === 'administrator') { // Generate NIK secara acak dengan 6 digit angka
$user = User::firstOrCreate( $randomNik = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT);
['email' => $roleData['name'] . '@ag.co.id'],
[
'name' => $roleData['name'],
'password' => Hash::make('bagbag'),
'branch_id' => 1,
'nik' => '000000',
'email_verified_at' => now(),
]
);
$role = \Spatie\Permission\Models\Role::firstOrCreate( // Ubah nama dengan format khusus
['name' => $roleData['name']], $formattedName = $this->formatRoleName($role->name);
['guard_name' => 'web']
);
$user->assignRole($role); $email = $role->name . '@ag.co.id';
}
$user = User::create([
'name' => $formattedName,
'nik' => $randomNik,
'branch_id' => Branch::inRandomOrder()->first()->id,
'email' => $email,
'password' => Hash::make('bagbag'),
'email_verified_at' => now(),
]);
$user->assignRole($role);
} }
} }
/**
* Format nama role dengan aturan khusus
*/
private function formatRoleName($roleName)
{
// Pisahkan bagian sebelum dan sesudah "-"
$parts = explode('-', $roleName);
// Jika ada lebih dari satu bagian
if (count($parts) > 1) {
// Kapitalisasi bagian pertama
$parts[0] = (strlen($parts[0]) > 2) ? ucfirst(strtolower($parts[0])) : strtoupper($parts[0]);
// Untuk bagian kedua, kapitalisasi jika lebih dari 2 huruf
$parts[1] = (strlen($parts[1]) > 2) ? ucfirst(strtolower($parts[1])) : strtoupper($parts[1]);
// Gabungkan kembali
return implode(' ', $parts);
}
// Jika tidak ada "-", kapitalisasi biasa
return ucfirst($roleName);
}
} }

View File

@@ -1,73 +1,61 @@
{ {
"name": "Usermanagement", "name": "Usermanagement",
"alias": "usermanagement", "alias": "usermanagement",
"database": "", "database": "",
"description": "", "description": "",
"keywords": [], "keywords": [],
"priority": 0, "priority": 0,
"providers": [ "providers": [
"Modules\\Usermanagement\\Providers\\UsermanagementServiceProvider" "Modules\\Usermanagement\\Providers\\UsermanagementServiceProvider"
], ],
"files": [ "files": [],
"app/Helpers/RolePermission.php" "menu": {
], "main": [],
"menu": { "master": [],
"main": [], "system": [
"master": [], {
"system": [ "title": "User Management",
{ "path": "users",
"title": "User Management", "icon": "ki-filled ki-users text-lg text-primary",
"path": "users", "classes": "",
"icon": "ki-filled ki-users text-lg text-primary", "attributes": [],
"classes": "", "permission": "",
"attributes": [], "roles": [
"permission": "", "administrator"
"roles": [ ],
"administrator" "sub": [
], {
"sub": [ "title": "Users",
{ "path": "users",
"title": "Users", "classes": "",
"path": "users", "attributes": [],
"classes": "", "permission": "",
"attributes": [], "roles": [
"permission": "", "administrator"
"roles": [ ]
"administrator" },
] {
}, "title": "Roles",
{ "path": "users.roles",
"title": "Positions", "classes": "",
"path": "users.positions", "attributes": [],
"classes": "", "permission": "",
"attributes": [], "roles": [
"permission": "", "administrator"
"roles": [ ]
"administrator" },
] {
}, "title": "Permissions",
{ "path": "users.permissions",
"title": "Roles", "classes": "",
"path": "users.roles", "attributes": [],
"classes": "", "permission": "",
"attributes": [], "roles": [
"permission": "", "administrator"
"roles": [ ]
"administrator" }
]
},
{
"title": "Permissions",
"path": "users.permissions",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator"
]
}
]
}
] ]
} }
]
}
} }

View File

@@ -7,10 +7,8 @@
@section('content') @section('content')
<div class="container-fluid"> <div class="container-fluid">
<div class="grid"> <div class="grid">
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="5" <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') }}">
data-datatable-state-save="true" id="permissions-table" <div class="card-header py-5 flex-wrap">
data-api-url="{{ route('users.permissions.datatables') }}">
<div class="flex-wrap py-5 card-header">
<h3 class="card-title"> <h3 class="card-title">
List of Permissions List of Permissions
</h3> </h3>
@@ -21,45 +19,62 @@
</label> </label>
</div> </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> <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 class="btn btn-sm btn-light" href="{{ route('users.permissions.export') }}"> Export to Excel </a>
</a> <a class="btn btn-sm btn-primary" href="{{ route('users.permissions.create') }}"> Add Permission </a>
<a class="btn btn-sm btn-primary" href="{{ route('users.permissions.create') }}"> Add Permission
</a>
</div> </div>
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="scrollable-x-auto"> <div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border" <table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
data-datatable-table="true">
<thead> <thead>
<tr> <tr>
<th class="w-14"> <th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" /> <input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
</th> </th>
<th class="min-w-[250px]" data-datatable-column="name"> <th class="min-w-[250px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Permission </span> <span class="sort"> <span class="sort-label"> Permission </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[250px]" data-datatable-column="roles"> <th class="min-w-[250px]" data-datatable-column="roles">
<span class="sort"> <span class="sort-label"> Roles </span> <span class="sort"> <span class="sort-label"> Roles </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th> <th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
</tr> </tr>
</thead> </thead>
</table> </table>
</div> </div>
<div <div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
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 items-center gap-2">
<div class="flex gap-2 items-center">
Show Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per <select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per page
page
</div> </div>
<div class="flex gap-4 items-center"> <div class="flex items-center gap-4">
<span data-datatable-info="true"> </span> <span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true"> <div class="pagination" data-datatable-pagination="true">
</div> </div>
@@ -77,7 +92,7 @@
function deleteData(data) { function deleteData(data) {
Swal.fire({ Swal.fire({
title: 'Are you sure?', title: 'Are you sure?',
text: "You won't be able to revert this!", text: "You won't be able to revert this!" ,
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonColor: '#3085d6', confirmButtonColor: '#3085d6',
@@ -87,14 +102,14 @@
if (result.isConfirmed) { if (result.isConfirmed) {
$.ajaxSetup({ $.ajaxSetup({
headers: { headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}' 'X-CSRF-TOKEN': '{{ csrf_token() }}'
} }
}); });
$.ajax(`permissions/${data}`, { $.ajax(`permissions/${data}`, {
type: 'DELETE' type: 'DELETE'
}).then((response) => { }).then((response) => {
swal.fire('Deleted!', 'User has been deleted.', 'success').then(() => { swal.fire('Deleted!', 'User has been deleted.','success').then(() => {
window.location.reload(); window.location.reload();
}); });
}).catch((error) => { }).catch((error) => {
@@ -109,7 +124,7 @@
const element = document.querySelector('#permissions-table'); const element = document.querySelector('#permissions-table');
const searchInput = document.getElementById('search'); const searchInput = document.getElementById('search');
const apiUrl = element.getAttribute('data-api-url'); const apiUrl = element.getAttribute('data-api-url');
const colors = ['', 'badge-primary', 'badge-success', 'badge-info', 'badge-danger', 'badge-warning', 'badge-dark']; const colors = ['','badge-primary', 'badge-success', 'badge-info', 'badge-danger', 'badge-warning', 'badge-dark'];
const dataTableOptions = { const dataTableOptions = {
@@ -132,12 +147,12 @@
roles: { roles: {
title: 'Roles', title: 'Roles',
render: (item, data) => { render: (item, data) => {
const _render = data.roles.map((role) => { const _render = data.roles.map((role) =>{
const randomColor = colors[Math.floor(Math.random() * colors.length)]; const randomColor = colors[Math.floor(Math.random() * colors.length)];
return `<span class="badge ${randomColor} badge-sm">${role.name}</span>`; return `<span class="badge ${randomColor} badge-sm">${role.name}</span>`;
}); });
return _render.join(' '); return _render.join(' ');
} }
}, },
actions: { actions: {
@@ -158,10 +173,10 @@
let dataTable = new KTDataTable(element, dataTableOptions); let dataTable = new KTDataTable(element, dataTableOptions);
// Custom search functionality // Custom search functionality
searchInput.addEventListener('input', function() { searchInput.addEventListener('input', function () {
const searchValue = this.value.trim(); const searchValue = this.value.trim();
dataTable.search(searchValue, true); dataTable.search(searchValue, true);
dataTable.goPage(1);
}); });
</script> </script>
@endpush @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,97 +6,81 @@
@section('content') @section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto"> <div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<form action="{{ isset($role->id) ? route('users.roles.update', $role->id) : route('users.roles.store') }}" method="POST" id="role_form"> @if(isset($role->id))
@csrf <form action="{{ route('users.roles.update', $role->id) }}" method="POST" id="role_form">
@if(isset($role->id))
<input type="hidden" name="id" value="{{ $role->id }}"> <input type="hidden" name="id" value="{{ $role->id }}">
@method('PUT') @method('PUT')
@endif @else
<div class="card pb-2.5"> <form method="POST" action="{{ route('users.roles.store') }}">
<div class="card-header" id="basic_settings"> @endif
<h3 class="card-title"> @csrf
{{ isset($role->id) ? 'Edit' : 'Add' }} Role <div class="card pb-2.5">
</h3> <div class="card-header" id="basic_settings">
<div class="flex items-center gap-2"> <h3 class="card-title">
<a href="{{ route('users.roles.index') }}" class="btn btn-xs btn-info">Back</a> {{ isset($role->id) ? 'Edit' : 'Add' }} Role
</div> </h3>
</div> <div class="flex items-center gap-2">
<div class="card-body grid gap-5"> <a href="{{ route('users.roles.index') }}" class="btn btn-xs btn-info">Back</a>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> </div>
<label class="form-label max-w-56"> </div>
Name <div class="card-body grid gap-5">
</label> <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<div class="flex flex-wrap items-baseline w-full"> <label class="form-label max-w-56">
<input class="input @error('name') border-danger @enderror" type="text" name="name" value="{{ $role->name ?? '' }}"> Name
@error('name') </label>
<em class="alert text-danger text-sm">{{ $message }}</em> <div class="flex flex-wrap items-baseline w-full">
@enderror <input class="input @error('name') border-danger @enderror" type="text" name="name" value="{{ $role->name ?? '' }}">
</div> @error('name')
</div> <em class="alert text-danger text-sm">{{ $message }}</em>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> @enderror
<label class="form-label max-w-56"> </div>
Position </div>
</label> <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<div class="flex flex-wrap items-baseline w-full"> <label class="form-label max-w-56">
<select class="select tomselect @error('position_id') border-danger @enderror" name="position_id"> Administrator/Superuser Access
<option value="">Select Position</option> </label>
@foreach($positions as $position) <div class="flex flex-wrap items-baseline w-full">
<option value="{{ $position->id }}" {{ (isset($role) && $role->position_id == $position->id) ? 'selected' : '' }}> <label class="switch">
{{ $position->name }} | Tingkat Jabatan: {{ $position->level }} <input name="check" id="select_all" type="checkbox" value="1"/>
</option> <span class="switch-label">
@endforeach Select All
</select> </span>
@error('position_id') </label>
<em class="alert text-danger text-sm">{{ $message }}</em> </div>
@enderror </div>
</div> @foreach($permissiongroups as $group)
</div> <div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5"> <label class="form-label max-w-56">
<label class="form-label max-w-56"> {{ ucwords($group->name) }}
Administrator/Superuser Access </label>
</label> <div class="flex flex-wrap items-baseline w-full gap-2.5">
<div class="flex flex-wrap items-baseline w-full"> @foreach($group->getpermissionsByGroupId($group->id) as $permission)
<label class="switch"> <label class="switch">
<input name="check" id="select_all" type="checkbox" value="1"/> @if(isset($role))
<span class="switch-label"> <input type="checkbox" value="{{ $permission->id }}" name="permissions[]" {{ $role->hasPermissionTo($permission->name) ? 'checked' : null }} />
Select All @else
</span> <input type="checkbox" value="{{ $permission->id }}" name="permissions[]"/>
</label> @endif
</div> @php
</div> $permission_name = explode('.',$permission->name);
@foreach($permissiongroups as $group) @endphp
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
{{ ucwords($group->name) }}
</label>
<div class="flex flex-wrap items-baseline w-full gap-2.5">
@foreach($group->getpermissionsByGroupId($group->id) as $permission)
<label class="switch">
@if(isset($role))
<input type="checkbox" value="{{ $permission->id }}" name="permissions[]" {{ $role->hasPermissionTo($permission->name) ? 'checked' : null }} />
@else
<input type="checkbox" value="{{ $permission->id }}" name="permissions[]"/>
@endif
@php
$permission_name = explode('.',$permission->name);
@endphp
<span class="switch-label"> <span class="switch-label">
{{ ucwords($permission_name[1]) }} {{ ucwords($permission_name[1]) }}
</span> </span>
</label> </label>
@endforeach
</div>
</div>
@endforeach @endforeach
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</div> </div>
</div> </div>
@endforeach </form>
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</div>
</div>
</form>
</div> </div>
@endsection @endsection

View File

@@ -7,9 +7,8 @@
@section('content') @section('content')
<div class="container-fluid"> <div class="container-fluid">
<div class="grid"> <div class="grid">
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="5" <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') }}">
data-datatable-state-save="true" id="roles-table" data-api-url="{{ route('users.roles.datatables') }}"> <div class="card-header py-5 flex-wrap">
<div class="flex-wrap py-5 card-header">
<h3 class="card-title"> <h3 class="card-title">
List of Roles List of Roles
</h3> </h3>
@@ -20,7 +19,29 @@
</label> </label>
</div> </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> <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-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> <a class="btn btn-sm btn-primary" href="{{ route('users.roles.create') }}"> Add Role </a>
@@ -29,38 +50,27 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="scrollable-x-auto"> <div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border" <table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
data-datatable-table="true">
<thead> <thead>
<tr> <tr>
<th class="w-14"> <th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" /> <input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
</th> </th>
<th class="min-w-[250px]" data-datatable-column="name"> <th class="min-w-[250px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Role </span> <span class="sort"> <span class="sort-label"> Role </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[200px]" data-datatable-column="position_name"> <th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
<span class="sort"> <span class="sort-label"> Position </span> </tr>
<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> </thead>
</table> </table>
</div> </div>
<div <div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
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 items-center gap-2">
<div class="flex gap-2 items-center">
Show Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per <select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per page
page
</div> </div>
<div class="flex gap-4 items-center"> <div class="flex items-center gap-4">
<span data-datatable-info="true"> </span> <span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true"> <div class="pagination" data-datatable-pagination="true">
</div> </div>
@@ -78,7 +88,7 @@
function deleteData(data) { function deleteData(data) {
Swal.fire({ Swal.fire({
title: 'Are you sure?', title: 'Are you sure?',
text: "You won't be able to revert this!", text: "You won't be able to revert this!" ,
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonColor: '#3085d6', confirmButtonColor: '#3085d6',
@@ -88,14 +98,14 @@
if (result.isConfirmed) { if (result.isConfirmed) {
$.ajaxSetup({ $.ajaxSetup({
headers: { headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}' 'X-CSRF-TOKEN': '{{ csrf_token() }}'
} }
}); });
$.ajax(`roles/${data}`, { $.ajax(`roles/${data}`, {
type: 'DELETE' type: 'DELETE'
}).then((response) => { }).then((response) => {
swal.fire('Deleted!', 'User has been deleted.', 'success').then(() => { swal.fire('Deleted!', 'User has been deleted.','success').then(() => {
window.location.reload(); window.location.reload();
}); });
}).catch((error) => { }).catch((error) => {
@@ -128,20 +138,6 @@
name: { name: {
title: 'Role', 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: { actions: {
title: 'Status', title: 'Status',
render: (item, data) => { render: (item, data) => {
@@ -160,10 +156,10 @@
let dataTable = new KTDataTable(element, dataTableOptions); let dataTable = new KTDataTable(element, dataTableOptions);
// Custom search functionality // Custom search functionality
searchInput.addEventListener('input', function() { searchInput.addEventListener('input', function () {
const searchValue = this.value.trim(); const searchValue = this.value.trim();
dataTable.search(searchValue, true); dataTable.search(searchValue, true);
dataTable.goPage(1);
}); });
</script> </script>
@endpush @endpush

View File

@@ -7,9 +7,8 @@
@section('content') @section('content')
<div class="container-fluid"> <div class="container-fluid">
<div class="grid"> <div class="grid">
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="5" <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') }}">
data-datatable-state-save="true" id="users-table" data-api-url="{{ route('users.datatables') }}"> <div class="card-header py-5 flex-wrap">
<div class="flex-wrap py-5 card-header">
<h3 class="card-title"> <h3 class="card-title">
List of Users List of Users
</h3> </h3>
@@ -20,56 +19,74 @@
</label> </label>
</div> </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> <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 <a class="btn btn-sm btn-light" href="{{ route('users.export') }}"> Export to Excel </a>
Excel </a>
<a class="btn btn-sm btn-primary" href="{{ route('users.create') }}"> Add User </a> <a class="btn btn-sm btn-primary" href="{{ route('users.create') }}"> Add User </a>
</div> </div>
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="scrollable-x-auto"> <div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border" <table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
data-datatable-table="true">
<thead> <thead>
<tr> <tr>
<th class="w-14"> <th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" /> <input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
</th> </th>
<th class="min-w-[250px]" data-datatable-column="name"> <th class="min-w-[250px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Name </span> <span class="sort"> <span class="sort-label"> Name </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[185px]" data-datatable-column="email"> <th class="min-w-[185px]" data-datatable-column="email">
<span class="sort"> <span class="sort-label"> Email </span> <span class="sort"> <span class="sort-label"> Email </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[185px]" data-datatable-column="nik"> <th class="min-w-[185px]" data-datatable-column="nik">
<span class="sort"> <span class="sort-label"> NIK </span> <span class="sort"> <span class="sort-label"> NIK </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[185px]" data-datatable-column="branch"> <th class="min-w-[185px]" data-datatable-column="branch">
<span class="sort"> <span class="sort-label"> Branch </span> <span class="sort"> <span class="sort-label"> Branch </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[185px]" data-datatable-column="role"> <th class="min-w-[185px]" data-datatable-column="role">
<span class="sort"> <span class="sort-label"> Role </span> <span class="sort"> <span class="sort-label"> Role </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th> <th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
</tr> </tr>
</thead> </thead>
</table> </table>
</div> </div>
<div <div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
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 items-center gap-2">
<div class="flex gap-2 items-center">
Show Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per <select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per page
page
</div> </div>
<div class="flex gap-4 items-center"> <div class="flex items-center gap-4">
<span data-datatable-info="true"> </span> <span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true"> <div class="pagination" data-datatable-pagination="true">
</div> </div>
@@ -87,7 +104,7 @@
function deleteData(data) { function deleteData(data) {
Swal.fire({ Swal.fire({
title: 'Are you sure?', title: 'Are you sure?',
text: "You won't be able to revert this!", text: "You won't be able to revert this!" ,
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonColor: '#3085d6', confirmButtonColor: '#3085d6',
@@ -97,14 +114,14 @@
if (result.isConfirmed) { if (result.isConfirmed) {
$.ajaxSetup({ $.ajaxSetup({
headers: { headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}' 'X-CSRF-TOKEN': '{{ csrf_token() }}'
} }
}); });
$.ajax(`users/${data}`, { $.ajax(`users/${data}`, {
type: 'DELETE' type: 'DELETE'
}).then((response) => { }).then((response) => {
swal.fire('Deleted!', 'User has been deleted.', 'success').then(() => { swal.fire('Deleted!', 'User has been deleted.','success').then(() => {
window.location.reload(); window.location.reload();
}); });
}).catch((error) => { }).catch((error) => {
@@ -118,20 +135,6 @@
<script type="module"> <script type="module">
const element = document.querySelector('#users-table'); const element = document.querySelector('#users-table');
const searchInput = document.getElementById('search'); 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 apiUrl = element.getAttribute('data-api-url');
const dataTableOptions = { const dataTableOptions = {
@@ -160,13 +163,12 @@
branch: { branch: {
title: 'Branch', title: 'Branch',
render: (item, data) => { render: (item, data) => {
return data.branch?.name || '-'; return data.branch.name;
}, },
}, },
role: { role: {
title: 'Role', title: 'Role',
render: (item, data) => { render: (item, data) => {
console.log(data);
return data.roles.map(role => role.name).join(', '); return data.roles.map(role => role.name).join(', ');
}, },
}, },
@@ -188,11 +190,10 @@
let dataTable = new KTDataTable(element, dataTableOptions); let dataTable = new KTDataTable(element, dataTableOptions);
// Custom search functionality // Custom search functionality
searchInput.addEventListener('input', function() { searchInput.addEventListener('input', function () {
const searchValue = this.value.trim(); const searchValue = this.value.trim();
dataTable.search(searchValue, true); dataTable.search(searchValue, true);
updateExportUrl();
dataTable.goPage(1);
}); });
</script> </script>
@endpush @endpush

View File

@@ -53,18 +53,3 @@
$trail->parent('users.permissions'); $trail->parent('users.permissions');
$trail->push('Edit Permission'); $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 Illuminate\Support\Facades\Route;
use Modules\Usermanagement\Http\Controllers\PermissionsController; use Modules\Usermanagement\Http\Controllers\PermissionsController;
use Modules\Usermanagement\Http\Controllers\PositionsController;
use Modules\Usermanagement\Http\Controllers\RolesController; use Modules\Usermanagement\Http\Controllers\RolesController;
use Modules\Usermanagement\Http\Controllers\UsersController; use Modules\Usermanagement\Http\Controllers\UsersController;
@@ -44,11 +43,6 @@
Route::get('export', [PermissionsController ::class, 'export'])->name('export'); Route::get('export', [PermissionsController ::class, 'export'])->name('export');
}); });
Route::resource('permissions', PermissionsController::class); 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');
}
}