feat: implement role management system with CRUD interface and permission redefinitions
This commit is contained in:
parent
cffea55157
commit
691916e6de
@ -103,6 +103,11 @@ enum Permission: string
|
|||||||
|
|
||||||
case ACTIVITY_LOGS_VIEW = 'activity-logs.view';
|
case ACTIVITY_LOGS_VIEW = 'activity-logs.view';
|
||||||
|
|
||||||
|
case ROLES_VIEW = 'roles.view';
|
||||||
|
case ROLES_CREATE = 'roles.create';
|
||||||
|
case ROLES_UPDATE = 'roles.update';
|
||||||
|
case ROLES_DELETE = 'roles.delete';
|
||||||
|
|
||||||
public function label(): string
|
public function label(): string
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
@ -200,6 +205,11 @@ public function label(): string
|
|||||||
self::SETTINGS_UPDATE => 'Ubah Pengaturan Aplikasi',
|
self::SETTINGS_UPDATE => 'Ubah Pengaturan Aplikasi',
|
||||||
|
|
||||||
self::ACTIVITY_LOGS_VIEW => 'Lihat Log Aktivitas',
|
self::ACTIVITY_LOGS_VIEW => 'Lihat Log Aktivitas',
|
||||||
|
|
||||||
|
self::ROLES_VIEW => 'Lihat Role',
|
||||||
|
self::ROLES_CREATE => 'Tambah Role',
|
||||||
|
self::ROLES_UPDATE => 'Ubah Role',
|
||||||
|
self::ROLES_DELETE => 'Hapus Role',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,6 +251,8 @@ public function group(): string
|
|||||||
self::PAYROLL_CLOSE => 'Gaji',
|
self::PAYROLL_CLOSE => 'Gaji',
|
||||||
self::SETTINGS_VIEW, self::SETTINGS_UPDATE => 'Pengaturan Aplikasi',
|
self::SETTINGS_VIEW, self::SETTINGS_UPDATE => 'Pengaturan Aplikasi',
|
||||||
self::ACTIVITY_LOGS_VIEW => 'Log Aktivitas',
|
self::ACTIVITY_LOGS_VIEW => 'Log Aktivitas',
|
||||||
|
self::ROLES_VIEW, self::ROLES_CREATE, self::ROLES_UPDATE,
|
||||||
|
self::ROLES_DELETE => 'Role & Permission',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,113 +36,84 @@ public function permissions(): array
|
|||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::DEVELOPER, self::OWNER => Permission::cases(),
|
self::DEVELOPER, self::OWNER => Permission::cases(),
|
||||||
|
|
||||||
self::DIREKTUR => [
|
self::DIREKTUR => [
|
||||||
Permission::DASHBOARD_VIEW,
|
Permission::DASHBOARD_VIEW,
|
||||||
|
|
||||||
Permission::EMPLOYEES_VIEW,
|
Permission::EMPLOYEES_VIEW,
|
||||||
Permission::EMPLOYEES_CREATE,
|
|
||||||
Permission::EMPLOYEES_UPDATE,
|
|
||||||
Permission::EMPLOYEES_DELETE,
|
|
||||||
Permission::EMPLOYEES_RESET_PASSWORD,
|
|
||||||
Permission::EMPLOYEES_TOGGLE_STATUS,
|
|
||||||
Permission::ATTENDANCES_VIEW,
|
Permission::ATTENDANCES_VIEW,
|
||||||
Permission::ATTENDANCES_CREATE,
|
Permission::ATTENDANCES_CREATE,
|
||||||
Permission::ATTENDANCES_DELETE,
|
Permission::ATTENDANCES_DELETE,
|
||||||
Permission::ATTENDANCES_MANAGE,
|
Permission::ATTENDANCES_MANAGE,
|
||||||
|
|
||||||
Permission::LEAVE_REQUESTS_VIEW,
|
Permission::LEAVE_REQUESTS_VIEW,
|
||||||
Permission::LEAVE_REQUESTS_CREATE,
|
Permission::LEAVE_REQUESTS_CREATE,
|
||||||
Permission::LEAVE_REQUESTS_UPDATE,
|
Permission::LEAVE_REQUESTS_UPDATE,
|
||||||
Permission::LEAVE_REQUESTS_DELETE,
|
Permission::LEAVE_REQUESTS_DELETE,
|
||||||
|
|
||||||
Permission::CATEGORIES_VIEW,
|
Permission::CATEGORIES_VIEW,
|
||||||
Permission::CATEGORIES_CREATE,
|
|
||||||
Permission::CATEGORIES_UPDATE,
|
|
||||||
Permission::CATEGORIES_DELETE,
|
|
||||||
Permission::SUPPLIERS_VIEW,
|
Permission::SUPPLIERS_VIEW,
|
||||||
Permission::SUPPLIERS_CREATE,
|
|
||||||
Permission::SUPPLIERS_UPDATE,
|
|
||||||
Permission::SUPPLIERS_DELETE,
|
|
||||||
Permission::CUSTOMERS_VIEW,
|
Permission::CUSTOMERS_VIEW,
|
||||||
Permission::CUSTOMERS_CREATE,
|
|
||||||
Permission::CUSTOMERS_UPDATE,
|
|
||||||
Permission::CUSTOMERS_DELETE,
|
|
||||||
Permission::PRODUCTS_VIEW,
|
Permission::PRODUCTS_VIEW,
|
||||||
Permission::PRODUCTS_CREATE,
|
|
||||||
Permission::PRODUCTS_UPDATE,
|
|
||||||
Permission::PRODUCTS_DELETE,
|
|
||||||
Permission::PRODUCTS_TOGGLE_STATUS,
|
|
||||||
Permission::RAW_MATERIALS_VIEW,
|
Permission::RAW_MATERIALS_VIEW,
|
||||||
Permission::RAW_MATERIALS_CREATE,
|
|
||||||
Permission::RAW_MATERIALS_UPDATE,
|
|
||||||
Permission::RAW_MATERIALS_DELETE,
|
|
||||||
Permission::RAW_MATERIALS_TOGGLE_STATUS,
|
|
||||||
Permission::PURCHASES_VIEW,
|
Permission::PURCHASES_VIEW,
|
||||||
Permission::PURCHASES_CREATE,
|
|
||||||
Permission::PURCHASES_UPDATE,
|
|
||||||
Permission::PURCHASES_DELETE,
|
|
||||||
Permission::ORDERS_VIEW,
|
Permission::ORDERS_VIEW,
|
||||||
Permission::ORDERS_CREATE,
|
|
||||||
Permission::ORDERS_UPDATE,
|
|
||||||
Permission::ORDERS_DELETE,
|
|
||||||
Permission::ORDERS_SEND,
|
|
||||||
Permission::ORDERS_COMPLETE,
|
|
||||||
Permission::ORDERS_CANCEL,
|
|
||||||
Permission::CUTTINGS_VIEW,
|
Permission::CUTTINGS_VIEW,
|
||||||
Permission::CUTTINGS_CREATE,
|
|
||||||
Permission::CUTTINGS_UPDATE,
|
|
||||||
Permission::CUTTINGS_DELETE,
|
|
||||||
Permission::CUTTINGS_COMPLETE,
|
|
||||||
Permission::CUTTINGS_VERIFY,
|
|
||||||
Permission::CUTTINGS_REJECT,
|
|
||||||
Permission::CASH_VIEW,
|
Permission::CASH_VIEW,
|
||||||
Permission::CASH_DEPOSIT,
|
|
||||||
Permission::CASH_UPDATE,
|
|
||||||
Permission::CASH_DELETE,
|
|
||||||
Permission::EXPENSES_VIEW,
|
Permission::EXPENSES_VIEW,
|
||||||
Permission::EXPENSES_CREATE,
|
|
||||||
Permission::EXPENSES_UPDATE,
|
|
||||||
Permission::EXPENSES_DELETE,
|
|
||||||
Permission::EMPLOYEE_ADVANCES_VIEW,
|
Permission::EMPLOYEE_ADVANCES_VIEW,
|
||||||
Permission::EMPLOYEE_ADVANCES_CREATE,
|
Permission::EMPLOYEE_ADVANCES_CREATE,
|
||||||
Permission::EMPLOYEE_ADVANCES_UPDATE,
|
Permission::EMPLOYEE_ADVANCES_UPDATE,
|
||||||
Permission::EMPLOYEE_ADVANCES_DELETE,
|
Permission::EMPLOYEE_ADVANCES_DELETE,
|
||||||
Permission::EMPLOYEE_ADVANCES_PAY,
|
Permission::EMPLOYEE_ADVANCES_PAY,
|
||||||
|
|
||||||
Permission::PAYROLL_VIEW,
|
Permission::PAYROLL_VIEW,
|
||||||
Permission::PAYROLL_PAY,
|
|
||||||
Permission::PAYROLL_ADJUST,
|
|
||||||
Permission::PAYROLL_CLOSE,
|
|
||||||
Permission::ACTIVITY_LOGS_VIEW,
|
|
||||||
],
|
],
|
||||||
|
|
||||||
self::ADMIN_TOKO => [
|
self::ADMIN_TOKO => [
|
||||||
Permission::DASHBOARD_VIEW,
|
Permission::DASHBOARD_VIEW,
|
||||||
Permission::EMPLOYEES_VIEW,
|
|
||||||
Permission::EMPLOYEES_CREATE,
|
|
||||||
Permission::EMPLOYEES_UPDATE,
|
|
||||||
Permission::EMPLOYEES_RESET_PASSWORD,
|
|
||||||
Permission::EMPLOYEES_TOGGLE_STATUS,
|
|
||||||
Permission::ATTENDANCES_VIEW,
|
Permission::ATTENDANCES_VIEW,
|
||||||
Permission::ATTENDANCES_CREATE,
|
Permission::ATTENDANCES_CREATE,
|
||||||
Permission::ATTENDANCES_DELETE,
|
Permission::ATTENDANCES_DELETE,
|
||||||
Permission::ATTENDANCES_MANAGE,
|
|
||||||
Permission::LEAVE_REQUESTS_VIEW,
|
Permission::LEAVE_REQUESTS_VIEW,
|
||||||
Permission::LEAVE_REQUESTS_CREATE,
|
Permission::LEAVE_REQUESTS_CREATE,
|
||||||
Permission::LEAVE_REQUESTS_UPDATE,
|
Permission::LEAVE_REQUESTS_UPDATE,
|
||||||
Permission::LEAVE_REQUESTS_DELETE,
|
Permission::LEAVE_REQUESTS_DELETE,
|
||||||
|
|
||||||
Permission::CATEGORIES_VIEW,
|
Permission::CATEGORIES_VIEW,
|
||||||
Permission::CATEGORIES_CREATE,
|
Permission::CATEGORIES_CREATE,
|
||||||
Permission::CATEGORIES_UPDATE,
|
Permission::CATEGORIES_UPDATE,
|
||||||
Permission::CATEGORIES_DELETE,
|
Permission::CATEGORIES_DELETE,
|
||||||
|
|
||||||
Permission::SUPPLIERS_VIEW,
|
Permission::SUPPLIERS_VIEW,
|
||||||
Permission::SUPPLIERS_CREATE,
|
Permission::SUPPLIERS_CREATE,
|
||||||
Permission::SUPPLIERS_UPDATE,
|
Permission::SUPPLIERS_UPDATE,
|
||||||
Permission::SUPPLIERS_DELETE,
|
Permission::SUPPLIERS_DELETE,
|
||||||
|
|
||||||
Permission::CUSTOMERS_VIEW,
|
Permission::CUSTOMERS_VIEW,
|
||||||
Permission::CUSTOMERS_CREATE,
|
Permission::CUSTOMERS_CREATE,
|
||||||
Permission::CUSTOMERS_UPDATE,
|
Permission::CUSTOMERS_UPDATE,
|
||||||
Permission::CUSTOMERS_DELETE,
|
Permission::CUSTOMERS_DELETE,
|
||||||
|
|
||||||
Permission::PRODUCTS_VIEW,
|
Permission::PRODUCTS_VIEW,
|
||||||
Permission::PRODUCTS_CREATE,
|
Permission::PRODUCTS_CREATE,
|
||||||
Permission::PRODUCTS_UPDATE,
|
Permission::PRODUCTS_UPDATE,
|
||||||
Permission::PRODUCTS_DELETE,
|
Permission::PRODUCTS_DELETE,
|
||||||
Permission::PRODUCTS_TOGGLE_STATUS,
|
Permission::PRODUCTS_TOGGLE_STATUS,
|
||||||
|
|
||||||
Permission::ORDERS_VIEW,
|
Permission::ORDERS_VIEW,
|
||||||
Permission::ORDERS_CREATE,
|
Permission::ORDERS_CREATE,
|
||||||
Permission::ORDERS_UPDATE,
|
Permission::ORDERS_UPDATE,
|
||||||
@ -150,84 +121,128 @@ public function permissions(): array
|
|||||||
Permission::ORDERS_SEND,
|
Permission::ORDERS_SEND,
|
||||||
Permission::ORDERS_COMPLETE,
|
Permission::ORDERS_COMPLETE,
|
||||||
Permission::ORDERS_CANCEL,
|
Permission::ORDERS_CANCEL,
|
||||||
|
|
||||||
Permission::CUTTINGS_VIEW,
|
Permission::CUTTINGS_VIEW,
|
||||||
Permission::CUTTINGS_VERIFY,
|
Permission::CUTTINGS_VERIFY,
|
||||||
Permission::CUTTINGS_REJECT,
|
|
||||||
Permission::CASH_VIEW,
|
Permission::CASH_VIEW,
|
||||||
Permission::CASH_DEPOSIT,
|
Permission::CASH_DEPOSIT,
|
||||||
Permission::CASH_UPDATE,
|
Permission::CASH_UPDATE,
|
||||||
Permission::CASH_DELETE,
|
Permission::CASH_DELETE,
|
||||||
|
|
||||||
Permission::EXPENSES_VIEW,
|
Permission::EXPENSES_VIEW,
|
||||||
Permission::EXPENSES_CREATE,
|
Permission::EXPENSES_CREATE,
|
||||||
Permission::EXPENSES_UPDATE,
|
Permission::EXPENSES_UPDATE,
|
||||||
Permission::EXPENSES_DELETE,
|
Permission::EXPENSES_DELETE,
|
||||||
|
|
||||||
Permission::EMPLOYEE_ADVANCES_VIEW,
|
Permission::EMPLOYEE_ADVANCES_VIEW,
|
||||||
Permission::EMPLOYEE_ADVANCES_CREATE,
|
Permission::EMPLOYEE_ADVANCES_CREATE,
|
||||||
Permission::EMPLOYEE_ADVANCES_UPDATE,
|
Permission::EMPLOYEE_ADVANCES_UPDATE,
|
||||||
Permission::EMPLOYEE_ADVANCES_DELETE,
|
Permission::EMPLOYEE_ADVANCES_DELETE,
|
||||||
Permission::EMPLOYEE_ADVANCES_PAY,
|
Permission::EMPLOYEE_ADVANCES_PAY,
|
||||||
|
|
||||||
Permission::PAYROLL_VIEW,
|
Permission::PAYROLL_VIEW,
|
||||||
Permission::PAYROLL_PAY,
|
Permission::PAYROLL_PAY,
|
||||||
Permission::PAYROLL_ADJUST,
|
Permission::PAYROLL_ADJUST,
|
||||||
Permission::PAYROLL_CLOSE,
|
Permission::PAYROLL_CLOSE,
|
||||||
Permission::SETTINGS_VIEW,
|
|
||||||
Permission::SETTINGS_UPDATE,
|
|
||||||
Permission::ACTIVITY_LOGS_VIEW,
|
|
||||||
],
|
],
|
||||||
|
|
||||||
self::ADMIN_BAHAN_BAKU => [
|
self::ADMIN_BAHAN_BAKU => [
|
||||||
Permission::DASHBOARD_VIEW,
|
Permission::DASHBOARD_VIEW,
|
||||||
Permission::EMPLOYEES_VIEW,
|
|
||||||
Permission::ATTENDANCES_VIEW,
|
|
||||||
Permission::ATTENDANCES_CREATE,
|
|
||||||
Permission::LEAVE_REQUESTS_VIEW,
|
|
||||||
Permission::LEAVE_REQUESTS_CREATE,
|
|
||||||
Permission::LEAVE_REQUESTS_UPDATE,
|
|
||||||
Permission::LEAVE_REQUESTS_DELETE,
|
|
||||||
Permission::CATEGORIES_VIEW,
|
|
||||||
Permission::CATEGORIES_CREATE,
|
|
||||||
Permission::CATEGORIES_UPDATE,
|
|
||||||
Permission::CATEGORIES_DELETE,
|
|
||||||
Permission::SUPPLIERS_VIEW,
|
|
||||||
Permission::SUPPLIERS_CREATE,
|
|
||||||
Permission::SUPPLIERS_UPDATE,
|
|
||||||
Permission::SUPPLIERS_DELETE,
|
|
||||||
Permission::RAW_MATERIALS_VIEW,
|
|
||||||
Permission::RAW_MATERIALS_CREATE,
|
|
||||||
Permission::RAW_MATERIALS_UPDATE,
|
|
||||||
Permission::RAW_MATERIALS_DELETE,
|
|
||||||
Permission::RAW_MATERIALS_TOGGLE_STATUS,
|
|
||||||
Permission::PURCHASES_VIEW,
|
|
||||||
Permission::PURCHASES_CREATE,
|
|
||||||
Permission::PURCHASES_UPDATE,
|
|
||||||
Permission::PURCHASES_DELETE,
|
|
||||||
Permission::CUTTINGS_VIEW,
|
|
||||||
Permission::CUTTINGS_CREATE,
|
|
||||||
Permission::CUTTINGS_UPDATE,
|
|
||||||
Permission::CUTTINGS_DELETE,
|
|
||||||
Permission::CUTTINGS_COMPLETE,
|
|
||||||
],
|
|
||||||
self::MARKETING => [
|
|
||||||
Permission::DASHBOARD_VIEW,
|
|
||||||
Permission::EMPLOYEES_VIEW,
|
|
||||||
Permission::ATTENDANCES_VIEW,
|
Permission::ATTENDANCES_VIEW,
|
||||||
Permission::ATTENDANCES_CREATE,
|
Permission::ATTENDANCES_CREATE,
|
||||||
|
Permission::ATTENDANCES_DELETE,
|
||||||
|
|
||||||
Permission::LEAVE_REQUESTS_VIEW,
|
Permission::LEAVE_REQUESTS_VIEW,
|
||||||
Permission::LEAVE_REQUESTS_CREATE,
|
Permission::LEAVE_REQUESTS_CREATE,
|
||||||
Permission::LEAVE_REQUESTS_UPDATE,
|
Permission::LEAVE_REQUESTS_UPDATE,
|
||||||
Permission::LEAVE_REQUESTS_DELETE,
|
Permission::LEAVE_REQUESTS_DELETE,
|
||||||
|
|
||||||
Permission::CATEGORIES_VIEW,
|
Permission::CATEGORIES_VIEW,
|
||||||
Permission::CATEGORIES_CREATE,
|
Permission::CATEGORIES_CREATE,
|
||||||
Permission::CATEGORIES_UPDATE,
|
Permission::CATEGORIES_UPDATE,
|
||||||
Permission::CATEGORIES_DELETE,
|
Permission::CATEGORIES_DELETE,
|
||||||
|
|
||||||
Permission::SUPPLIERS_VIEW,
|
Permission::SUPPLIERS_VIEW,
|
||||||
Permission::SUPPLIERS_CREATE,
|
Permission::SUPPLIERS_CREATE,
|
||||||
Permission::SUPPLIERS_UPDATE,
|
Permission::SUPPLIERS_UPDATE,
|
||||||
Permission::SUPPLIERS_DELETE,
|
Permission::SUPPLIERS_DELETE,
|
||||||
|
|
||||||
Permission::CUSTOMERS_VIEW,
|
Permission::CUSTOMERS_VIEW,
|
||||||
Permission::CUSTOMERS_CREATE,
|
Permission::CUSTOMERS_CREATE,
|
||||||
Permission::CUSTOMERS_UPDATE,
|
Permission::CUSTOMERS_UPDATE,
|
||||||
Permission::CUSTOMERS_DELETE,
|
Permission::CUSTOMERS_DELETE,
|
||||||
|
|
||||||
Permission::PRODUCTS_VIEW,
|
Permission::PRODUCTS_VIEW,
|
||||||
|
Permission::PRODUCTS_CREATE,
|
||||||
|
Permission::PRODUCTS_UPDATE,
|
||||||
|
Permission::PRODUCTS_DELETE,
|
||||||
|
Permission::PRODUCTS_TOGGLE_STATUS,
|
||||||
|
|
||||||
|
Permission::ORDERS_VIEW,
|
||||||
|
Permission::ORDERS_CREATE,
|
||||||
|
Permission::ORDERS_UPDATE,
|
||||||
|
Permission::ORDERS_DELETE,
|
||||||
|
Permission::ORDERS_SEND,
|
||||||
|
Permission::ORDERS_COMPLETE,
|
||||||
|
Permission::ORDERS_CANCEL,
|
||||||
|
|
||||||
|
Permission::CUTTINGS_VIEW,
|
||||||
|
Permission::CUTTINGS_VERIFY,
|
||||||
|
|
||||||
|
Permission::CASH_VIEW,
|
||||||
|
Permission::CASH_DEPOSIT,
|
||||||
|
Permission::CASH_UPDATE,
|
||||||
|
Permission::CASH_DELETE,
|
||||||
|
|
||||||
|
Permission::EXPENSES_VIEW,
|
||||||
|
Permission::EXPENSES_CREATE,
|
||||||
|
Permission::EXPENSES_UPDATE,
|
||||||
|
Permission::EXPENSES_DELETE,
|
||||||
|
|
||||||
|
Permission::EMPLOYEE_ADVANCES_VIEW,
|
||||||
|
Permission::EMPLOYEE_ADVANCES_CREATE,
|
||||||
|
Permission::EMPLOYEE_ADVANCES_UPDATE,
|
||||||
|
Permission::EMPLOYEE_ADVANCES_DELETE,
|
||||||
|
Permission::EMPLOYEE_ADVANCES_PAY,
|
||||||
|
|
||||||
|
Permission::PAYROLL_VIEW,
|
||||||
|
Permission::PAYROLL_PAY,
|
||||||
|
Permission::PAYROLL_ADJUST,
|
||||||
|
Permission::PAYROLL_CLOSE,
|
||||||
|
],
|
||||||
|
|
||||||
|
self::MARKETING => [
|
||||||
|
Permission::DASHBOARD_VIEW,
|
||||||
|
|
||||||
|
Permission::EMPLOYEES_VIEW,
|
||||||
|
|
||||||
|
Permission::ATTENDANCES_VIEW,
|
||||||
|
Permission::ATTENDANCES_CREATE,
|
||||||
|
|
||||||
|
Permission::LEAVE_REQUESTS_VIEW,
|
||||||
|
Permission::LEAVE_REQUESTS_CREATE,
|
||||||
|
Permission::LEAVE_REQUESTS_UPDATE,
|
||||||
|
Permission::LEAVE_REQUESTS_DELETE,
|
||||||
|
|
||||||
|
Permission::CATEGORIES_VIEW,
|
||||||
|
Permission::CATEGORIES_CREATE,
|
||||||
|
Permission::CATEGORIES_UPDATE,
|
||||||
|
Permission::CATEGORIES_DELETE,
|
||||||
|
|
||||||
|
Permission::SUPPLIERS_VIEW,
|
||||||
|
Permission::SUPPLIERS_CREATE,
|
||||||
|
Permission::SUPPLIERS_UPDATE,
|
||||||
|
Permission::SUPPLIERS_DELETE,
|
||||||
|
|
||||||
|
Permission::CUSTOMERS_VIEW,
|
||||||
|
Permission::CUSTOMERS_CREATE,
|
||||||
|
Permission::CUSTOMERS_UPDATE,
|
||||||
|
Permission::CUSTOMERS_DELETE,
|
||||||
|
|
||||||
|
Permission::PRODUCTS_VIEW,
|
||||||
|
|
||||||
Permission::ORDERS_VIEW,
|
Permission::ORDERS_VIEW,
|
||||||
Permission::ORDERS_CREATE,
|
Permission::ORDERS_CREATE,
|
||||||
Permission::ORDERS_UPDATE,
|
Permission::ORDERS_UPDATE,
|
||||||
@ -235,6 +250,7 @@ public function permissions(): array
|
|||||||
Permission::ORDERS_COMPLETE,
|
Permission::ORDERS_COMPLETE,
|
||||||
Permission::ORDERS_CANCEL,
|
Permission::ORDERS_CANCEL,
|
||||||
],
|
],
|
||||||
|
|
||||||
self::NON_OPERATOR => [
|
self::NON_OPERATOR => [
|
||||||
Permission::DASHBOARD_VIEW,
|
Permission::DASHBOARD_VIEW,
|
||||||
],
|
],
|
||||||
|
|||||||
109
app/Http/Controllers/Admin/System/RoleController.php
Normal file
109
app/Http/Controllers/Admin/System/RoleController.php
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin\System;
|
||||||
|
|
||||||
|
use App\Enums\Permission as PermissionEnum;
|
||||||
|
use App\Enums\Role as EnumsRole;
|
||||||
|
use App\Http\Controllers\Concerns\FlashesEntityMessage;
|
||||||
|
use App\Http\Controllers\Concerns\ParsesDataTableQuery;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Admin\System\RoleRequest;
|
||||||
|
use App\Services\System\RoleService;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Inertia\Response;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class RoleController extends Controller
|
||||||
|
{
|
||||||
|
use FlashesEntityMessage;
|
||||||
|
use ParsesDataTableQuery;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly RoleService $roleService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function index(Request $request): Response
|
||||||
|
{
|
||||||
|
$tableQuery = $this->parseDataTableQuery($request);
|
||||||
|
|
||||||
|
return Inertia::render('admin/system/roles/Index', [
|
||||||
|
'roles' => $this->roleService->paginateForIndex($tableQuery),
|
||||||
|
'filters' => $this->dataTableFilters($tableQuery),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(): Response
|
||||||
|
{
|
||||||
|
$permissions = collect(PermissionEnum::cases())
|
||||||
|
->map(fn (PermissionEnum $permission) => [
|
||||||
|
'value' => $permission->value,
|
||||||
|
'label' => $permission->label(),
|
||||||
|
'group' => $permission->group(),
|
||||||
|
])
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
return Inertia::render('admin/system/roles/Create', [
|
||||||
|
'permissions' => $permissions,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(RoleRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$this->roleService->create($request->validated());
|
||||||
|
|
||||||
|
$this->flashCreated('Role');
|
||||||
|
|
||||||
|
return redirect()->route('admin.system.roles.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Role $role): Response
|
||||||
|
{
|
||||||
|
$role->load('permissions');
|
||||||
|
|
||||||
|
$permissions = collect(PermissionEnum::cases())
|
||||||
|
->map(fn (PermissionEnum $permission) => [
|
||||||
|
'value' => $permission->value,
|
||||||
|
'label' => $permission->label(),
|
||||||
|
'group' => $permission->group(),
|
||||||
|
])
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Check if role is developer or owner to protect it
|
||||||
|
$isProtected = in_array($role->name, [EnumsRole::DEVELOPER->value, EnumsRole::OWNER->value], true);
|
||||||
|
|
||||||
|
return Inertia::render('admin/system/roles/Edit', [
|
||||||
|
'role' => [
|
||||||
|
'id' => $role->id,
|
||||||
|
'name' => $role->name,
|
||||||
|
'permission_names' => $role->permissions->pluck('name')->all(),
|
||||||
|
],
|
||||||
|
'permissions' => $permissions,
|
||||||
|
'isProtected' => $isProtected,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(RoleRequest $request, Role $role): RedirectResponse
|
||||||
|
{
|
||||||
|
$this->roleService->update($role, $request->validated());
|
||||||
|
|
||||||
|
$this->flashUpdated('Role');
|
||||||
|
|
||||||
|
return redirect()->route('admin.system.roles.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Role $role): RedirectResponse
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->roleService->delete($role);
|
||||||
|
$this->flashDeleted('Role');
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->flashError($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.system.roles.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
60
app/Http/Requests/Admin/System/RoleRequest.php
Normal file
60
app/Http/Requests/Admin/System/RoleRequest.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Admin\System;
|
||||||
|
|
||||||
|
use App\Enums\Permission;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class RoleRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
$permission = $this->isMethod('POST')
|
||||||
|
? Permission::ROLES_CREATE
|
||||||
|
: Permission::ROLES_UPDATE;
|
||||||
|
|
||||||
|
return $this->user()?->can($permission->value) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepareForValidation(): void
|
||||||
|
{
|
||||||
|
if ($this->has('name')) {
|
||||||
|
$this->merge([
|
||||||
|
'name' => Str::slug($this->input('name')),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
$roleId = $this->route('role')?->id;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
'max:50',
|
||||||
|
'regex:/^[a-z0-9\-]+$/',
|
||||||
|
Rule::unique('roles', 'name')->ignore($roleId),
|
||||||
|
],
|
||||||
|
'permissions' => ['nullable', 'array'],
|
||||||
|
'permissions.*' => ['string', Rule::exists('permissions', 'name')],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
public function attributes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'nama',
|
||||||
|
'permissions' => 'hak akses',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -93,7 +93,13 @@ public function roleLabel(): Attribute
|
|||||||
get: function () {
|
get: function () {
|
||||||
$role = $this->roles->first();
|
$role = $this->roles->first();
|
||||||
|
|
||||||
return $role ? Role::from($role->name)->label() : null;
|
if (! $role) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$enum = Role::tryFrom($role->name);
|
||||||
|
|
||||||
|
return $enum ? $enum->label() : ucwords(str_replace('-', ' ', $role->name));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
87
app/Services/System/RoleService.php
Normal file
87
app/Services/System/RoleService.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\System;
|
||||||
|
|
||||||
|
use App\Enums\Role as EnumsRole;
|
||||||
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class RoleService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
|
||||||
|
*/
|
||||||
|
public function paginateForIndex(array $tableQuery): LengthAwarePaginator
|
||||||
|
{
|
||||||
|
$query = Role::query()
|
||||||
|
->withCount('permissions')
|
||||||
|
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
||||||
|
$search = $tableQuery['search'];
|
||||||
|
$query->where(function (Builder $query) use ($search): void {
|
||||||
|
$query->where('name', 'like', "%{$search}%");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);
|
||||||
|
|
||||||
|
return $query
|
||||||
|
->paginate(10)
|
||||||
|
->withQueryString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $validated
|
||||||
|
*/
|
||||||
|
public function create(array $validated): void
|
||||||
|
{
|
||||||
|
DB::transaction(function () use ($validated): void {
|
||||||
|
$role = Role::create([
|
||||||
|
'name' => Str::slug($validated['name']),
|
||||||
|
'guard_name' => 'web',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! empty($validated['permissions'])) {
|
||||||
|
$role->syncPermissions($validated['permissions']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $validated
|
||||||
|
*/
|
||||||
|
public function update(Role $role, array $validated): void
|
||||||
|
{
|
||||||
|
DB::transaction(function () use ($role, $validated): void {
|
||||||
|
// Protect developer and owner names from changing
|
||||||
|
if (! in_array($role->name, [EnumsRole::DEVELOPER->value, EnumsRole::OWNER->value], true)) {
|
||||||
|
$role->name = Str::slug($validated['name']);
|
||||||
|
}
|
||||||
|
$role->save();
|
||||||
|
|
||||||
|
$role->syncPermissions($validated['permissions'] ?? []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(Role $role): void
|
||||||
|
{
|
||||||
|
if (in_array($role->name, [EnumsRole::DEVELOPER->value, EnumsRole::OWNER->value], true)) {
|
||||||
|
throw new \InvalidArgumentException('Role developer atau owner tidak dapat dihapus.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$role->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applySorting(Builder $query, string $sort, string $direction): void
|
||||||
|
{
|
||||||
|
if (in_array($sort, ['name'], true)) {
|
||||||
|
$query->orderBy($sort, $direction);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->latest();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,30 +33,6 @@ public function run(): void
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$legacyRoleMap = [
|
|
||||||
'super-admin' => RoleEnum::DEVELOPER->value,
|
|
||||||
'admin' => RoleEnum::DIREKTUR->value,
|
|
||||||
'hr' => RoleEnum::ADMIN_TOKO->value,
|
|
||||||
'user' => RoleEnum::NON_OPERATOR->value,
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($legacyRoleMap as $oldRole => $newRole) {
|
|
||||||
$oldRoleModel = Role::query()->where('name', $oldRole)->where('guard_name', 'web')->first();
|
|
||||||
|
|
||||||
if (! $oldRoleModel) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$newRoleModel = Role::findByName($newRole, 'web');
|
|
||||||
|
|
||||||
foreach ($oldRoleModel->users as $user) {
|
|
||||||
$user->removeRole($oldRole);
|
|
||||||
$user->assignRole($newRoleModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
$oldRoleModel->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
Role::query()
|
Role::query()
|
||||||
->where('guard_name', 'web')
|
->where('guard_name', 'web')
|
||||||
->whereNotIn('name', RoleEnum::values())
|
->whereNotIn('name', RoleEnum::values())
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Link, usePage } from '@inertiajs/vue3';
|
import { Link, usePage } from '@inertiajs/vue3';
|
||||||
import { Banknote, CalendarDays, Clock, FolderTree, History, Layers, LayoutDashboard, Package, Receipt, Scissors, Settings2, ShoppingBag, ShoppingCart, User, UserCheck, Users, Wallet, WalletCards } from '@lucide/vue';
|
import { Banknote, CalendarDays, Clock, FolderTree, History, Layers, LayoutDashboard, Package, Receipt, Scissors, Settings2, Shield, ShoppingBag, ShoppingCart, User, UserCheck, Users, Wallet, WalletCards } from '@lucide/vue';
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
SidebarContent,
|
SidebarContent,
|
||||||
@ -75,6 +75,7 @@ const menuGroups: MenuGroup[] = [
|
|||||||
{
|
{
|
||||||
label: 'Sistem',
|
label: 'Sistem',
|
||||||
items: [
|
items: [
|
||||||
|
{ title: 'Role & Permission', href: '/admin/system/roles', icon: Shield, permission: 'roles.view' },
|
||||||
{ title: 'Pengaturan', href: '/admin/system/setting', icon: Settings2, permission: 'settings.view' },
|
{ title: 'Pengaturan', href: '/admin/system/setting', icon: Settings2, permission: 'settings.view' },
|
||||||
{ title: 'Log Aktivitas', href: '/admin/system/activity-logs', icon: History, permission: 'activity-logs.view' },
|
{ title: 'Log Aktivitas', href: '/admin/system/activity-logs', icon: History, permission: 'activity-logs.view' },
|
||||||
],
|
],
|
||||||
|
|||||||
227
resources/js/components/admin/system/roles/RoleForm.vue
Normal file
227
resources/js/components/admin/system/roles/RoleForm.vue
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import {
|
||||||
|
Field,
|
||||||
|
FieldError,
|
||||||
|
FieldGroup,
|
||||||
|
FieldLabel,
|
||||||
|
FieldSet,
|
||||||
|
} from '@/components/ui/field';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { formErrors } from '@/lib/form';
|
||||||
|
import { useForm } from '@inertiajs/vue3';
|
||||||
|
import { Save } from '@lucide/vue';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { toast } from 'vue-sonner';
|
||||||
|
|
||||||
|
interface PermissionOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
group: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
permissions: PermissionOption[];
|
||||||
|
initialData?: {
|
||||||
|
name?: string;
|
||||||
|
permission_names?: string[];
|
||||||
|
};
|
||||||
|
submitUrl: string;
|
||||||
|
method?: 'post' | 'put';
|
||||||
|
submitLabel?: string;
|
||||||
|
isProtected?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
method: 'post',
|
||||||
|
submitLabel: 'Simpan',
|
||||||
|
isProtected: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
name: props.initialData?.name ?? '',
|
||||||
|
permissions: props.initialData?.permission_names ?? [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group permissions by group/module
|
||||||
|
const groupedPermissions = computed(() => {
|
||||||
|
const groups: Record<string, PermissionOption[]> = {};
|
||||||
|
props.permissions.forEach((perm) => {
|
||||||
|
if (!groups[perm.group]) {
|
||||||
|
groups[perm.group] = [];
|
||||||
|
}
|
||||||
|
groups[perm.group].push(perm);
|
||||||
|
});
|
||||||
|
return groups;
|
||||||
|
});
|
||||||
|
|
||||||
|
function togglePermission(permValue: string, checked: boolean) {
|
||||||
|
if (checked) {
|
||||||
|
if (!form.permissions.includes(permValue)) {
|
||||||
|
form.permissions = [...form.permissions, permValue];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
form.permissions = form.permissions.filter((p) => p !== permValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPermissionChecked(permValue: string): boolean {
|
||||||
|
return form.permissions.includes(permValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check/Uncheck all for a specific module group
|
||||||
|
function isGroupAllChecked(groupName: string): boolean {
|
||||||
|
const groupPerms = groupedPermissions.value[groupName] || [];
|
||||||
|
if (groupPerms.length === 0) return false;
|
||||||
|
return groupPerms.every((perm) => form.permissions.includes(perm.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleGroupAll(groupName: string, checked: boolean) {
|
||||||
|
const groupPerms = groupedPermissions.value[groupName] || [];
|
||||||
|
const groupValues = groupPerms.map((p) => p.value);
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
// Add all group permissions that are not already in form.permissions
|
||||||
|
const toAdd = groupValues.filter((v) => !form.permissions.includes(v));
|
||||||
|
form.permissions = [...form.permissions, ...toAdd];
|
||||||
|
} else {
|
||||||
|
// Remove all group permissions
|
||||||
|
form.permissions = form.permissions.filter(
|
||||||
|
(p) => !groupValues.includes(p),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit() {
|
||||||
|
form[props.method](props.submitUrl, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success(
|
||||||
|
`Role berhasil ${props.method === 'post' ? 'dibuat' : 'diperbarui'}.`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast.error('Gagal menyimpan data. Periksa kembali formulir.');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="grid gap-6">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Informasi Role</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<FieldGroup>
|
||||||
|
<FieldSet class="grid gap-4">
|
||||||
|
<Field>
|
||||||
|
<FieldLabel for="name" required
|
||||||
|
>Nama</FieldLabel
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
v-model="form.name"
|
||||||
|
type="text"
|
||||||
|
placeholder="Contoh: admin-gudang"
|
||||||
|
:disabled="isProtected"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
v-if="isProtected"
|
||||||
|
class="mt-1 text-xs text-muted-foreground"
|
||||||
|
>
|
||||||
|
Nama bawaan sistem (developer/owner) tidak
|
||||||
|
dapat diubah.
|
||||||
|
</span>
|
||||||
|
<FieldError
|
||||||
|
:errors="formErrors(form, 'name')"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</FieldSet>
|
||||||
|
</FieldGroup>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Hak Akses (Permissions)</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div
|
||||||
|
v-for="(perms, groupName) in groupedPermissions"
|
||||||
|
:key="groupName"
|
||||||
|
class="border-b pb-6 last:border-b-0 last:pb-0"
|
||||||
|
>
|
||||||
|
<div class="mb-3 flex items-center justify-between">
|
||||||
|
<h3
|
||||||
|
class="text-base font-semibold text-foreground"
|
||||||
|
>
|
||||||
|
{{ groupName }}
|
||||||
|
</h3>
|
||||||
|
<label
|
||||||
|
class="flex cursor-pointer items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="size-3.5 cursor-pointer rounded border-input"
|
||||||
|
:checked="isGroupAllChecked(groupName)"
|
||||||
|
@change="
|
||||||
|
toggleGroupAll(
|
||||||
|
groupName,
|
||||||
|
(
|
||||||
|
$event.target as HTMLInputElement
|
||||||
|
).checked,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<span>Pilih Semua</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="grid gap-3 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
v-for="perm in perms"
|
||||||
|
:key="perm.value"
|
||||||
|
class="flex cursor-pointer items-center gap-2 rounded-md border px-3 py-2 transition-colors hover:bg-muted/50"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="size-4 cursor-pointer rounded border-input"
|
||||||
|
:checked="
|
||||||
|
isPermissionChecked(perm.value)
|
||||||
|
"
|
||||||
|
@change="
|
||||||
|
togglePermission(
|
||||||
|
perm.value,
|
||||||
|
(
|
||||||
|
$event.target as HTMLInputElement
|
||||||
|
).checked,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<span class="text-sm">{{
|
||||||
|
perm.label
|
||||||
|
}}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FieldError :errors="formErrors(form, 'permissions')" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end">
|
||||||
|
<Button type="submit" :disabled="form.processing">
|
||||||
|
<Save class="size-4" />
|
||||||
|
{{ form.processing ? 'Menyimpan...' : submitLabel }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
62
resources/js/components/admin/system/roles/columns.ts
Normal file
62
resources/js/components/admin/system/roles/columns.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import DataTableActions from '@/components/admin/system/roles/data-table-actions.vue';
|
||||||
|
import { DataTableColumnHeader } from '@/components/data-table';
|
||||||
|
import type { ColumnDef } from '@tanstack/vue-table';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
export interface RoleListItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
permissions_count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createColumns(): ColumnDef<RoleListItem>[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessorKey: 'name',
|
||||||
|
enableSorting: true,
|
||||||
|
header: () =>
|
||||||
|
h(DataTableColumnHeader, { title: 'Nama', column: 'name' }),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const name = row.original.name;
|
||||||
|
const formattedName = name
|
||||||
|
.split('-')
|
||||||
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
return h('div', { class: 'flex flex-col' }, [
|
||||||
|
h(
|
||||||
|
'span',
|
||||||
|
{ class: 'font-medium text-foreground' },
|
||||||
|
formattedName,
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'span',
|
||||||
|
{ class: 'text-xs text-muted-foreground font-mono' },
|
||||||
|
name,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'permissions_count',
|
||||||
|
enableSorting: false,
|
||||||
|
header: 'Jumlah Hak Akses',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return h(
|
||||||
|
'span',
|
||||||
|
{ class: 'text-sm' },
|
||||||
|
`${row.original.permissions_count} hak akses`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) =>
|
||||||
|
h(DataTableActions, {
|
||||||
|
role: row.original,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Link, router } from '@inertiajs/vue3';
|
||||||
|
import { Pencil, Trash2 } from '@lucide/vue';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { toast } from 'vue-sonner';
|
||||||
|
import ConfirmDialog from '@/components/ConfirmDialog.vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
|
import { useCan } from '@/composables/useCan';
|
||||||
|
import type { RoleListItem } from './columns';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
role: RoleListItem;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { can } = useCan();
|
||||||
|
|
||||||
|
const deleteConfirmOpen = ref(false);
|
||||||
|
const deleteProcessing = ref(false);
|
||||||
|
|
||||||
|
const isSystemRole = computed(() => {
|
||||||
|
return ['developer', 'owner'].includes(props.role.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
function destroyRole() {
|
||||||
|
if (isSystemRole.value) {
|
||||||
|
toast.error('Role sistem tidak dapat dihapus.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteProcessing.value = true;
|
||||||
|
|
||||||
|
router.delete(`/admin/system/roles/${props.role.id}`, {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => {
|
||||||
|
deleteConfirmOpen.value = false;
|
||||||
|
toast.success('Role berhasil dihapus.');
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast.error('Gagal menghapus role.');
|
||||||
|
},
|
||||||
|
onFinish: () => {
|
||||||
|
deleteProcessing.value = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center justify-end gap-1">
|
||||||
|
<Tooltip v-if="can('roles.update')">
|
||||||
|
<TooltipTrigger as-child>
|
||||||
|
<Button variant="ghost" size="icon" class="size-8" as-child>
|
||||||
|
<Link :href="`/admin/system/roles/${role.id}/edit`">
|
||||||
|
<Pencil class="size-4" />
|
||||||
|
<span class="sr-only">Ubah</span>
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>Ubah</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip v-if="can('roles.delete')">
|
||||||
|
<TooltipTrigger as-child>
|
||||||
|
<span>
|
||||||
|
<Button variant="ghost" size="icon"
|
||||||
|
class="text-destructive hover:text-destructive size-8"
|
||||||
|
:disabled="isSystemRole"
|
||||||
|
@click="deleteConfirmOpen = true">
|
||||||
|
<Trash2 class="size-4" />
|
||||||
|
<span class="sr-only">Hapus</span>
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
{{ isSystemRole ? 'Role sistem tidak dapat dihapus' : 'Hapus' }}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ConfirmDialog v-if="can('roles.delete') && !isSystemRole" v-model:open="deleteConfirmOpen" title="Hapus role?"
|
||||||
|
:description="`Role ${role.name} akan dihapus secara permanen. Tindakan ini tidak dapat dibatalkan.`"
|
||||||
|
confirm-label="Hapus" cancel-label="Batal" destructive :loading="deleteProcessing" @confirm="destroyRole" />
|
||||||
|
</template>
|
||||||
40
resources/js/pages/admin/system/roles/Create.vue
Normal file
40
resources/js/pages/admin/system/roles/Create.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Head, Link } from '@inertiajs/vue3';
|
||||||
|
import { ArrowLeft } from '@lucide/vue';
|
||||||
|
import RoleForm from '@/components/admin/system/roles/RoleForm.vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
|
||||||
|
interface PermissionOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
group: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
permissions: PermissionOption[];
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Tambah Role" />
|
||||||
|
|
||||||
|
<AdminLayout>
|
||||||
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
|
Tambah Role
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
||||||
|
<Link href="/admin/system/roles">
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
Kembali
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RoleForm submit-url="/admin/system/roles" method="post" submit-label="Simpan" :permissions="permissions" />
|
||||||
|
</AdminLayout>
|
||||||
|
</template>
|
||||||
49
resources/js/pages/admin/system/roles/Edit.vue
Normal file
49
resources/js/pages/admin/system/roles/Edit.vue
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Head, Link } from '@inertiajs/vue3';
|
||||||
|
import { ArrowLeft } from '@lucide/vue';
|
||||||
|
import RoleForm from '@/components/admin/system/roles/RoleForm.vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
|
||||||
|
interface PermissionOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
group: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RoleData {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
permission_names: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
role: RoleData;
|
||||||
|
permissions: PermissionOption[];
|
||||||
|
isProtected: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Ubah Role" />
|
||||||
|
|
||||||
|
<AdminLayout>
|
||||||
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
|
Ubah Role
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
||||||
|
<Link href="/admin/system/roles">
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
Kembali
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RoleForm :submit-url="`/admin/system/roles/${role.id}`" method="put" submit-label="Perbarui"
|
||||||
|
:initial-data="role" :permissions="permissions" :is-protected="isProtected" />
|
||||||
|
</AdminLayout>
|
||||||
|
</template>
|
||||||
102
resources/js/pages/admin/system/roles/Index.vue
Normal file
102
resources/js/pages/admin/system/roles/Index.vue
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Head, Link } from '@inertiajs/vue3';
|
||||||
|
import { Plus } from '@lucide/vue';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { createColumns } from '@/components/admin/system/roles/columns';
|
||||||
|
import { DataTable } from '@/components/data-table';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
import { useCan } from '@/composables/useCan';
|
||||||
|
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
||||||
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
import type { DataTableSort } from '@/types/data-table';
|
||||||
|
import type { RoleListItem } from '@/components/admin/system/roles/columns';
|
||||||
|
|
||||||
|
interface PaginatedRoles {
|
||||||
|
current_page: number;
|
||||||
|
per_page: number;
|
||||||
|
last_page: number;
|
||||||
|
total: number;
|
||||||
|
data: RoleListItem[];
|
||||||
|
links: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
roles: PaginatedRoles;
|
||||||
|
filters: {
|
||||||
|
search: string;
|
||||||
|
sort?: string;
|
||||||
|
direction?: 'asc' | 'desc';
|
||||||
|
};
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { can } = useCan();
|
||||||
|
const search = ref(props.filters.search ?? '');
|
||||||
|
|
||||||
|
const { query, setSearch, setSort, resetFilters, syncFromServer } = useDataTableQuery({
|
||||||
|
url: '/admin/system/roles',
|
||||||
|
initial: { ...props.filters },
|
||||||
|
});
|
||||||
|
|
||||||
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
|
const columns = computed(() => createColumns());
|
||||||
|
|
||||||
|
const currentSort = computed<DataTableSort | null>(() => {
|
||||||
|
if (!query.value.sort || !query.value.direction) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
column: query.value.sort,
|
||||||
|
direction: query.value.direction,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const pagination = computed(() => ({
|
||||||
|
currentPage: props.roles.current_page,
|
||||||
|
perPage: props.roles.per_page,
|
||||||
|
lastPage: props.roles.last_page,
|
||||||
|
total: props.roles.total,
|
||||||
|
}));
|
||||||
|
|
||||||
|
watch(search, (value) => {
|
||||||
|
setSearch(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.filters.search,
|
||||||
|
(value) => {
|
||||||
|
search.value = value ?? '';
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Role & Permission" />
|
||||||
|
|
||||||
|
<AdminLayout>
|
||||||
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
|
Role & Permission
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button v-if="can('roles.create')" class="shrink-0 self-start sm:self-center" as-child>
|
||||||
|
<Link href="/admin/system/roles/create">
|
||||||
|
<Plus class="size-4" />
|
||||||
|
Tambah
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Card class="min-w-0">
|
||||||
|
<CardContent class="min-w-0">
|
||||||
|
<DataTable v-model:search="search" :columns="columns" :data="roles.data" :pagination="pagination"
|
||||||
|
:pagination-links="roles.links" :sort="currentSort" @sort-change="setSort"
|
||||||
|
@filters-reset="resetFilters" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</AdminLayout>
|
||||||
|
</template>
|
||||||
@ -24,6 +24,7 @@
|
|||||||
use App\Http\Controllers\Admin\Master\RawMaterialController;
|
use App\Http\Controllers\Admin\Master\RawMaterialController;
|
||||||
use App\Http\Controllers\Admin\Master\SupplierController;
|
use App\Http\Controllers\Admin\Master\SupplierController;
|
||||||
use App\Http\Controllers\Admin\System\ActivityLogController;
|
use App\Http\Controllers\Admin\System\ActivityLogController;
|
||||||
|
use App\Http\Controllers\Admin\System\RoleController;
|
||||||
use App\Http\Controllers\Admin\System\SettingController;
|
use App\Http\Controllers\Admin\System\SettingController;
|
||||||
use App\Http\Controllers\Auth\LoginController;
|
use App\Http\Controllers\Auth\LoginController;
|
||||||
use App\Http\Controllers\Auth\LogoutController;
|
use App\Http\Controllers\Auth\LogoutController;
|
||||||
@ -273,6 +274,27 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
Route::prefix('system')->name('system.')->group(function () {
|
Route::prefix('system')->name('system.')->group(function () {
|
||||||
|
Route::prefix('roles')->name('roles.')
|
||||||
|
->middleware('permission:'.Permission::ROLES_VIEW->value)
|
||||||
|
->group(function () {
|
||||||
|
Route::get('/', [RoleController::class, 'index'])->name('index');
|
||||||
|
Route::get('create', [RoleController::class, 'create'])
|
||||||
|
->middleware('permission:'.Permission::ROLES_CREATE->value)
|
||||||
|
->name('create');
|
||||||
|
Route::post('/', [RoleController::class, 'store'])
|
||||||
|
->middleware('permission:'.Permission::ROLES_CREATE->value)
|
||||||
|
->name('store');
|
||||||
|
Route::get('{role}/edit', [RoleController::class, 'edit'])
|
||||||
|
->middleware('permission:'.Permission::ROLES_UPDATE->value)
|
||||||
|
->name('edit');
|
||||||
|
Route::put('{role}', [RoleController::class, 'update'])
|
||||||
|
->middleware('permission:'.Permission::ROLES_UPDATE->value)
|
||||||
|
->name('update');
|
||||||
|
Route::delete('{role}', [RoleController::class, 'destroy'])
|
||||||
|
->middleware('permission:'.Permission::ROLES_DELETE->value)
|
||||||
|
->name('destroy');
|
||||||
|
});
|
||||||
|
|
||||||
Route::prefix('activity-logs')->name('activity-logs.')
|
Route::prefix('activity-logs')->name('activity-logs.')
|
||||||
->middleware('permission:'.Permission::ACTIVITY_LOGS_VIEW->value)
|
->middleware('permission:'.Permission::ACTIVITY_LOGS_VIEW->value)
|
||||||
->group(function () {
|
->group(function () {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user