From d1ef32bb79a1f4bad338926e666a7feebe010e67 Mon Sep 17 00:00:00 2001 From: A1Gard Date: Wed, 25 Dec 2024 05:03:03 +0330 Subject: [PATCH] added fast category edit ux feature --- .../Controllers/Admin/ProductController.php | 69 +++++++++++++------ resources/js/app.js | 1 + resources/js/panel/fast-edit.js | 59 ++++++++++++++++ resources/sass/panel/_common.scss | 31 +++++++++ resources/sass/panel/_item-list.scss | 4 ++ .../admin/products/category-edit.blade.php | 27 ++++++++ .../admin/products/product-list.blade.php | 6 ++ routes/web.php | 3 + 8 files changed, 178 insertions(+), 22 deletions(-) create mode 100644 resources/js/panel/fast-edit.js create mode 100644 resources/views/admin/products/category-edit.blade.php diff --git a/app/Http/Controllers/Admin/ProductController.php b/app/Http/Controllers/Admin/ProductController.php index c0f17ed..16c286d 100644 --- a/app/Http/Controllers/Admin/ProductController.php +++ b/app/Http/Controllers/Admin/ProductController.php @@ -19,10 +19,10 @@ class ProductController extends XController // protected $_MODEL_ = Product::class; // protected $SAVE_REQUEST = ProductSaveRequest::class; - protected $cols = ['name','category_id','view','sell','status']; - protected $extra_cols = ['id','slug','image_index']; + protected $cols = ['name', 'category_id', 'view', 'sell', 'status']; + protected $extra_cols = ['id', 'slug', 'image_index']; - protected $searchable = ['name','slug','description','excerpt','sku','table']; + protected $searchable = ['name', 'slug', 'description', 'excerpt', 'sku', 'table']; protected $listView = 'admin.products.product-list'; protected $formView = 'admin.products.product-form'; @@ -34,6 +34,8 @@ class ProductController extends XController ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'], 'destroy' => ['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'], + 'category' => + ['title' => "Edit category", 'class' => 'btn-outline-light edit-category-btn', 'icon' => 'ri-list-check-3'], ]; @@ -52,18 +54,18 @@ class ProductController extends XController // dd($request->all()); $product->name = $request->input('name'); - $product->slug = $this->getSlug($product,'slug','name'); + $product->slug = $this->getSlug($product, 'slug', 'name'); $product->table = $request->input('table'); $product->description = $request->input('desc'); $product->excerpt = $request->input('excerpt'); $product->keyword = $request->input('keyword'); $product->stock_status = $request->input('stock_status'); - $product->price = $request->input('price',0); - $product->buy_price = $request->input('buy_price',0); + $product->price = $request->input('price', 0); + $product->buy_price = $request->input('buy_price', 0); if (!$request->has('quantity')) { - $product->price = $request->input('price',0); + $product->price = $request->input('price', 0); $product->stock_quantity = $request->input('stock_quantity'); } $product->average_rating = $request->input('average_rating', 0); @@ -74,17 +76,17 @@ class ProductController extends XController $product->virtual = $request->input('virtual', false); $product->downloadable = $request->input('downloadable', false); $product->category_id = $request->input('category_id'); - $product->image_index = $request->input('index_image',0); + $product->image_index = $request->input('index_image', 0); $product->user_id = auth()->id(); $product->status = $request->input('status'); $tags = array_filter(explode(',,', $request->input('tags'))); - if ($request->has('canonical') && trim($request->input('canonical')) != ''){ + if ($request->has('canonical') && trim($request->input('canonical')) != '') { $product->canonical = $request->input('canonical'); } $product->save(); $product->categories()->sync($request->input('cat')); - if (count($tags) > 0){ + if (count($tags) > 0) { $product->syncTags($tags); } @@ -103,17 +105,17 @@ class ProductController extends XController if ($request->has('meta')) { // dd($request->input('meta')); - $product->syncMeta(json_decode($request->get('meta','[]'),true)); + $product->syncMeta(json_decode($request->get('meta', '[]'), true)); } $toRemoveQ = $product->quantities()->pluck('id')->toArray(); - if ($request->has('q')){ + if ($request->has('q')) { $qz = json_decode($request->input('q')); - foreach ($qz as $qi){ - if ($qi->id == null){ + foreach ($qz as $qi) { + if ($qi->id == null) { $q = new Quantity(); - }else{ + } else { $q = Quantity::whereId($qi->id)->first(); - unset($toRemoveQ[array_search($q->id, $toRemoveQ) ]); // remove for to remove IDz + unset($toRemoveQ[array_search($q->id, $toRemoveQ)]); // remove for to remove IDz } $q->image = $qi->image; $q->count = $qi->count; @@ -122,9 +124,9 @@ class ProductController extends XController $q->data = json_encode($qi->data); $q->save(); } - $product->quantities()->whereIn('id',$toRemoveQ)->delete(); + $product->quantities()->whereIn('id', $toRemoveQ)->delete(); - if ($product->quantities()->count() > 0){ + if ($product->quantities()->count() > 0) { $product->stock_quantity = $product->quantities()->sum('count'); $product->price = $product->quantities()->min('price'); } @@ -143,8 +145,8 @@ class ProductController extends XController public function create() { // - $cats = Category::all(['id','name','parent_id']); - return view($this->formView,compact('cats')); + $cats = Category::all(['id', 'name', 'parent_id']); + return view($this->formView, compact('cats')); } /** @@ -154,8 +156,8 @@ class ProductController extends XController { // - $cats = Category::all(['id','name','parent_id']); - return view($this->formView, compact('item','cats')); + $cats = Category::all(['id', 'name', 'parent_id']); + return view($this->formView, compact('item', 'cats')); } public function bulk(Request $request) @@ -211,4 +213,27 @@ class ProductController extends XController } /*restore**/ + + /** + * @param $id Product's id + * @return \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\View\View + */ + public function categoryEdit($id) + { + + $product = Product::find($id); + $cats = Category::all(['id', 'name', 'parent_id']); + return view('admin.products.category-edit', compact('product', 'cats')); + } + + public function categorySave(Product $item, Request $request) + { + $item->categories()->sync($request->input('cat')); + logAdmin(__METHOD__, __CLASS__, $item->id); + if ($request->ajax()) { + return ['OK' => true, 'message' => __('Categories saved successfully')]; + } else { + return redirect()->back()->with(['message' => __('Categories saved successfully')]); + } + } } diff --git a/resources/js/app.js b/resources/js/app.js index 0a3180f..03528cf 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -31,6 +31,7 @@ import './panel/sotable-controller.js'; import './panel/prototypes.js'; import './panel/panel-window-loader.js'; import './panel/responsive-control.js'; +import './panel/fast-edit.js'; // import './panel/seo-analyzer.js'; // chartjs.defaults.defaultFontFamily = "Vazir"; diff --git a/resources/js/panel/fast-edit.js b/resources/js/panel/fast-edit.js new file mode 100644 index 0000000..de2fe3e --- /dev/null +++ b/resources/js/panel/fast-edit.js @@ -0,0 +1,59 @@ +document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll('.edit-category-btn')?.forEach(function (el) { + el.setAttribute('href', '#edit-category'); + el.addEventListener('click', function (e) { + e.preventDefault(); + let id = this.closest('tr').querySelector('input.chkbox').getAttribute('value'); + const url = document.querySelector('#category-edit-url').value + id; + document.querySelector('#iframe-modal iframe').setAttribute('src', url); + document.querySelector('#iframe-modal').style.display = 'block'; + + }); + }); + + document.querySelector('#iframe-modal')?.addEventListener('click', function (e) { + if (e.target == this) { + this.style.display = 'none'; + } + }); + + document.querySelector('#categories-save-btn')?.addEventListener('click', function (e) { + e.preventDefault(); + + + const url = document.querySelector('#ajax-sync-form').getAttribute('action'); + + // Serialize the form data + const formData = new FormData(); + const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked'); + + checkboxes.forEach(checkbox => { + formData.append('cat[]', checkbox.value); + }); + + // Optional: log serialized data for debugging + for (const [key, value] of formData.entries()) { + console.log(`${key}: ${value}`); + } + + // Get the URL from the form's action attribute + + // Make the AJAX POST request using Axios + axios.post(url, formData) + .then(response => { + // Handle success + if (response.data.OK == true){ + $toast.success(response.data.message); + }else{ + + $toast.error(response.data.error); + } + }) + .catch(error => { + // Handle error + $toast.error( error); + }); + + }); + +}); diff --git a/resources/sass/panel/_common.scss b/resources/sass/panel/_common.scss index 7753704..a5d569e 100644 --- a/resources/sass/panel/_common.scss +++ b/resources/sass/panel/_common.scss @@ -474,3 +474,34 @@ a.btn,a.action-btn,a.circle-btn{ } } + +.nested-ul{ + list-style: none; + ul{ + list-style: none; + } + input[type='checkbox']{ + margin: 0 1rem; + } +} + + +#iframe-modal{ + background: #00000011; + backdrop-filter: blur(10px); + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 999; + padding-top: 5vh; + display: none; + + iframe{ + border: 0; + height: 90vh; + width: 100%; + border-radius: 1rem; + } +} diff --git a/resources/sass/panel/_item-list.scss b/resources/sass/panel/_item-list.scss index cb3943d..3ae94ca 100644 --- a/resources/sass/panel/_item-list.scss +++ b/resources/sass/panel/_item-list.scss @@ -30,6 +30,10 @@ padding: 1rem; } } + + .alert{ + margin-bottom: 1rem; + } } .table-list { diff --git a/resources/views/admin/products/category-edit.blade.php b/resources/views/admin/products/category-edit.blade.php new file mode 100644 index 0000000..9d9cd9a --- /dev/null +++ b/resources/views/admin/products/category-edit.blade.php @@ -0,0 +1,27 @@ +@include('components.panel-header') +
+
+
+ +
+@csrf +
+ @include('components.err') +
+ {{__("Categories")}} [{{$product->name}}] +
+
    + {!!showCatNestedControl($cats,old('cat',isset($product)?$product->categories()->pluck('id')->toArray():[]))!!} +
