فهرست منبع

новая логика открытия карточек и возврата

Alexander Musikhin 3 هفته پیش
والد
کامیت
a1d583cfb8

+ 37 - 0
app/Http/Controllers/Controller.php

@@ -22,6 +22,43 @@ class Controller extends BaseController
         'dates'     => [],
     ];
 
+    protected function resolvePreviousUrl(Request $request, string $sessionKey, ?string $fallback = null): ?string
+    {
+        $previousUrl = $request->get('previous_url');
+        if (!empty($previousUrl)) {
+            session([$sessionKey => $previousUrl]);
+            return $previousUrl;
+        }
+
+        $previousUrl = session($sessionKey);
+        if (!empty($previousUrl)) {
+            return $previousUrl;
+        }
+
+        if (!empty($fallback)) {
+            session([$sessionKey => $fallback]);
+            return $fallback;
+        }
+
+        return null;
+    }
+
+    protected function previousUrlForRedirect(Request $request, string $sessionKey, ?string $fallback = null): ?string
+    {
+        $previousUrl = $request->get('previous_url');
+        if (!empty($previousUrl)) {
+            session([$sessionKey => $previousUrl]);
+            return $previousUrl;
+        }
+
+        $previousUrl = session($sessionKey);
+        if (!empty($previousUrl)) {
+            return $previousUrl;
+        }
+
+        return $fallback;
+    }
+
     /**
      * @param Model $model
      * @param string ...$columnNames

+ 6 - 1
app/Http/Controllers/ImportController.php

@@ -79,9 +79,14 @@ class ImportController extends Controller
 
     }
 
-    public function show(Import $import)
+    public function show(Request $request, Import $import)
     {
         $this->data['import'] = $import;
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_import',
+            route('import.index', session('gp_import'))
+        );
         return view('import.show', $this->data);
     }
 

+ 5 - 1
app/Http/Controllers/MafOrderController.php

@@ -62,7 +62,11 @@ class MafOrderController extends Controller
     public function show(Request $request, int $maf_order)
     {
         $this->data['maf_order'] = MafOrder::query()->withoutGlobalScopes()->find($maf_order);
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_maf_order',
+            route('maf_order.index', session('gp_maf_order'))
+        );
         return view('maf_orders.edit', $this->data);
     }
 

+ 15 - 3
app/Http/Controllers/OrderController.php

@@ -122,7 +122,11 @@ class OrderController extends Controller
      */
     public function create(Request $request)
     {
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_orders',
+            route('order.index', session('gp_orders'))
+        );
         return view('orders.edit', $this->data);
     }
 
@@ -184,7 +188,11 @@ class OrderController extends Controller
     public function show(Request $request, int $order)
     {
         $this->data['order'] = Order::query()->withoutGlobalScopes()->find($order);
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_orders',
+            route('order.index', session('gp_orders'))
+        );
         return view('orders.show', $this->data);
     }
 
@@ -194,7 +202,11 @@ class OrderController extends Controller
     public function edit(Request $request, Order $order)
     {
         $this->data['order'] = $order;
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_orders',
+            route('order.index', session('gp_orders'))
+        );
         return view('orders.edit', $this->data);
     }
 

+ 21 - 4
app/Http/Controllers/ProductController.php

@@ -80,13 +80,22 @@ class ProductController extends Controller
 
     public function show(Request $request, int $product)
     {
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_products',
+            route('catalog.index', session('gp_products'))
+        );
         $this->data['product'] = Product::query()->withoutGlobalScopes()->find($product);
         return view('catalog.edit', $this->data);
     }
 
-    public function create()
+    public function create(Request $request)
     {
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_products',
+            route('catalog.index', session('gp_products'))
+        );
         $this->data['product'] = null;
         return view('catalog.edit', $this->data);
     }
@@ -94,14 +103,22 @@ class ProductController extends Controller
     public function store(StoreProductRequest $request)
     {
         Product::create($request->validated());
-        $previous_url = $request->get('previous_url') ?? route('catalog.index', session('gp_products'));
+        $previous_url = $this->previousUrlForRedirect(
+            $request,
+            'previous_url_products',
+            route('catalog.index', session('gp_products'))
+        );
         return redirect()->to($previous_url);
     }
 
     public function update(StoreProductRequest $request, Product $product)
     {
         $product->update($request->validated());
-        $previous_url = $request->get('previous_url') ?? route('catalog.index', session('gp_products'));
+        $previous_url = $this->previousUrlForRedirect(
+            $request,
+            'previous_url_products',
+            route('catalog.index', session('gp_products'))
+        );
         return redirect()->to($previous_url);
     }
 

