Add loading Permission

pull/2/head
sundayenglish 4 weeks ago
parent eb88911dfc
commit 8ca9f346d9
  1. 137
      app/Components/Role/Manager.php
  2. 13
      app/Http/Controllers/RoleController.php
  3. 7
      resources/views/admin/roles/manager.blade.php
  4. 175
      resources/views/components/role/manager.blade.php
  5. 15
      routes/web.php

@ -0,0 +1,137 @@
<?php
namespace App\Components\Role;
use Livewire\Component;
use Livewire\Attributes\On;
use Livewire\WithPagination;
use Spatie\Permission\Models\Role;
class Manager extends Component
{
use WithPagination;
/**
* Các tùy chọn số bản ghi mỗi trang.
*
* @var int[]
*/
public array $perPageOptions = [5, 10, 25, 50, 100];
/**
* Số bản ghi mỗi trang (mặc định).
*/
public int $perPage = 10;
/**
* Giao diện phân trang (bootstrap hoặc tailwind).
*/
protected string $paginationTheme = 'bootstrap';
/**
* Chế độ UI: 'index' (danh sách) hoặc 'form' (tạo/sửa).
*/
public string $mode = 'index';
/**
* ID của role đang sửa (null khi tạo mới).
*/
public ?int $editingId = null;
/**
* Chuỗi tìm kiếm hiện tại.
*/
public string $search = '';
/**
* Tên role dùng trong form.
*/
public string $name = '';
/**
* Khi thay đổi số bản ghi mỗi trang, reset về trang 1.
*/
public function updatedPerPage(int $value): void
{
$this->resetPage();
}
#[On('roleSaved')]
public function showIndex(): void
{
$this->mode = 'index';
$this->resetPage();
$this->name = '';
$this->editingId = null;
}
public function updatedSearch(string $value): void
{
$this->resetPage();
}
public function showForm(?int $id = null): void
{
$this->mode = 'form';
$this->editingId = $id;
$this->name = $id
? Role::findOrFail($id)->name
: '';
}
public function delete(int $id): void
{
Role::findOrFail($id)->delete();
session()->flash('message', 'Role deleted.');
$this->resetPage();
}
public function save(): void
{
$uniqueRule = 'unique:roles,name'
. ($this->editingId ? ',' . $this->editingId : '');
$this->validate([
'name' => "required|string|{$uniqueRule}",
]);
Role::updateOrCreate(
['id' => $this->editingId],
['name' => $this->name]
);
session()->flash('message', 'Role saved.');
$this->dispatch('roleSaved');
}
protected function computeTitle(): string
{
if ($this->mode === 'index') {
return 'Roles';
}
if ($this->mode === 'form' && $this->editingId) {
return "Edit Role: {$this->name}";
}
return 'Create Role';
}
public function getTitleProperty(): string
{
return $this->computeTitle();
}
public function render()
{
$roles = Role::query()
->when($this->search, fn($q) => $q->where('name', 'like', "%{$this->search}%"))
->orderByDesc('id')
->paginate($this->perPage);
return view('components.role.manager', [
'roles' => $roles,
'title' => $this->title,
]);
}
}

@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Spatie\Permission\Models\Role;
class RoleController extends Controller
{
public function manager()
{
return view('admin.roles.manager');
}
}

@ -0,0 +1,7 @@
@extends('layouts.app')
@section('title', 'Permissions')
@section('content')
<livewire:role.manager />
@endsection