+
+ +
+@include('components.panel-footer') diff --git a/resources/views/admin/products/product-list.blade.php b/resources/views/admin/products/product-list.blade.php index 7e4fcc5..b7c20b5 100644 --- a/resources/views/admin/products/product-list.blade.php +++ b/resources/views/admin/products/product-list.blade.php @@ -10,6 +10,12 @@ @section('filter') {{-- Other filters --}}

+ +
+
+ +
+
{{__("Category")}}:

diff --git a/routes/web.php b/routes/web.php index ed17949..957a218 100644 --- a/routes/web.php +++ b/routes/web.php @@ -301,6 +301,9 @@ Route::prefix(config('app.panel.prefix'))->name('admin.')->group( Route::get('restore/{item}', [\App\Http\Controllers\Admin\ProductController::class, 'restore'])->name('restore'); Route::post('bulk', [\App\Http\Controllers\Admin\ProductController::class, "bulk"])->name('bulk'); Route::get('trashed', [\App\Http\Controllers\Admin\ProductController::class, "trashed"])->name('trashed'); + Route::get('category/edit/{id}', [\App\Http\Controllers\Admin\ProductController::class, 'categoryEdit'])->name('category-edit'); + Route::post('category/save/{item}', [\App\Http\Controllers\Admin\ProductController::class, 'categorySave'])->name('category-save'); + }); Route::prefix('props')->name('prop.')->group( function () {