+ 22 - 4
app/Http/Controllers/ProductSKUController.php

@@ -79,7 +79,11 @@ class ProductSKUController extends Controller
 
     public function update(ProductSKUStoreRequest $request, ProductSKU $product_sku)
     {
-        $url = $request->previous_url ?? route('product_sku.index', session('gp_product_sku'));
+        $url = $this->previousUrlForRedirect(
+            $request,
+            'previous_url_product_sku',
+            route('product_sku.index', session('gp_product_sku'))
+        );
 
         $product_sku->update($request->validated());
         return redirect($url);
@@ -88,7 +92,11 @@ class ProductSKUController extends Controller
     public function show(Request $request, int $product_sku)
     {
         $this->data['product_sku'] = ProductSKU::query()->withoutGlobalScopes()->find($product_sku);
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_product_sku',
+            route('product_sku.index', session('gp_product_sku'))
+        );
         return view('products_sku.edit', $this->data);
     }
 
@@ -100,7 +108,14 @@ class ProductSKUController extends Controller
 
         $f = $fileService->saveUploadedFile('maf/' . $product_sku->id . '/passport', $data['passport']);
         $product_sku->update(['passport_id' => $f->id]);
-        return redirect()->route('product_sku.show', ['product_sku' => $product_sku, 'previous_url' => $request->get('previous_url')]);
+        return redirect()->route('product_sku.show', [
+            'product_sku' => $product_sku,
+            'previous_url' => $this->previousUrlForRedirect(
+                $request,
+                'previous_url_product_sku',
+                route('product_sku.index', session('gp_product_sku'))
+            ),
+        ]);
     }
 
     public function deletePassport(ProductSKU $product_sku, File $file)
@@ -108,7 +123,10 @@ class ProductSKUController extends Controller
         $product_sku->update(['passport_id' => null]);
         Storage::disk('public')->delete($file->path);
         $file->delete();
-        return redirect()->route('product_sku.show', $product_sku);
+        return redirect()->route('product_sku.show', [
+            'product_sku' => $product_sku,
+            'previous_url' => session('previous_url_product_sku'),
+        ]);
     }
 
     public function exportMaf(Request $request)

+ 13 - 1
app/Http/Controllers/ReclamationController.php

@@ -124,7 +124,11 @@ class ReclamationController extends Controller
     {
         $this->data['brigadiers'] = User::query()->where('role', Role::BRIGADIER)->get()->pluck('name', 'id');
         $this->data['reclamation'] = $reclamation;
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_reclamations',
+            route('reclamations.index', session('gp_reclamations'))
+        );
         return view('reclamations.edit', $this->data);
     }
 
@@ -132,6 +136,14 @@ class ReclamationController extends Controller
     {
         $data = $request->validated();
         $reclamation->update($data);
+        $previousUrl = $this->previousUrlForRedirect($request, 'previous_url_reclamations');
+        if (!empty($previousUrl)) {
+            return redirect()->route('reclamations.show', [
+                'reclamation' => $reclamation,
+                'previous_url' => $previousUrl,
+            ]);
+        }
+
         return redirect()->route('reclamations.show', $reclamation->id);
     }
 

+ 21 - 4
app/Http/Controllers/SparePartController.php

@@ -88,7 +88,11 @@ class SparePartController extends Controller
 
     public function show(Request $request, SparePart $sparePart)
     {
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_spare_parts',
+            route('spare_parts.index', session('gp_spare_parts'))
+        );
         $this->data['spare_part'] = $sparePart;
         return view('spare_parts.edit', $this->data);
     }
@@ -116,8 +120,13 @@ class SparePartController extends Controller
         return view('spare_parts.index', $this->data);
     }
 
-    public function create()
+    public function create(Request $request)
     {
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_spare_parts',
+            route('spare_parts.index', session('gp_spare_parts'))
+        );
         $this->data['spare_part'] = null;
         return view('spare_parts.edit', $this->data);
     }
