This commit is contained in:
pbao9
2026-02-07 16:18:39 +07:00
parent e7b9ce7485
commit 3dc19ccb83
11 changed files with 222 additions and 38 deletions
@@ -59,6 +59,23 @@ class ProductCategoryController extends Controller
return redirect()->route('products.group.index')->with('success', 'Delete Group Success');
}
public function trash()
{
return view('administrator.pages.product.categories.trash');
}
public function restore(int $id)
{
$this->productCategoryService->restore($id);
return redirect()->route('products.group.trash')->with('success', 'Restore Group Success');
}
public function forceDelete(int $id)
{
$this->productCategoryService->forceDelete($id);
return redirect()->route('products.group.trash')->with('success', 'Force Delete Group Success');
}
public function datatable(Request $request)
{
$query = $this->productCategoryRepository->filter(
@@ -72,7 +89,10 @@ class ProductCategoryController extends Controller
return DataTables::eloquent($query)
->addColumn(
'action',
function ($d) {
function ($d) use ($request) {
if ($request->has('trashed') && $request->trashed == 1) {
return view('administrator.pages.product.categories.datatable.action_trash', ['row' => $d])->render();
}
return view('administrator.pages.product.categories.datatable.action', ['row' => $d])->render();
}
)
@@ -29,4 +29,14 @@ class ProductCategoryService implements ProductCategoryServiceInterface
{
return $this->repository->delete($id);
}
public function restore(int $id)
{
return $this->repository->restore($id);
}
public function forceDelete(int $id)
{
return $this->repository->forceDelete($id);
}
}
@@ -6,4 +6,8 @@ interface ProductCategoryServiceInterface {
public function store(array $data);
public function update(int $id, array $data);
public function delete(int $id);
public function restore(int $id);
public function forceDelete(int $id);
}
@@ -40,6 +40,10 @@ class ProductCategoryRepository extends BaseRepository implements ProductCategor
->select($select)
->with($relationships);
if (!empty($data['trashed'])) {
$query->onlyTrashed();
}
// if (!empty($data['vcenter_id'])) {
// $query->where('vcenter_id', $data['vcenter_id']);
// }
@@ -60,4 +64,21 @@ class ProductCategoryRepository extends BaseRepository implements ProductCategor
return $query->get();
}
public function restore(int $id)
{
$item = $this->model->withTrashed()->find($id);
if ($item) {
return $item->restore();
}
return false;
}
public function forceDelete(int $id)
{
$item = $this->model->withTrashed()->find($id);
if ($item) {
return $item->forceDelete();
}
return false;
}
}
@@ -13,4 +13,8 @@ interface ProductCategoryRepositoryInterface extends BaseRepositoryInterface
array $select = ['*'],
bool $returnQuery = false
);
public function restore(int $id);
public function forceDelete(int $id);
}
@@ -1,6 +1,9 @@
@if(auth()->guard('admins')->user()->can("manager product"))
<x-button-crm type="button" variant="primary" size="sm" route="products.create" icon="plus-circle">Add
Product</x-button-crm>
<x-button-crm type="button" variant="info" size="sm" route="products.group.create" icon="plus-circle">Add
Group</x-button-crm>
<x-button-crm type="button" variant="outline-primary" size="sm" route="products.create" icon="plus-circle">Thêm sản
phẩm</x-button-crm>
<x-button-crm type="button" variant="outline-info" size="sm" route="products.group.create" icon="plus-circle">Thêm
nhóm
SP</x-button-crm>
<x-button-crm type="button" variant="outline-danger" size="sm" route="products.group.trash" icon="trash">Thùng
rác</x-button-crm>
@endif
@@ -1,6 +1,8 @@
<div class="d-flex gap-2">
<x-button-crm type="button" variant="primary" size="sm" route="products.group.edit" :params="['id' => $row->id]" icon="pencil">Edit</x-button-crm>
<x-button-crm type="button" variant="danger" size="sm" icon="trash" data-micromodal-trigger="deleteModal-{{ $row->id }}">Delete</x-button-crm>
<x-button-crm type="button" variant="outline-primary" size="sm" route="products.group.edit" :params="['id' => $row->id]"
icon="pencil">Edit</x-button-crm>
<x-button-crm type="button" variant="outline-danger" size="sm" icon="trash"
data-micromodal-trigger="deleteModal-{{ $row->id }}">Delete</x-button-crm>
</div>
@include('administrator.pages.product.categories.datatable.modal', [
@@ -0,0 +1,12 @@
<div class="d-flex gap-2">
<x-button-crm type="button" variant="success" size="sm" route="products.group.restore" :params="['id' => $row->id]" icon="rotate-ccw">Restore</x-button-crm>
<x-button-crm type="button" variant="danger" size="sm" icon="trash-2" data-micromodal-trigger="forceDeleteModal-{{ $row->id }}">Force Delete</x-button-crm>
</div>
@include('administrator.pages.product.categories.datatable.modal', [
'deleteRoute' => 'products.group.forceDelete',
'itemLabel' => 'Product Group',
'itemId' => $row->id,
'itemName' => $row->name,
'modalId' => 'forceDeleteModal-' . $row->id
])
@@ -0,0 +1,98 @@
@extends('administrator.layout.master')
@push('plugin-styles')
@foreach (config('assets.datatable.css') as $datatable)
<link href="{{ asset($datatable) }}" rel="stylesheet" />
@endforeach
@endpush
@section('breadcrumb')
@php
$breadcrumbItems = [
['label' => 'Product'],
['label' => 'Product Group', 'route' => route('products.group.index')],
['label' => 'Trash List', 'active' => true]
];
@endphp
<x-admin-breadcrumb :items="$breadcrumbItems" />
@endsection
@section('tab')
@php
$tabs = [
[
'label' => 'Product',
'route' => 'products.index',
'icon' => 'server',
],
[
'label' => 'Product Group',
'route' => 'products.group.index',
'icon' => 'server',
],
];
@endphp
<x-admin-tab :items="$tabs" />
@endsection
@section('content')
@if (auth()->guard('admins')->user()->can('manager product'))
<div class="card card-primary card-outline">
<div class="card-header d-flex justify-content-end gap-2">
<x-button-crm type="button" variant="secondary" size="sm" route="products.group.index" icon="arrow-left">Back to List</x-button-crm>
</div>
<div class="card-body">
<x-datatable id="productTable" ajax="{{ route('products.group.datatable', ['trashed' => 1]) }}" :columns="[
['key' => 'name', 'label' => 'Group'],
['key' => 'slug', 'label' => 'Slug'],
['key' => 'deleted_at', 'label' => 'Deleted At'],
['key' => 'action', 'label' => 'Action', 'searchable' => false],
]" />
</div>
</div>
@else
<div class="alert alert-danger alert-dismissible">
<h5 class="mb-0"><i class="icon fas fa-ban"></i> Access Denied - You are not assigned to any Product
management department</h5>
</div>
@endif
@endsection
@push('plugin-scripts')
@foreach (config('assets.datatable.js') as $datatable)
<script src="{{ asset($datatable) }}"></script>
@endforeach
@endpush
@push('custom-scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
// Function to initialize MicroModal
function initMicroModal() {
if (typeof MicroModal !== 'undefined') {
MicroModal.init({
onShow: modal => console.info(`${modal.id} is shown`),
onClose: modal => console.info(`${modal.id} is hidden`),
openTrigger: 'data-micromodal-trigger',
closeTrigger: 'data-micromodal-close',
disableScroll: true,
disableFocus: false,
awaitOpenAnimation: false,
awaitCloseAnimation: false,
debugMode: true
});
}
}
// Initialize on page load
initMicroModal();
// Re-initialize after DataTable draws (when action column renders)
$('#productTable').on('draw.dt', function() {
setTimeout(function() {
initMicroModal();
// Re-initialize Lucide icons for modal content
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}, 100);
});
});
</script>
@endpush
@@ -20,46 +20,53 @@
$tag = 'a';
}
$baseClasses = 'd-inline-flex gap-2 align-items-center justify-content-center text-white px-3 py-2 rounded-3 transition-all border-0';
$variants = [
'primary' => 'bg-primary',
'secondary' => 'bg-secondary',
'outline' => 'bg-outline',
'ghost' => 'bg-ghost',
'link' => 'bg-link',
'danger' => 'bg-danger',
'destructive' => 'bg-destructive',
'success' => 'bg-success',
'warning' => 'bg-warning',
'info' => 'bg-info'
'primary' => 'btn-primary',
'secondary' => 'btn-secondary',
'outline' => 'btn-outline-primary',
'outline-primary' => 'btn-outline-primary',
'outline-secondary' => 'btn-outline-secondary',
'outline-success' => 'btn-outline-success',
'outline-danger' => 'btn-outline-danger',
'outline-warning' => 'btn-outline-warning',
'outline-info' => 'btn-outline-info',
'ghost' => 'btn-light',
'link' => 'btn-link',
'danger' => 'btn-danger',
'destructive' => 'btn-danger',
'success' => 'btn-success',
'warning' => 'btn-warning',
'info' => 'btn-info',
'light' => 'btn-light',
'dark' => 'btn-dark',
];
$sizes = [
'default' => 'h-10',
'sm' => 'h-6',
'lg' => 'h-12',
'icon' => 'h-10',
'default' => '',
'sm' => 'btn-sm',
'lg' => 'btn-lg',
'xs' => 'btn-xs',
'icon' => 'btn-icon',
];
$variantClass = $variants[$variant ?? 'primary'] ?? $variants['primary'];
$sizeClass = $sizes[$size ?? 'md'] ?? $sizes['default'];
$sizeClass = $sizes[$size ?? 'default'] ?? '';
$classes = "{$baseClasses} {$variantClass} {$sizeClass}";
$classes = "btn {$variantClass} {$sizeClass}";
@endphp
@if($tag === 'a')
<a href="{{ $href }}" {{ $attributes->merge(['class' => $classes]) }}>
@if($icon)
<i data-lucide="{{ $icon }}" class="mr-2" style="height: 15px; width: 15px"></i>
@endif
{{ $slot }}
</a>
@else
<button type="{{ $buttonType }}" {{ $attributes->merge(['class' => $classes]) }}>
@if($tag === 'a')
<a href="{{ $href }}" {{ $attributes->merge(['class' => $classes]) }}>
@if($icon)
<i data-lucide="{{ $icon }}" class="mr-2" style="height: 15px; width: 15px"></i>
<i data-lucide="{{ $icon }}" class="me-1" style="height: 14px; width: 14px"></i>
@endif
{{ $slot }}
</button>
@endif
</a>
@else
<button type="{{ $buttonType }}" {{ $attributes->merge(['class' => $classes]) }}>
@if($icon)
<i data-lucide="{{ $icon }}" class="me-1" style="height: 14px; width: 14px"></i>
@endif
{{ $slot }}
</button>
@endif
+3
View File
@@ -143,6 +143,9 @@ Route::prefix('administrator')->group(function () {
Route::put('view-group-item/{id}', 'update')->name('update');
Route::delete('remove-group-item/{id}', 'destroy')->name('destroy');
Route::get('datatable', 'datatable')->name('datatable');
Route::get('trash', 'trash')->name('trash');
Route::get('restore/{id}', 'restore')->name('restore');
Route::delete('force-delete/{id}', 'forceDelete')->name('forceDelete');
});
});