@ -0,0 +1,175 @@
{{-- resources/views/livewire/role-manager.blade.php --}}
<div class="row">
<div class="col-12">
<div class="card mb-4">
{{-- Header --}}
<div class="card-header d-flex justify-content-between align-items-center pb-0">
<h6 class="mb-0">{{ $title }}</h6>
@if ($mode === 'index')
<div class="d-flex align-items-center">
{{-- 1. Dropdown chọn số bản ghi --}}
<div class="me-2">
<select
wire:model="perPage"
wire:change="resetPage"
class="form-select form-select-sm"
style="width: 100px; display:inline-block;"
>
@foreach ($perPageOptions as $opt)
<option value="{{ $opt }}">{{ $opt }}</option>
@endforeach
</select>
</div>
{{-- 2. Search box --}}
<div class="input-group input-group-outline me-2">
<input
type="text"
wire:model.debounce.500ms="search"
wire:keydown.enter="resetPage"
class="form-control"
placeholder="Search..."
>
</div>
<button
wire:click="resetPage"
class="btn btn-sm btn-outline-success me-2 m-0"
>
<i class="fas fa-search me-1" style="font-size:1.2rem;"></i>
</button>
{{-- 3. Create button --}}
<button
wire:click="showForm"
class="btn btn-sm btn-info m-0"
>
Create
</button>
</div>
@else
<button
wire:click="showIndex"
class="btn btn-sm btn-secondary"
>
← Back to list
</button>
@endif
</div>
{{-- Body --}}
<div class="card-body p-3">
@if (session()->has('message'))
<div class="alert alert-success">{{ session('message') }}</div>
@endif
@if ($mode === 'index')
{{-- SKELETON TABLE --}}
<x-skeleton-table :columns="3" :rows="5" height="3.5rem" />
{{-- ACTUAL TABLE --}}
<div wire:loading.remove class="table-responsive w-100">
<table class="table align-items-center mb-0 w-100">
<thead>
<tr style="height:3.5rem">
<th class="text-center">ID</th>
<th>Name</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
@forelse($roles as $r)
<tr style="height:3.5rem">
<td class="text-center align-middle">{{ $r->id }}</td>
<td class="align-middle">{{ $r->name }}</td>
<td class="text-center align-middle">
<button
wire:click="showForm({{ $r->id }})"
class="btn btn-sm btn-outline-info me-1"
>
Edit
</button>
<button
x-data
@click.prevent="
if (confirm('Bạn có chắc chắn muốn xóa role này?')) {
$wire.delete({{ $r->id }})
}
"
class="btn btn-sm btn-danger"
>
Delete
</button>
</td>
</tr>
@empty
<tr>
<td colspan="3" class="text-center">No roles found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{-- Pagination --}}
<div wire:loading.remove class="mt-3">
@if ($roles->lastPage() > 1)
{{ $roles->links() }}
@endif
</div>
@else
{{-- SKELETON FORM --}}
<x-skeleton-form :fields="1" :button-count="2" />
{{-- ACTUAL FORM --}}
<div wire:loading.remove x-data>
<form>
<div class="mb-3">
<label class="form-label">Name</label>
<input
type="text"
wire:model.defer="name"
class="form-control"
placeholder="Enter role name"
>
@error('name')
<div class="text-danger text-xs mt-1">{{ $message }}</div>
@enderror
</div>
<div class="d-flex justify-content-end">
<button
wire:click="showIndex"
type="button"
class="btn btn-secondary btn-sm me-2"
>
Cancel
</button>
<button
type="button"
@click.prevent="
if (confirm('Bạn có chắc chắn muốn {{ $editingId ? 'cập nhật' : 'tạo' }} role này?')) {
$wire.save()
}
"
class="btn btn-info btn-sm"
>
{{ $editingId ? 'Update' : 'Create' }}
</button>
</div>
</form>
</div>
@endif
</div>
</div>
</div>
@script
<script>
document.title = @json($title);
</script>
@endscript
</div>

@ -1,14 +1,21 @@
<?php <?php
use App\Http\Controllers\Auth\LoginController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\DashboardController; use App\Http\Controllers\DashboardController;
use App\Http\Controllers\PermissionController; use App\Http\Controllers\PermissionController;
use App\Components\Permission\Manager as PermissionManager; use App\Http\Controllers\RoleController;
Route::get('dashboard', [DashboardController::class, 'index'])
->name('dashboard')
->middleware('auth');
// Dashboard route (Livewire) for authenticated users only
Route::get('dashboard', [DashboardController::class, 'index'])->name('dashboard');
Route::middleware('auth')->group(function () { Route::middleware('auth')->group(function () {
// Permissions
Route::get('/permissions', [PermissionController::class, 'manager']) Route::get('/permissions', [PermissionController::class, 'manager'])
->name('permissions.index'); ->name('permissions.index');
// Roles
Route::get('/roles', [RoleController::class, 'manager'])
->name('roles.index');
}); });

Loading…
Cancel
Save