@@ -126,7 +135,11 @@ class SparePartController extends Controller
     {
         $sparePart = SparePart::create($request->validated());
         $this->syncPricingCodes($sparePart, $request);
-        $previous_url = $request->get('previous_url') ?? route('spare_parts.index', session('gp_spare_parts'));
+        $previous_url = $this->previousUrlForRedirect(
+            $request,
+            'previous_url_spare_parts',
+            route('spare_parts.index', session('gp_spare_parts'))
+        );
         return redirect()->to($previous_url)->with(['success' => 'Запчасть успешно создана!']);
     }
 
@@ -134,7 +147,11 @@ class SparePartController extends Controller
     {
         $sparePart->update($request->validated());
         $this->syncPricingCodes($sparePart, $request);
-        $previous_url = $request->get('previous_url') ?? route('spare_parts.index', session('gp_spare_parts'));
+        $previous_url = $this->previousUrlForRedirect(
+            $request,
+            'previous_url_spare_parts',
+            route('spare_parts.index', session('gp_spare_parts'))
+        );
         return redirect()->to($previous_url)->with(['success' => 'Запчасть успешно обновлена!']);
     }
 

+ 21 - 4
app/Http/Controllers/SparePartOrderController.php

@@ -96,7 +96,11 @@ class SparePartOrderController extends Controller
 
     public function show(Request $request, SparePartOrder $sparePartOrder)
     {
-        $this->data['previous_url'] = $request->get('previous_url');
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_spare_part_orders',
+            route('spare_part_orders.index', session('gp_spare_part_orders'))
+        );
         $this->data['spare_part_order'] = $sparePartOrder->load([
             'sparePart',
             'movements.user',
@@ -105,8 +109,13 @@ class SparePartOrderController extends Controller
         return view('spare_part_orders.edit', $this->data);
     }
 
-    public function create()
+    public function create(Request $request)
     {
+        $this->data['previous_url'] = $this->resolvePreviousUrl(
+            $request,
+            'previous_url_spare_part_orders',
+            route('spare_part_orders.index', session('gp_spare_part_orders'))
+        );
         $this->data['spare_part_order'] = null;
         return view('spare_part_orders.edit', $this->data);
     }
@@ -119,7 +128,11 @@ class SparePartOrderController extends Controller
         // Observer автоматически обработает дефициты при создании партии со статусом in_stock
         SparePartOrder::create($data);
 
-        $previous_url = $request->get('previous_url') ?? route('spare_part_orders.index', session('gp_spare_part_orders'));
+        $previous_url = $this->previousUrlForRedirect(
+            $request,
+            'previous_url_spare_part_orders',
+            route('spare_part_orders.index', session('gp_spare_part_orders'))
+        );
         return redirect()->to($previous_url)->with(['success' => 'Заказ детали успешно создан!']);
     }
 
@@ -128,7 +141,11 @@ class SparePartOrderController extends Controller
         // Observer автоматически обработает дефициты при смене статуса на in_stock
         $sparePartOrder->update($request->validated());
 
-        $previous_url = $request->get('previous_url') ?? route('spare_part_orders.index', session('gp_spare_part_orders'));
+        $previous_url = $this->previousUrlForRedirect(
+            $request,
+            'previous_url_spare_part_orders',
+            route('spare_part_orders.index', session('gp_spare_part_orders'))
+        );
         return redirect()->to($previous_url)->with(['success' => 'Заказ детали успешно обновлён!']);
     }
 

+ 16 - 0
resources/sass/app.scss

@@ -59,6 +59,22 @@
   font-size: 0.8rem;
 }
 
+.table-interactive tbody tr {
+  transition: background-color 0.12s ease-in-out;
+}
+
+.table-interactive tbody tr:hover > td {
+  background-color: #f2f7ff;
+}
+
+.table-interactive tbody tr.is-selected > td {
+  background-color: #dbe7ff;
+}
+
+.table-interactive tbody tr.is-selected:hover > td {
+  background-color: #cfe0ff;
+}
+
 .maf:hover {
   background-color: #fff8ca;
 }

+ 1 - 2
resources/views/maf_orders/edit.blade.php

@@ -21,7 +21,7 @@
                     @if($maf_order->products_sku->count())
                         <div class="row">
                             <div class="buttons offset-md-4 col-md-8 ">
-                                <a href="{{ url()->previous() }}" class="btn btn-sm btn-primary">Назад</a>
+                                <a href="{{ $previous_url ?? route('maf_order.index', session('gp_maf_order')) }}" class="btn btn-sm btn-primary">Назад</a>
                             </div>
                         </div>
                     @else
@@ -63,4 +63,3 @@
         </form>
     </div>
 @endsection
-

+ 25 - 5
resources/views/partials/newFilterElement.blade.php

@@ -39,6 +39,10 @@
                         <i class="bi bi-sort-alpha-down" id="sort-filter-icon-{{$id}}"></i>
                     </button>
                 </div>
+                <div class="d-flex gap-2 mb-2" id="bulk-toggle_{{$id}}">
+                    <button type="button" class="btn btn-outline-secondary btn-sm w-50" id="check-all_{{$id}}">Выбрать все</button>
+                    <button type="button" class="btn btn-outline-secondary btn-sm w-50" id="uncheck-all_{{$id}}">Снять все</button>
+                </div>
                 <div class="ps-1" id="filter-list-{{$id}}" style="max-height: 200px; overflow-y: scroll;">
                 </div>
             </form>
@@ -103,9 +107,12 @@
                 const $searchInput = $("#search_{{$id}}");
                 const $sortBtn = $("#sort-filter-{{$id}}");
                 const $sortIcon = $("#sort-filter-icon-{{$id}}");
+                const $bulkToggle = $("#bulk-toggle_{{$id}}");
+                const $checkAllBtn = $("#check-all_{{$id}}");
+                const $uncheckAllBtn = $("#uncheck-all_{{$id}}");
                 const urlParams = new URL(document.location.href);
                 const existingFilter = urlParams.searchParams.get(`filters[{{$id}}]`);
-                const selectedValues = existingFilter ? existingFilter.split("||") : [];
+                const selectedValues = existingFilter ? existingFilter.split("||") : null;
 
                 let filterData = [];
                 let sortAsc = true;
@@ -121,9 +128,13 @@
                     `).join('');
                     $container.html(html);
 
-                    selectedValues.forEach(value => {
-                        $container.find(`.form-check-input[value="${$.escapeSelector(value)}"]`).prop("checked", true);
-                    });
+                    if (selectedValues === null) {
+                        $container.find(".form-check-input").prop("checked", true);
+                    } else {
+                        selectedValues.forEach(value => {
+                            $container.find(`.form-check-input[value="${$.escapeSelector(value)}"]`).prop("checked", true);
+                        });
+                    }
                 }
 
                 function sortData(asc) {
@@ -146,6 +157,7 @@
                         renderFilterList(sortedData);
                     } else {
                         $("#search_{{$id}}").parent().hide();
+                        $bulkToggle.hide();
                         $("#modal-footer_{{$id}}").hide();
                         $container.html('<div class="text-muted">Нет данных</div>');
                     }
@@ -164,6 +176,14 @@
                     renderFilterList(sortedData);
                 });
 
+                $checkAllBtn.on("click", function () {
+                    $container.find(".form-check-input").prop("checked", true);
+                });
+
+                $uncheckAllBtn.on("click", function () {
+                    $container.find(".form-check-input").prop("checked", false);
+                });
+
                 $("#accept-filter_{{$id}}").on("click", function () {
                     const checkedValues = $container.find(".form-check-input:checked")
                         .map(function () {
@@ -172,7 +192,7 @@
                         .get();
 
                     let currentUrl = new URL(document.location.href);
-                    if(!checkedValues.join('||')) {
+                    if (!checkedValues.join('||') || checkedValues.length === filterData.length) {
                         currentUrl.searchParams.delete('filters[{{$id}}]');
                     } else {
                         currentUrl.searchParams.set('filters[{{$id}}]', checkedValues.join('||'));

+ 1 - 1
resources/views/partials/submit.blade.php

@@ -5,7 +5,7 @@
             <a href="#" class="btn btn-sm mb-1 btn-danger delete">{{ $delete['title'] ?? 'Удалить' }}</a>
         @endif
 
-        <a href="{!! $backurl ?? url()->previous() !!}" class="btn btn-sm mb-1 btn-outline-secondary">Назад</a>
+        <a href="{!! $backurl ?? ($previous_url ?? url()->previous()) !!}" class="btn btn-sm mb-1 btn-outline-secondary">Назад</a>
 
     </div>
 </div>

+ 88 - 8
resources/views/partials/table.blade.php

@@ -17,7 +17,7 @@
             <i class="bi bi-gear-fill"></i>
         </button>
     </div>
-    <table class="table" id="tbl" data-table-name="{{ $id }}" style="display: none; min-height: 380px;">
+    <table class="table table-interactive" id="tbl" data-table-name="{{ $id }}" style="display: none; min-height: 380px;">
         <thead>
         <tr>
             @foreach($header as $headerName => $headerTitle)
@@ -77,15 +77,22 @@
         </thead>
         <tbody>
         @foreach($strings as $string)
-            <tr>
+            @php
+                $rowId = $string->id ?? null;
+                $rowAnchor = $rowId ? 'row-' . $rowId : null;
+                $rowHref = null;
+                if (isset($routeName) && $rowId) {
+                    $rowHref = route($routeName, [$string->id, 'previous_url' => request()->fullUrl() . '#' . $rowAnchor]);
+                }
+            @endphp
+            <tr
+                @if($rowAnchor) id="{{ $rowAnchor }}" data-row-id="{{ $rowId }}" @endif
+                @if($rowHref) data-row-href="{{ $rowHref }}" @endif
+                @if($id === 'reclamations') data-open-target="_blank" @endif
+            >
                 @foreach($header as $headerName => $headerTitle)
                     <td class="column_{{$headerName}}"
-                        @if(isset($routeName)
-                            && !str_contains($headerName, 'image')
-                            && !str_contains($headerName, 'order_status_name')
-                            && !($id === 'reclamations' && $headerName === 'status_name'))
-                            onclick="@if($id === 'reclamations')window.open('{{ route($routeName, $string->id) }}','_blank');@else location.href='{{ route($routeName, $string->id) }}';@endif"
-                        @endif>
+                    >
                         @if(str_contains($headerName, '-'))
                             @php
                                 list($rel, $field) = explode('-', $headerName);
@@ -369,6 +376,54 @@
 
         $('.table').fadeIn();
 
+        function isRowActionTarget(target) {
+            return $(target).closest('a, button, input, select, textarea, label, .dropdown, [data-bs-toggle], [data-no-row-select]').length > 0;
+        }
+
+        function selectRow($row, updateHash = true) {
+            if (!$row || !$row.length) {
+                return;
+            }
+            $row.closest('tbody').find('tr.is-selected').removeClass('is-selected');
+            $row.addClass('is-selected');
+            if (updateHash) {
+                const rowId = $row.data('row-id');
+                if (rowId) {
+                    const hash = '#row-' + rowId;
+                    const newUrl = window.location.pathname + window.location.search + hash;
+                    history.replaceState(null, '', newUrl);
+                    if (tableName) {
+                        localStorage.setItem('table_last_row_' + tableName, String(rowId));
+                    }
+                }
+            }
+        }
+
+        $(document).on('click', '.table-interactive tbody tr', function (e) {
+            if (isRowActionTarget(e.target)) {
+                return;
+            }
+            selectRow($(this));
+        });
+
+        $(document).on('dblclick', '.table-interactive tbody tr', function (e) {
+            if (isRowActionTarget(e.target)) {
+                return;
+            }
+            const $row = $(this);
+            const href = $row.data('row-href');
+            if (!href) {
+                return;
+            }
+            selectRow($row, false);
+            const target = $row.data('open-target');
+            if (target === '_blank') {
+                window.open(href, '_blank');
+            } else {
+                window.location.href = href;
+            }
+        });
+
         $('.toggle-column').on('change', function () {
             let columnName = $(this).attr('data-name');
             let columnStatus = $(this).is(':checked');
@@ -497,6 +552,31 @@
             // Инициализация tooltips для полей tsn_number и pricing_code
             const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
             const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
+
+            const hash = window.location.hash;
+            if (hash && hash.startsWith('#row-')) {
+                const $row = $(hash);
+                if ($row.length) {
+                    selectRow($row, false);
+                    $row[0].scrollIntoView({block: 'center'});
+                    const rowId = $row.data('row-id');
+                    if (rowId && tableName) {
+                        localStorage.setItem('table_last_row_' + tableName, String(rowId));
+                    }
+                    return;
+                }
+            }
+
+            if (tableName) {
+                const storedRowId = localStorage.getItem('table_last_row_' + tableName);
+                if (storedRowId) {
+                    const $storedRow = $('#row-' + storedRowId);
+                    if ($storedRow.length) {
+                        selectRow($storedRow, false);
+                        $storedRow[0].scrollIntoView({block: 'center'});
+                    }
+                }
+            }
         });
         }); // end waitForJQuery
     </script>