Merge pull request #3223 from Haendlerbund/admin-backend/keep-search-terms-after-new-page-load
Admin DataGrid: Keep search terms after page reload
This commit is contained in:
commit
e6d11ed809
|
|
@ -16,7 +16,7 @@
|
|||
"cross-env": "^5.1",
|
||||
"jquery": "^3.2",
|
||||
"laravel-mix": "^5.0.1",
|
||||
"lodash": "^4.17.4",
|
||||
"lodash": "^4.17.19",
|
||||
"popper.js": "^1.12",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.24.5",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ class CartRuleDataGrid extends DataGrid
|
|||
|
||||
protected $channel = 'all';
|
||||
|
||||
/** @var string[] contains the keys for which extra filters to show */
|
||||
protected $extraFilters = [
|
||||
'channels',
|
||||
'customer_groups',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ class ProductDataGrid extends DataGrid
|
|||
|
||||
protected $channel = 'all';
|
||||
|
||||
/** @var string[] contains the keys for which extra filters to render */
|
||||
protected $extraFilters = [
|
||||
'channels',
|
||||
'locales',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
|
@ -44,15 +50,11 @@ class ProductDataGrid extends DataGrid
|
|||
);
|
||||
|
||||
if ($this->locale !== 'all') {
|
||||
$queryBuilder->where('locale', $this->locale);
|
||||
$queryBuilder->where('product_flat.locale', $this->locale);
|
||||
}
|
||||
|
||||
if ($this->channel !== 'all') {
|
||||
$queryBuilder->where('channel', $this->channel);
|
||||
}
|
||||
|
||||
if ($currentLocale = app()->getLocale()) {
|
||||
$queryBuilder->where('product_flat.locale', $currentLocale);
|
||||
$queryBuilder->where('product_flat.channel', $this->channel);
|
||||
}
|
||||
|
||||
$queryBuilder->groupBy('product_flat.product_id');
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ class SliderDataGrid extends DataGrid
|
|||
|
||||
protected $channel = 'all';
|
||||
|
||||
/** @var string[] contains the keys for which extra filters to render */
|
||||
protected $extraFilters = [
|
||||
'channels',
|
||||
'locales',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
|
|
|||
|
|
@ -11,39 +11,6 @@
|
|||
<div class="page-header">
|
||||
<div class="page-title">
|
||||
<h1>{{ __('admin::app.catalog.products.title') }}</h1>
|
||||
|
||||
<div class="control-group">
|
||||
<select class="control" id="channel-switcher" name="channel" onchange="reloadPage('channel', this.value)" >
|
||||
<option value="all" {{ ! isset($channel) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-channels') }}
|
||||
</option>
|
||||
|
||||
@foreach (core()->getAllChannels() as $channelModel)
|
||||
<option
|
||||
value="{{ $channelModel->code }}" {{ (isset($channel) && ($channelModel->code) == $channel) ? 'selected' : '' }}>
|
||||
{{ $channelModel->name }}
|
||||
</option>
|
||||
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<select class="control" id="locale-switcher" name="locale" onchange="reloadPage('locale', this.value)" >
|
||||
<option value="all" {{ ! isset($locale) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-locales') }}
|
||||
</option>
|
||||
|
||||
@foreach (core()->getAllLocales() as $localeModel)
|
||||
|
||||
<option
|
||||
value="{{ $localeModel->code }}" {{ (isset($locale) && ($localeModel->code) == $locale) ? 'selected' : '' }}>
|
||||
{{ $localeModel->name }}
|
||||
</option>
|
||||
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-action">
|
||||
|
|
|
|||
|
|
@ -14,38 +14,6 @@
|
|||
</div>
|
||||
|
||||
<div class="page-action">
|
||||
<div class="control-group">
|
||||
<select class="control" id="channel-switcher" name="channel" onchange="reloadPage('channel', this.value)" >
|
||||
<option value="all" {{ ! isset($channel) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-channels') }}
|
||||
</option>
|
||||
|
||||
@foreach (core()->getAllChannels() as $channelModel)
|
||||
<option
|
||||
value="{{ $channelModel->id }}" {{ (isset($channel) && ($channelModel->id) == $channel) ? 'selected' : '' }}>
|
||||
{{ $channelModel->name }}
|
||||
</option>
|
||||
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<select class="control" id="customer-group-switcher" name="customer_group" onchange="reloadPage('customer_group', this.value)" >
|
||||
<option value="all" {{ ! isset($locale) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-customer-groups') }}
|
||||
</option>
|
||||
|
||||
@foreach (core()->getAllCustomerGroups() as $customerGroupModel)
|
||||
<option
|
||||
value="{{ $customerGroupModel->id }}" {{ (isset($customerGroupModel) && ($customerGroupModel->id) == $customer_group) ? 'selected' : '' }}>
|
||||
{{ $customerGroupModel->name }}
|
||||
</option>
|
||||
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('admin.cart-rules.create') }}" class="btn btn-lg btn-primary">
|
||||
{{ __('admin::app.promotions.cart-rules.add-title') }}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -14,41 +14,6 @@
|
|||
<div class="page-header">
|
||||
<div class="page-title">
|
||||
<h1>{{ __('admin::app.settings.sliders.title') }}</h1>
|
||||
|
||||
<div class="control-group">
|
||||
<select class="control" id="channel-switcher" name="channel" onchange="reloadPage('channel', this.value)" >
|
||||
<option value="all" {{ ! isset($channel) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-channels') }}
|
||||
</option>
|
||||
|
||||
@foreach (core()->getAllChannels() as $channelModel)
|
||||
|
||||
<option
|
||||
value="{{ $channelModel->code }}" {{ (isset($channel) && ($channelModel->code) == $channel) ? 'selected' : '' }}>
|
||||
{{ $channelModel->name }}
|
||||
</option>
|
||||
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<select class="control" id="locale-switcher" name="locale" onchange="reloadPage('locale', this.value)" >
|
||||
<option value="all" {{ ! isset($locale) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-locales') }}
|
||||
</option>
|
||||
|
||||
@foreach (core()->getAllLocales() as $localeModel)
|
||||
|
||||
<option
|
||||
value="{{ $localeModel->code }}" {{ (isset($locale) && ($localeModel->code) == $locale) ? 'selected' : '' }}>
|
||||
{{ $localeModel->name }}
|
||||
</option>
|
||||
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="page-action">
|
||||
|
|
|
|||
|
|
@ -2,16 +2,14 @@
|
|||
|
||||
/** @var \Illuminate\Database\Eloquent\Factory $factory */
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Faker\Generator as Faker;
|
||||
use Webkul\BookingProduct\Models\BookingProduct;
|
||||
use Webkul\BookingProduct\Models\BookingProductEventTicket;
|
||||
use Webkul\Product\Models\Product;
|
||||
|
||||
$factory->define(BookingProductEventTicket::class, function (Faker $faker, array $attributes) {
|
||||
$factory->define(BookingProductEventTicket::class, static function (Faker $faker, array $attributes) {
|
||||
return [
|
||||
'price' => $faker->randomFloat(4, 3, 900),
|
||||
'qty' => $faker->numberBetween(1, 99),
|
||||
'qty' => $faker->numberBetween(100, 1000),
|
||||
'booking_product_id' => static function () {
|
||||
return factory(BookingProduct::class)->create(['type' => 'event'])->id;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
*
|
||||
*
|
||||
* @author Jerry Bendy <jerry@icewingcc.com>
|
||||
* @licence MIT
|
||||
*
|
||||
*/
|
||||
|
||||
/* flatpickr v4.6.3, @license MIT */
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/* flatpickr v4.6.3, @license MIT */
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Jerry Bendy <jerry@icewingcc.com>
|
||||
* @licence MIT
|
||||
*
|
||||
*/
|
||||
|
||||
/**!
|
||||
* @fileOverview Kickass library to create and place poppers near their reference elements.
|
||||
* @version 1.16.1
|
||||
* @license
|
||||
* Copyright (c) 2016 Federico Zivolo and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"/js/ui.js": "/js/ui.js?id=3ccd955983e91b6ccc96",
|
||||
"/js/ui.js": "/js/ui.js?id=9f7467acd7ea119b7d87",
|
||||
"/css/ui.css": "/css/ui.css?id=b5bedc0ff04a145ca18a"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ abstract class DataGrid
|
|||
{
|
||||
/**
|
||||
* set index columns, ex: id.
|
||||
*
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $index = null;
|
||||
protected $index;
|
||||
|
||||
/**
|
||||
* Default sort order of datagrid
|
||||
*
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $sortOrder = 'asc';
|
||||
|
|
@ -23,21 +23,21 @@ abstract class DataGrid
|
|||
/**
|
||||
* Situation handling property when working with custom columns in datagrid, helps abstaining
|
||||
* aliases on custom column.
|
||||
*
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enableFilterMap = false;
|
||||
|
||||
/**
|
||||
* This is array where aliases and custom column's name are passed
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $filterMap = [];
|
||||
|
||||
/**
|
||||
* array to hold all the columns which will be displayed on frontend.
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $columns = [];
|
||||
|
|
@ -51,14 +51,14 @@ abstract class DataGrid
|
|||
/**
|
||||
* Hold query builder instance of the query prepared by executing datagrid
|
||||
* class method setQueryBuilder
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $queryBuilder = [];
|
||||
|
||||
/**
|
||||
* Final result of the datagrid program that is collection object.
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $collection = [];
|
||||
|
|
@ -66,7 +66,7 @@ abstract class DataGrid
|
|||
/**
|
||||
* Set of handly click tools which you could be using for various operations.
|
||||
* ex: dyanmic and static redirects, deleting, etc.
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $actions = [];
|
||||
|
|
@ -74,21 +74,21 @@ abstract class DataGrid
|
|||
/**
|
||||
* Works on selection of values index column as comma separated list as response
|
||||
* to your endpoint set as route.
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $massActions = [];
|
||||
|
||||
/**
|
||||
* Parsed value of the url parameters
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $parse;
|
||||
|
||||
/**
|
||||
* To show mass action or not.
|
||||
*
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enableMassAction = false;
|
||||
|
|
@ -100,14 +100,14 @@ abstract class DataGrid
|
|||
|
||||
/**
|
||||
* paginate the collection or not
|
||||
*
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $paginate = true;
|
||||
|
||||
/**
|
||||
* If paginated then value of pagination.
|
||||
*
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $itemsPerPage = 10;
|
||||
|
|
@ -164,8 +164,11 @@ abstract class DataGrid
|
|||
10 => "lock",
|
||||
];
|
||||
|
||||
/** @var string[] contains the keys for which extra filters to show */
|
||||
protected $extraFilters = [];
|
||||
|
||||
abstract public function prepareQueryBuilder();
|
||||
|
||||
|
||||
abstract public function addColumns();
|
||||
|
||||
/**
|
||||
|
|
@ -178,7 +181,7 @@ abstract class DataGrid
|
|||
|
||||
/**
|
||||
* Parse the URL and get it ready to be used.
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function parseUrl()
|
||||
|
|
@ -209,42 +212,47 @@ abstract class DataGrid
|
|||
/**
|
||||
* Add the index as alias of the column and use the column to make things happen
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $column
|
||||
* @param string $alias
|
||||
* @param string $column
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addFilter($alias, $column) {
|
||||
public function addFilter($alias, $column)
|
||||
{
|
||||
$this->filterMap[$alias] = $column;
|
||||
|
||||
$this->enableFilterMap = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $column
|
||||
* @param string $column
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addColumn($column)
|
||||
{
|
||||
$this->fireEvent('add.column.before.'.$column['index']);
|
||||
$this->fireEvent('add.column.before.' . $column['index']);
|
||||
|
||||
array_push($this->columns, $column);
|
||||
$this->columns[] = $column;
|
||||
|
||||
$this->setCompleteColumnDetails($column);
|
||||
|
||||
$this->fireEvent('add.column.after.'.$column['index']);
|
||||
$this->fireEvent('add.column.after.' . $column['index']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $column
|
||||
* @param string $column
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCompleteColumnDetails($column)
|
||||
{
|
||||
array_push($this->completeColumnDetails, $column);
|
||||
$this->completeColumnDetails[] = $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Database\Query\Builder $queryBuilder
|
||||
* @param \Illuminate\Database\Query\Builder $queryBuilder
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setQueryBuilder($queryBuilder)
|
||||
|
|
@ -253,7 +261,8 @@ abstract class DataGrid
|
|||
}
|
||||
|
||||
/**
|
||||
* @param array $action
|
||||
* @param array $action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addAction($action)
|
||||
|
|
@ -266,7 +275,7 @@ abstract class DataGrid
|
|||
$eventName = null;
|
||||
}
|
||||
|
||||
$this->fireEvent('action.before.'.$eventName);
|
||||
$this->fireEvent('action.before.' . $eventName);
|
||||
|
||||
array_push($this->actions, $action);
|
||||
|
||||
|
|
@ -276,7 +285,8 @@ abstract class DataGrid
|
|||
}
|
||||
|
||||
/**
|
||||
* @param array $massAction
|
||||
* @param array $massAction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addMassAction($massAction)
|
||||
|
|
@ -291,7 +301,7 @@ abstract class DataGrid
|
|||
|
||||
$this->fireEvent('mass.action.before.' . $eventName);
|
||||
|
||||
array_push($this->massActions, $massAction);
|
||||
$this->massActions[] = $massAction;
|
||||
|
||||
$this->enableMassAction = true;
|
||||
|
||||
|
|
@ -306,21 +316,24 @@ abstract class DataGrid
|
|||
$parsedUrl = $this->parseUrl();
|
||||
|
||||
foreach ($parsedUrl as $key => $value) {
|
||||
if ( $key == 'locale') {
|
||||
if ( ! is_array($value)) {
|
||||
if ($key === 'locale') {
|
||||
if (! is_array($value)) {
|
||||
unset($parsedUrl[$key]);
|
||||
}
|
||||
} elseif ( ! is_array($value)) {
|
||||
} elseif (! is_array($value)) {
|
||||
unset($parsedUrl[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($parsedUrl)) {
|
||||
$filteredOrSortedCollection = $this->sortOrFilterCollection($this->collection = $this->queryBuilder, $parsedUrl);
|
||||
$filteredOrSortedCollection = $this->sortOrFilterCollection($this->collection = $this->queryBuilder,
|
||||
$parsedUrl);
|
||||
|
||||
if ($this->paginate) {
|
||||
if ($this->itemsPerPage > 0)
|
||||
return $filteredOrSortedCollection->orderBy($this->index, $this->sortOrder)->paginate($this->itemsPerPage)->appends(request()->except('page'));
|
||||
if ($this->itemsPerPage > 0) {
|
||||
return $filteredOrSortedCollection->orderBy($this->index,
|
||||
$this->sortOrder)->paginate($this->itemsPerPage)->appends(request()->except('page'));
|
||||
}
|
||||
} else {
|
||||
return $filteredOrSortedCollection->orderBy($this->index, $this->sortOrder)->get();
|
||||
}
|
||||
|
|
@ -328,7 +341,8 @@ abstract class DataGrid
|
|||
|
||||
if ($this->paginate) {
|
||||
if ($this->itemsPerPage > 0) {
|
||||
$this->collection = $this->queryBuilder->orderBy($this->index, $this->sortOrder)->paginate($this->itemsPerPage)->appends(request()->except('page'));
|
||||
$this->collection = $this->queryBuilder->orderBy($this->index,
|
||||
$this->sortOrder)->paginate($this->itemsPerPage)->appends(request()->except('page'));
|
||||
}
|
||||
} else {
|
||||
$this->collection = $this->queryBuilder->orderBy($this->index, $this->sortOrder)->get();
|
||||
|
|
@ -340,30 +354,32 @@ abstract class DataGrid
|
|||
/**
|
||||
* To find the alias of the column and by taking the column name.
|
||||
*
|
||||
* @param array $columnAlias
|
||||
* @param array $columnAlias
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findColumnType($columnAlias)
|
||||
{
|
||||
foreach($this->completeColumnDetails as $column) {
|
||||
if($column['index'] == $columnAlias) {
|
||||
foreach ($this->completeColumnDetails as $column) {
|
||||
if ($column['index'] == $columnAlias) {
|
||||
return [$column['type'], $column['index']];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Support\Collection $collection
|
||||
* @param array $parseInfo
|
||||
* @param \Illuminate\Support\Collection $collection
|
||||
* @param array $parseInfo
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function sortOrFilterCollection($collection, $parseInfo)
|
||||
{
|
||||
foreach ($parseInfo as $key => $info) {
|
||||
foreach ($parseInfo as $key => $info) {
|
||||
$columnType = $this->findColumnType($key)[0] ?? null;
|
||||
$columnName = $this->findColumnType($key)[1] ?? null;
|
||||
|
||||
if ($key == "sort") {
|
||||
if ($key === "sort") {
|
||||
$count_keys = count(array_keys($info));
|
||||
|
||||
if ($count_keys > 1) {
|
||||
|
|
@ -376,7 +392,7 @@ abstract class DataGrid
|
|||
$columnName[1],
|
||||
array_values($info)[0]
|
||||
);
|
||||
} elseif ($key == "search") {
|
||||
} elseif ($key === "search") {
|
||||
$count_keys = count(array_keys($info));
|
||||
|
||||
if ($count_keys > 1) {
|
||||
|
|
@ -384,15 +400,16 @@ abstract class DataGrid
|
|||
}
|
||||
|
||||
if ($count_keys == 1) {
|
||||
$collection->where(function($collection) use($info) {
|
||||
$collection->where(function ($collection) use ($info) {
|
||||
foreach ($this->completeColumnDetails as $column) {
|
||||
if ($column['searchable'] == true) {
|
||||
if($this->enableFilterMap && isset($this->filterMap[$column['index']])) {
|
||||
$collection->orWhere($this->filterMap[$column['index']], 'like', '%'.$info['all'].'%');
|
||||
} elseif($this->enableFilterMap && !isset($this->filterMap[$column['index']])) {
|
||||
$collection->orWhere($column['index'], 'like', '%'.$info['all'].'%');
|
||||
}else {
|
||||
$collection->orWhere($column['index'], 'like', '%'.$info['all'].'%');
|
||||
if ($this->enableFilterMap && isset($this->filterMap[$column['index']])) {
|
||||
$collection->orWhere($this->filterMap[$column['index']], 'like',
|
||||
'%' . $info['all'] . '%');
|
||||
} elseif ($this->enableFilterMap && ! isset($this->filterMap[$column['index']])) {
|
||||
$collection->orWhere($column['index'], 'like', '%' . $info['all'] . '%');
|
||||
} else {
|
||||
$collection->orWhere($column['index'], 'like', '%' . $info['all'] . '%');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -400,36 +417,41 @@ abstract class DataGrid
|
|||
}
|
||||
} else {
|
||||
foreach ($this->completeColumnDetails as $column) {
|
||||
if($column['index'] == $columnName && !$column['filterable']) {
|
||||
if ($column['index'] === $columnName && ! $column['filterable']) {
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_keys($info)[0] == "like" || array_keys($info)[0] == "nlike") {
|
||||
if (array_keys($info)[0] === "like" || array_keys($info)[0] === "nlike") {
|
||||
foreach ($info as $condition => $filter_value) {
|
||||
if ($this->enableFilterMap && isset($this->filterMap[$columnName])) {
|
||||
$collection->where(
|
||||
$this->filterMap[$columnName],
|
||||
$this->operators[$condition],
|
||||
'%'.$filter_value.'%'
|
||||
'%' . $filter_value . '%'
|
||||
);
|
||||
} elseif ($this->enableFilterMap && ! isset($this->filterMap[$columnName])) {
|
||||
$collection->where(
|
||||
$columnName,
|
||||
$this->operators[$condition],
|
||||
'%'.$filter_value.'%'
|
||||
'%' . $filter_value . '%'
|
||||
);
|
||||
} else {
|
||||
$collection->where(
|
||||
$columnName,
|
||||
$this->operators[$condition],
|
||||
'%'.$filter_value.'%'
|
||||
'%' . $filter_value . '%'
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($info as $condition => $filter_value) {
|
||||
if ($columnType == 'datetime') {
|
||||
|
||||
if ($condition === 'undefined') {
|
||||
$condition = '=';
|
||||
}
|
||||
|
||||
if ($columnType === 'datetime') {
|
||||
if ($this->enableFilterMap && isset($this->filterMap[$columnName])) {
|
||||
$collection->whereDate(
|
||||
$this->filterMap[$columnName],
|
||||
|
|
@ -456,7 +478,7 @@ abstract class DataGrid
|
|||
$this->operators[$condition],
|
||||
$filter_value
|
||||
);
|
||||
} elseif($this->enableFilterMap && !isset($this->filterMap[$columnName])) {
|
||||
} elseif ($this->enableFilterMap && ! isset($this->filterMap[$columnName])) {
|
||||
$collection->where(
|
||||
$columnName,
|
||||
$this->operators[$condition],
|
||||
|
|
@ -479,7 +501,8 @@ abstract class DataGrid
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function fireEvent($name)
|
||||
|
|
@ -524,6 +547,17 @@ abstract class DataGrid
|
|||
|
||||
$this->prepareQueryBuilder();
|
||||
|
||||
$necessaryExtraFilters = [];
|
||||
if (in_array('channels', $this->extraFilters)) {
|
||||
$necessaryExtraFilters['channels'] = core()->getAllChannels();
|
||||
}
|
||||
if (in_array('locales', $this->extraFilters)) {
|
||||
$necessaryExtraFilters['locales'] = core()->getAllLocales();
|
||||
}
|
||||
if (in_array('customer_groups', $this->extraFilters)) {
|
||||
$necessaryExtraFilters['customer_groups'] = core()->getAllCustomerGroups();
|
||||
}
|
||||
|
||||
return view('ui::datagrid.table')->with('results', [
|
||||
'records' => $this->getCollection(),
|
||||
'columns' => $this->completeColumnDetails,
|
||||
|
|
@ -533,7 +567,8 @@ abstract class DataGrid
|
|||
'enableMassActions' => $this->enableMassAction,
|
||||
'enableActions' => $this->enableAction,
|
||||
'paginated' => $this->paginate,
|
||||
'norecords' => trans('ui::app.datagrid.no-records'),
|
||||
'norecords' => __('ui::app.datagrid.no-records'),
|
||||
'extraFilters' => $necessaryExtraFilters
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,27 +12,90 @@
|
|||
|
||||
<div class="filter-left">
|
||||
<div class="search-filter">
|
||||
<input type="search" id="search-field" class="control" placeholder="{{ __('ui::app.datagrid.search') }}" v-model="searchValue" v-on:keyup.enter="searchCollection(searchValue)" />
|
||||
<input type="search" id="search-field" class="control"
|
||||
placeholder="{{ __('ui::app.datagrid.search') }}" v-model="searchValue"
|
||||
v-on:keyup.enter="searchCollection(searchValue)"/>
|
||||
|
||||
<div class="icon-wrapper">
|
||||
<span class="icon search-icon search-btn" v-on:click="searchCollection(searchValue)"></span>
|
||||
<span class="icon search-icon search-btn"
|
||||
v-on:click="searchCollection(searchValue)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-right">
|
||||
@if (isset($results['extraFilters']['channels']))
|
||||
<div class="dropdown-filters per-page">
|
||||
<div class="control-group">
|
||||
<select class="control" id="channel-switcher" name="channel"
|
||||
onchange="reloadPage('channel', this.value)">
|
||||
<option value="all" {{ ! isset($channel) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-channels') }}
|
||||
</option>
|
||||
@foreach ($results['extraFilters']['channels'] as $channelModel)
|
||||
<option
|
||||
value="{{ $channelModel->code }}"
|
||||
{{ (isset($channel) && ($channelModel->code) == $channel) ? 'selected' : '' }}>
|
||||
{{ $channelModel->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (isset($results['extraFilters']['locales']))
|
||||
<div class="dropdown-filters per-page">
|
||||
<div class="control-group">
|
||||
<select class="control" id="locale-switcher" name="locale"
|
||||
onchange="reloadPage('locale', this.value)">
|
||||
<option value="all" {{ ! isset($locale) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-locales') }}
|
||||
</option>
|
||||
@foreach ($results['extraFilters']['locales'] as $localeModel)
|
||||
<option
|
||||
value="{{ $localeModel->code }}" {{ (isset($locale) && ($localeModel->code) == $locale) ? 'selected' : '' }}>
|
||||
{{ $localeModel->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (isset($results['extraFilters']['customer_groups']))
|
||||
<div class="dropdown-filters per-page">
|
||||
<div class="control-group">
|
||||
<select class="control" id="customer-group-switcher" name="customer_group"
|
||||
onchange="reloadPage('customer_group', this.value)">
|
||||
<option value="all" {{ ! isset($customer_group) ? 'selected' : '' }}>
|
||||
{{ __('admin::app.admin.system.all-customer-groups') }}
|
||||
</option>
|
||||
@foreach ($results['extraFilters']['customer_groups'] as $customerGroupModel)
|
||||
<option
|
||||
value="{{ $customerGroupModel->id }}"
|
||||
{{ (isset($customer_group) && ($customerGroupModel->id) == $customer_group) ? 'selected' : '' }}>
|
||||
{{ $customerGroupModel->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="dropdown-filters per-page">
|
||||
<div class="control-group">
|
||||
<label class="per-page-label" for="perPage">
|
||||
{{ __('ui::app.datagrid.items-per-page') }}
|
||||
</label>
|
||||
|
||||
<select id="perPage" name="perPage" class="control" v-model="perPage" v-on:change="paginate">
|
||||
<option value="10"> 10 </option>
|
||||
<option value="20"> 20 </option>
|
||||
<option value="30"> 30 </option>
|
||||
<option value="40"> 40 </option>
|
||||
<option value="50"> 50 </option>
|
||||
<select id="perPage" name="perPage" class="control" v-model="perPage"
|
||||
v-on:change="paginate">
|
||||
<option value="10"> 10</option>
|
||||
<option value="20"> 20</option>
|
||||
<option value="30"> 30</option>
|
||||
<option value="40"> 40</option>
|
||||
<option value="50"> 50</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -49,7 +112,8 @@
|
|||
<ul>
|
||||
<li>
|
||||
<div class="control-group">
|
||||
<select class="filter-column-select control" v-model="filterColumn" v-on:click="getColumnOrAlias(filterColumn)">
|
||||
<select class="filter-column-select control" v-model="filterColumn"
|
||||
v-on:click="getColumnOrAlias(filterColumn)">
|
||||
<option selected disabled>{{ __('ui::app.datagrid.column') }}</option>
|
||||
@foreach($results['columns'] as $column)
|
||||
@if(isset($column['filterable']) && $column['filterable'])
|
||||
|
|
@ -66,7 +130,8 @@
|
|||
<li v-if='stringConditionSelect'>
|
||||
<div class="control-group">
|
||||
<select class="control" v-model="stringCondition">
|
||||
<option selected disabled>{{ __('ui::app.datagrid.condition') }}</option>
|
||||
<option selected
|
||||
disabled>{{ __('ui::app.datagrid.condition') }}</option>
|
||||
<option value="like">{{ __('ui::app.datagrid.contains') }}</option>
|
||||
<option value="nlike">{{ __('ui::app.datagrid.ncontains') }}</option>
|
||||
<option value="eq">{{ __('ui::app.datagrid.equals') }}</option>
|
||||
|
|
@ -78,7 +143,9 @@
|
|||
{{-- Response fields based on the type of columns to be filtered --}}
|
||||
<li v-if='stringCondition != null'>
|
||||
<div class="control-group">
|
||||
<input type="text" class="control response-string" placeholder="{{ __('ui::app.datagrid.value-here') }}" v-model="stringValue" />
|
||||
<input type="text" class="control response-string"
|
||||
placeholder="{{ __('ui::app.datagrid.value-here') }}"
|
||||
v-model="stringValue"/>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
|
@ -86,7 +153,8 @@
|
|||
<li v-if='numberConditionSelect'>
|
||||
<div class="control-group">
|
||||
<select class="control" v-model="numberCondition">
|
||||
<option selected disabled>{{ __('ui::app.datagrid.condition') }}</option>
|
||||
<option selected
|
||||
disabled>{{ __('ui::app.datagrid.condition') }}</option>
|
||||
<option value="eq">{{ __('ui::app.datagrid.equals') }}</option>
|
||||
<option value="neqs">{{ __('ui::app.datagrid.nequals') }}</option>
|
||||
<option value="gt">{{ __('ui::app.datagrid.greater') }}</option>
|
||||
|
|
@ -99,7 +167,9 @@
|
|||
|
||||
<li v-if='numberCondition != null'>
|
||||
<div class="control-group">
|
||||
<input type="number" class="control response-number" placeholder="{{ __('ui::app.datagrid.numeric-value-here') }}" v-model="numberValue"/>
|
||||
<input type="number" class="control response-number"
|
||||
placeholder="{{ __('ui::app.datagrid.numeric-value-here') }}"
|
||||
v-model="numberValue"/>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
|
@ -107,7 +177,8 @@
|
|||
<li v-if='booleanConditionSelect'>
|
||||
<div class="control-group">
|
||||
<select class="control" v-model="booleanCondition">
|
||||
<option selected disabled>{{ __('ui::app.datagrid.condition') }}</option>
|
||||
<option selected
|
||||
disabled>{{ __('ui::app.datagrid.condition') }}</option>
|
||||
<option value="eq">{{ __('ui::app.datagrid.equals') }}</option>
|
||||
<option value="neqs">{{ __('ui::app.datagrid.nequals') }}</option>
|
||||
</select>
|
||||
|
|
@ -128,7 +199,8 @@
|
|||
<li v-if='datetimeConditionSelect'>
|
||||
<div class="control-group">
|
||||
<select class="control" v-model="datetimeCondition">
|
||||
<option selected disabled>{{ __('ui::app.datagrid.condition') }}</option>
|
||||
<option selected
|
||||
disabled>{{ __('ui::app.datagrid.condition') }}</option>
|
||||
<option value="eq">{{ __('ui::app.datagrid.equals') }}</option>
|
||||
<option value="neqs">{{ __('ui::app.datagrid.nequals') }}</option>
|
||||
<option value="gt">{{ __('ui::app.datagrid.greater') }}</option>
|
||||
|
|
@ -146,7 +218,8 @@
|
|||
</div>
|
||||
</li>
|
||||
|
||||
<button class="btn btn-sm btn-primary apply-filter" v-on:click="getResponse">{{ __('ui::app.datagrid.apply') }}</button>
|
||||
<button class="btn btn-sm btn-primary apply-filter"
|
||||
v-on:click="getResponse">{{ __('ui::app.datagrid.apply') }}</button>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -154,13 +227,16 @@
|
|||
</div>
|
||||
|
||||
<div class="filtered-tags">
|
||||
<span class="filter-tag" v-if="filters.length > 0" v-for="filter in filters" style="text-transform: capitalize;">
|
||||
<span v-if="filter.column == 'sort'">@{{ filter.label }}</span>
|
||||
<span v-else-if="filter.column == 'search'">Search</span>
|
||||
<span v-else-if="filter.column == 'perPage'">perPage</span>
|
||||
<span class="filter-tag" v-if="filters.length > 0" v-for="filter in filters"
|
||||
style="text-transform: capitalize;">
|
||||
<span v-if="filter.column == 'perPage'">perPage</span>
|
||||
<span v-else>@{{ filter.label }}</span>
|
||||
|
||||
<span class="wrapper">
|
||||
<span class="wrapper" v-if="filter.prettyValue">
|
||||
@{{ filter.prettyValue }}
|
||||
<span class="icon cross-icon" v-on:click="removeFilter(filter)"></span>
|
||||
</span>
|
||||
<span class="wrapper" v-else>
|
||||
@{{ decodeURIComponent(filter.val) }}
|
||||
<span class="icon cross-icon" v-on:click="removeFilter(filter)"></span>
|
||||
</span>
|
||||
|
|
@ -181,7 +257,7 @@
|
|||
Vue.component('datagrid-filters', {
|
||||
template: '#datagrid-filters',
|
||||
|
||||
data: function() {
|
||||
data: function () {
|
||||
return {
|
||||
filterIndex: @json($results['index']),
|
||||
gridCurrentData: @json($results['records']),
|
||||
|
|
@ -208,7 +284,7 @@
|
|||
filters: [],
|
||||
columnOrAlias: '',
|
||||
type: null,
|
||||
columns : @json($results['columns']),
|
||||
columns: @json($results['columns']),
|
||||
stringCondition: null,
|
||||
booleanCondition: null,
|
||||
numberCondition: null,
|
||||
|
|
@ -222,15 +298,16 @@
|
|||
numberConditionSelect: false,
|
||||
datetimeConditionSelect: false,
|
||||
perPage: 10,
|
||||
extraFilters: @json($results['extraFilters']),
|
||||
}
|
||||
},
|
||||
|
||||
mounted: function() {
|
||||
mounted: function () {
|
||||
this.setParamsAndUrl();
|
||||
|
||||
if (this.filters.length) {
|
||||
for (let i = 0; i < this.filters.length; i++) {
|
||||
if (this.filters[i].column == 'perPage') {
|
||||
if (this.filters[i].column === 'perPage') {
|
||||
this.perPage = this.filters[i].val;
|
||||
}
|
||||
}
|
||||
|
|
@ -238,119 +315,134 @@
|
|||
},
|
||||
|
||||
methods: {
|
||||
getColumnOrAlias: function(columnOrAlias) {
|
||||
getColumnOrAlias: function (columnOrAlias) {
|
||||
this.columnOrAlias = columnOrAlias;
|
||||
|
||||
for(column in this.columns) {
|
||||
if (this.columns[column].index == this.columnOrAlias) {
|
||||
for (column in this.columns) {
|
||||
if (this.columns[column].index === this.columnOrAlias) {
|
||||
this.type = this.columns[column].type;
|
||||
|
||||
if (this.type == 'string') {
|
||||
this.stringConditionSelect = true;
|
||||
this.datetimeConditionSelect = false;
|
||||
this.booleanConditionSelect = false;
|
||||
this.numberConditionSelect = false;
|
||||
switch (this.type) {
|
||||
|
||||
this.nullify();
|
||||
} else if (this.type == 'datetime') {
|
||||
this.datetimeConditionSelect = true;
|
||||
this.stringConditionSelect = false;
|
||||
this.booleanConditionSelect = false;
|
||||
this.numberConditionSelect = false;
|
||||
case 'string': {
|
||||
this.stringConditionSelect = true;
|
||||
this.datetimeConditionSelect = false;
|
||||
this.booleanConditionSelect = false;
|
||||
this.numberConditionSelect = false;
|
||||
|
||||
this.nullify();
|
||||
} else if (this.type == 'boolean') {
|
||||
this.booleanConditionSelect = true;
|
||||
this.datetimeConditionSelect = false;
|
||||
this.stringConditionSelect = false;
|
||||
this.numberConditionSelect = false;
|
||||
this.nullify();
|
||||
break;
|
||||
}
|
||||
case 'datetime': {
|
||||
this.datetimeConditionSelect = true;
|
||||
this.stringConditionSelect = false;
|
||||
this.booleanConditionSelect = false;
|
||||
this.numberConditionSelect = false;
|
||||
|
||||
this.nullify();
|
||||
} else if (this.type == 'number') {
|
||||
this.numberConditionSelect = true;
|
||||
this.booleanConditionSelect = false;
|
||||
this.datetimeConditionSelect = false;
|
||||
this.stringConditionSelect = false;
|
||||
this.nullify();
|
||||
break;
|
||||
}
|
||||
case 'boolean': {
|
||||
this.booleanConditionSelect = true;
|
||||
this.datetimeConditionSelect = false;
|
||||
this.stringConditionSelect = false;
|
||||
this.numberConditionSelect = false;
|
||||
|
||||
this.nullify();
|
||||
} else if (this.type == 'price') {
|
||||
this.numberConditionSelect = true;
|
||||
this.booleanConditionSelect = false;
|
||||
this.datetimeConditionSelect = false;
|
||||
this.stringConditionSelect = false;
|
||||
this.nullify();
|
||||
break;
|
||||
}
|
||||
case 'number': {
|
||||
this.numberConditionSelect = true;
|
||||
this.booleanConditionSelect = false;
|
||||
this.datetimeConditionSelect = false;
|
||||
this.stringConditionSelect = false;
|
||||
|
||||
this.nullify();
|
||||
break;
|
||||
}
|
||||
case 'price': {
|
||||
this.numberConditionSelect = true;
|
||||
this.booleanConditionSelect = false;
|
||||
this.datetimeConditionSelect = false;
|
||||
this.stringConditionSelect = false;
|
||||
|
||||
this.nullify();
|
||||
break;
|
||||
}
|
||||
|
||||
this.nullify();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
nullify: function() {
|
||||
nullify: function () {
|
||||
this.stringCondition = null;
|
||||
this.datetimeCondition = null;
|
||||
this.booleanCondition = null;
|
||||
this.numberCondition = null;
|
||||
},
|
||||
|
||||
getResponse: function() {
|
||||
getResponse: function () {
|
||||
label = '';
|
||||
|
||||
for(colIndex in this.columns) {
|
||||
if(this.columns[colIndex].index == this.columnOrAlias) {
|
||||
for (let colIndex in this.columns) {
|
||||
if (this.columns[colIndex].index == this.columnOrAlias) {
|
||||
label = this.columns[colIndex].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.type == 'string' && this.stringValue != null) {
|
||||
if (this.type === 'string' && this.stringValue !== null) {
|
||||
this.formURL(this.columnOrAlias, this.stringCondition, encodeURIComponent(this.stringValue), label)
|
||||
} else if (this.type == 'number') {
|
||||
} else if (this.type === 'number') {
|
||||
indexConditions = true;
|
||||
|
||||
if (this.filterIndex == this.columnOrAlias && (this.numberValue == 0 || this.numberValue < 0)) {
|
||||
if (this.filterIndex === this.columnOrAlias
|
||||
&& (this.numberValue === 0 || this.numberValue < 0)) {
|
||||
indexConditions = false;
|
||||
|
||||
alert('{{__('ui::app.datagrid.zero-index')}}');
|
||||
}
|
||||
|
||||
if(indexConditions)
|
||||
if (indexConditions) {
|
||||
this.formURL(this.columnOrAlias, this.numberCondition, this.numberValue, label);
|
||||
} else if (this.type == 'boolean') {
|
||||
}
|
||||
} else if (this.type === 'boolean') {
|
||||
this.formURL(this.columnOrAlias, this.booleanCondition, this.booleanValue, label);
|
||||
} else if (this.type == 'datetime') {
|
||||
} else if (this.type === 'datetime') {
|
||||
this.formURL(this.columnOrAlias, this.datetimeCondition, this.datetimeValue, label);
|
||||
} else if (this.type == 'price') {
|
||||
} else if (this.type === 'price') {
|
||||
this.formURL(this.columnOrAlias, this.numberCondition, this.numberValue, label);
|
||||
}
|
||||
},
|
||||
|
||||
sortCollection: function(alias) {
|
||||
label = '';
|
||||
sortCollection: function (alias) {
|
||||
let label = '';
|
||||
|
||||
for(colIndex in this.columns) {
|
||||
if(this.columns[colIndex].index == alias) {
|
||||
for (let colIndex in this.columns) {
|
||||
if (this.columns[colIndex].index === alias) {
|
||||
matched = 0;
|
||||
label = this.columns[colIndex].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.formURL("sort", alias, this.sortAsc, label);
|
||||
},
|
||||
|
||||
searchCollection: function(searchValue) {
|
||||
label = 'Search';
|
||||
|
||||
this.formURL("search", 'all', searchValue, label);
|
||||
searchCollection: function (searchValue) {
|
||||
this.formURL("search", 'all', searchValue, 'Search');
|
||||
},
|
||||
|
||||
// function triggered to check whether the query exists or not and then call the make filters from the url
|
||||
setParamsAndUrl: function() {
|
||||
setParamsAndUrl: function () {
|
||||
params = (new URL(window.location.href)).search;
|
||||
|
||||
if (params.slice(1, params.length).length > 0) {
|
||||
this.arrayFromUrl();
|
||||
}
|
||||
|
||||
for(id in this.massActions) {
|
||||
for (let id in this.massActions) {
|
||||
targetObj = {
|
||||
'type': this.massActions[id].type,
|
||||
'action': this.massActions[id].action
|
||||
|
|
@ -360,24 +452,24 @@
|
|||
|
||||
targetObj = {};
|
||||
|
||||
if (this.massActions[id].type == 'update') {
|
||||
if (this.massActions[id].type === 'update') {
|
||||
this.massActionValues = this.massActions[id].options;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
findCurrentSort: function() {
|
||||
for(i in this.filters) {
|
||||
if (this.filters[i].column == 'sort') {
|
||||
findCurrentSort: function () {
|
||||
for (let i in this.filters) {
|
||||
if (this.filters[i].column === 'sort') {
|
||||
this.currentSort = this.filters[i].val;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
changeMassActionTarget: function() {
|
||||
if (this.massActionType == 'delete') {
|
||||
for(i in this.massActionTargets) {
|
||||
if (this.massActionTargets[i].type == 'delete') {
|
||||
changeMassActionTarget: function () {
|
||||
if (this.massActionType === 'delete') {
|
||||
for (let i in this.massActionTargets) {
|
||||
if (this.massActionTargets[i].type === 'delete') {
|
||||
this.massActionTarget = this.massActionTargets[i].action;
|
||||
|
||||
break;
|
||||
|
|
@ -385,9 +477,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (this.massActionType == 'update') {
|
||||
for(i in this.massActionTargets) {
|
||||
if (this.massActionTargets[i].type == 'update') {
|
||||
if (this.massActionType === 'update') {
|
||||
for (let i in this.massActionTargets) {
|
||||
if (this.massActionTargets[i].type === 'update') {
|
||||
this.massActionTarget = this.massActionTargets[i].action;
|
||||
|
||||
break;
|
||||
|
|
@ -399,26 +491,28 @@
|
|||
},
|
||||
|
||||
//make array of filters, sort and search
|
||||
formURL: function(column, condition, response, label) {
|
||||
formURL: function (column, condition, response, label) {
|
||||
var obj = {};
|
||||
|
||||
if (column == "" || condition == "" || response == "" || column == null || condition == null || response == null) {
|
||||
if (column === "" || condition === "" || response === ""
|
||||
|| column === null || condition === null || response === null) {
|
||||
alert('{{ __('ui::app.datagrid.filter-fields-missing') }}');
|
||||
|
||||
return false;
|
||||
} else {
|
||||
if (this.filters.length > 0) {
|
||||
if (column != "sort" && column != "search") {
|
||||
filterRepeated = 0;
|
||||
|
||||
for(j = 0; j < this.filters.length; j++) {
|
||||
if (this.filters[j].column == column) {
|
||||
if (this.filters[j].cond == condition && this.filters[j].val == response) {
|
||||
filterRepeated = 1;
|
||||
if (this.filters.length > 0) {
|
||||
if (column !== "sort" && column !== "search") {
|
||||
let filterRepeated = false;
|
||||
|
||||
for (let j = 0; j < this.filters.length; j++) {
|
||||
if (this.filters[j].column === column) {
|
||||
if (this.filters[j].cond === condition && this.filters[j].val === response) {
|
||||
filterRepeated = true;
|
||||
|
||||
return false;
|
||||
} else if(this.filters[j].cond == condition && this.filters[j].val != response) {
|
||||
filterRepeated = 1;
|
||||
} else if (this.filters[j].cond === condition && this.filters[j].val !== response) {
|
||||
filterRepeated = true;
|
||||
|
||||
this.filters[j].val = response;
|
||||
|
||||
|
|
@ -427,7 +521,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (filterRepeated == 0) {
|
||||
if (filterRepeated === false) {
|
||||
obj.column = column;
|
||||
obj.cond = condition;
|
||||
obj.val = response;
|
||||
|
|
@ -440,15 +534,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (column == "sort") {
|
||||
sort_exists = 0;
|
||||
if (column === "sort") {
|
||||
let sort_exists = false;
|
||||
|
||||
for (j = 0; j < this.filters.length; j++) {
|
||||
if (this.filters[j].column == "sort") {
|
||||
if (this.filters[j].column == column && this.filters[j].cond == condition) {
|
||||
for (let j = 0; j < this.filters.length; j++) {
|
||||
if (this.filters[j].column === "sort") {
|
||||
if (this.filters[j].column === column && this.filters[j].cond === condition) {
|
||||
this.findCurrentSort();
|
||||
|
||||
if (this.currentSort == "asc") {
|
||||
if (this.currentSort === "asc") {
|
||||
this.filters[j].column = column;
|
||||
this.filters[j].cond = condition;
|
||||
this.filters[j].val = this.sortDesc;
|
||||
|
|
@ -470,12 +564,12 @@
|
|||
this.makeURL();
|
||||
}
|
||||
|
||||
sort_exists = 1;
|
||||
sort_exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sort_exists == 0) {
|
||||
if (this.currentSort == null)
|
||||
if (sort_exists === false) {
|
||||
if (this.currentSort === null)
|
||||
this.currentSort = this.sortAsc;
|
||||
|
||||
obj.column = column;
|
||||
|
|
@ -491,30 +585,30 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (column == "search") {
|
||||
search_found = 0;
|
||||
if (column === "search") {
|
||||
let search_found = false;
|
||||
|
||||
for(j = 0; j < this.filters.length; j++) {
|
||||
if (this.filters[j].column == "search") {
|
||||
for (let j = 0; j < this.filters.length; j++) {
|
||||
if (this.filters[j].column === "search") {
|
||||
this.filters[j].column = column;
|
||||
this.filters[j].cond = condition;
|
||||
this.filters[j].val = response;
|
||||
this.filters[j].val = encodeURIComponent(response);
|
||||
this.filters[j].label = label;
|
||||
|
||||
this.makeURL();
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0;j < this.filters.length;j++) {
|
||||
if (this.filters[j].column == "search") {
|
||||
search_found = 1;
|
||||
for (let j = 0; j < this.filters.length; j++) {
|
||||
if (this.filters[j].column === "search") {
|
||||
search_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (search_found == 0) {
|
||||
if (search_found === false) {
|
||||
obj.column = column;
|
||||
obj.cond = condition;
|
||||
obj.val = response;
|
||||
obj.val = encodeURIComponent(response);
|
||||
obj.label = label;
|
||||
|
||||
this.filters.push(obj);
|
||||
|
|
@ -527,7 +621,7 @@
|
|||
} else {
|
||||
obj.column = column;
|
||||
obj.cond = condition;
|
||||
obj.val = response;
|
||||
obj.val = encodeURIComponent(response);
|
||||
obj.label = label;
|
||||
|
||||
this.filters.push(obj);
|
||||
|
|
@ -540,10 +634,10 @@
|
|||
},
|
||||
|
||||
// make the url from the array and redirect
|
||||
makeURL: function() {
|
||||
makeURL: function () {
|
||||
newParams = '';
|
||||
|
||||
for(i = 0; i < this.filters.length; i++) {
|
||||
for(let i = 0; i < this.filters.length; i++) {
|
||||
if (this.filters[i].column == 'status' || this.filters[i].column == 'value_per_locale' || this.filters[i].column == 'value_per_channel' || this.filters[i].column == 'is_unique') {
|
||||
if (this.filters[i].val.includes("True")) {
|
||||
this.filters[i].val = 1;
|
||||
|
|
@ -552,11 +646,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
newParams = '?' + this.filters[i].column + '[' + this.filters[i].cond + ']' + '=' + this.filters[i].val;
|
||||
} else {
|
||||
newParams = newParams + '&' + this.filters[i].column + '[' + this.filters[i].cond + ']' + '=' + this.filters[i].val;
|
||||
let condition = '';
|
||||
if (this.filters[i].cond !== undefined) {
|
||||
condition = '[' + this.filters[i].cond + ']';
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
newParams = '?' + this.filters[i].column + condition + '=' + this.filters[i].val;
|
||||
} else {
|
||||
newParams = newParams + '&' + this.filters[i].column + condition + '=' + this.filters[i].val;
|
||||
}
|
||||
console.log('newParams', newParams);
|
||||
}
|
||||
|
||||
var uri = window.location.href.toString();
|
||||
|
|
@ -567,68 +667,93 @@
|
|||
},
|
||||
|
||||
//make the filter array from url after being redirected
|
||||
arrayFromUrl: function() {
|
||||
var obj = {};
|
||||
processedUrl = this.url.search.slice(1, this.url.length);
|
||||
splitted = [];
|
||||
moreSplitted = [];
|
||||
arrayFromUrl: function () {
|
||||
|
||||
let obj = {};
|
||||
const processedUrl = this.url.search.slice(1, this.url.length);
|
||||
let splitted = [];
|
||||
let moreSplitted = [];
|
||||
|
||||
splitted = processedUrl.split('&');
|
||||
|
||||
for(i = 0; i < splitted.length; i++) {
|
||||
for (let i = 0; i < splitted.length; i++) {
|
||||
moreSplitted.push(splitted[i].split('='));
|
||||
}
|
||||
|
||||
for(i = 0; i < moreSplitted.length; i++) {
|
||||
col = moreSplitted[i][0].replace(']', '').split('[')[0];
|
||||
cond = moreSplitted[i][0].replace(']', '').split('[')[1]
|
||||
val = moreSplitted[i][1];
|
||||
for (let i = 0; i < moreSplitted.length; i++) {
|
||||
const key = decodeURI(moreSplitted[i][0]);
|
||||
let value = decodeURI(moreSplitted[i][1]);
|
||||
|
||||
label = 'cannotfindthislabel';
|
||||
if (value.includes('+')) {
|
||||
value = value.replace('+', ' ');
|
||||
}
|
||||
|
||||
obj.column = col;
|
||||
obj.cond = cond;
|
||||
obj.val = val;
|
||||
obj.column = key.replace(']', '').split('[')[0];
|
||||
obj.cond = key.replace(']', '').split('[')[1]
|
||||
obj.val = value;
|
||||
|
||||
if(col == "sort") {
|
||||
label = '';
|
||||
|
||||
for(colIndex in this.columns) {
|
||||
if(this.columns[colIndex].index == obj.cond) {
|
||||
|
||||
obj.label = this.columns[colIndex].label;
|
||||
switch (obj.column) {
|
||||
case "search":
|
||||
obj.label = "Search";
|
||||
break;
|
||||
case "channel":
|
||||
obj.label = "Channel";
|
||||
if ('channels' in this.extraFilters) {
|
||||
obj.prettyValue = this.extraFilters['channels'].find(channel => channel.code === obj.val).name
|
||||
}
|
||||
}
|
||||
} else if (col == "search") {
|
||||
obj.label = 'Search';
|
||||
} else {
|
||||
obj.label = '';
|
||||
break;
|
||||
case "locale":
|
||||
obj.label = "Locale";
|
||||
if ('locales' in this.extraFilters) {
|
||||
obj.prettyValue = this.extraFilters['locales'].find(locale => locale.code === obj.val).name
|
||||
}
|
||||
break;
|
||||
case "customer_group":
|
||||
obj.label = "Customer Group";
|
||||
if ('customer_groups' in this.extraFilters) {
|
||||
obj.prettyValue = this.extraFilters['customer_groups'].find(customer_group => customer_group.id === parseInt(obj.val, 10)).name
|
||||
}
|
||||
break;
|
||||
case "sort":
|
||||
for (let colIndex in this.columns) {
|
||||
if (this.columns[colIndex].index === obj.cond) {
|
||||
obj.label = this.columns[colIndex].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (let colIndex in this.columns) {
|
||||
if (this.columns[colIndex].index === obj.column) {
|
||||
obj.label = this.columns[colIndex].label;
|
||||
|
||||
for(colIndex in this.columns) {
|
||||
if (this.columns[colIndex].index == obj.column) {
|
||||
obj.label = this.columns[colIndex].label;
|
||||
|
||||
if (this.columns[colIndex].type == 'boolean') {
|
||||
if (obj.val == 1) {
|
||||
obj.val = '{{ __('ui::app.datagrid.true') }}';
|
||||
} else {
|
||||
obj.val = '{{ __('ui::app.datagrid.false') }}';
|
||||
if (this.columns[colIndex].type === 'boolean') {
|
||||
console.log('obj.val', obj.val);
|
||||
if (obj.val === '1') {
|
||||
obj.val = '{{ __('ui::app.datagrid.true') }}';
|
||||
} else {
|
||||
obj.val = '{{ __('ui::app.datagrid.false') }}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (col != undefined && cond != undefined && val != undefined)
|
||||
if (obj.column !== undefined && obj.val !== undefined) {
|
||||
this.filters.push(obj);
|
||||
console.log('pushed');
|
||||
}
|
||||
|
||||
obj = {};
|
||||
}
|
||||
},
|
||||
|
||||
removeFilter: function(filter) {
|
||||
for(i in this.filters) {
|
||||
if (this.filters[i].col == filter.col && this.filters[i].cond == filter.cond && this.filters[i].val == filter.val) {
|
||||
removeFilter: function (filter) {
|
||||
for (let i in this.filters) {
|
||||
if (this.filters[i].col === filter.col
|
||||
&& this.filters[i].cond === filter.cond
|
||||
&& this.filters[i].val === filter.val) {
|
||||
this.filters.splice(i, 1);
|
||||
|
||||
this.makeURL();
|
||||
|
|
@ -637,10 +762,10 @@
|
|||
},
|
||||
|
||||
//triggered when any select box is clicked in the datagrid
|
||||
select: function() {
|
||||
select: function () {
|
||||
this.allSelected = false;
|
||||
|
||||
if (this.dataIds.length == 0) {
|
||||
if (this.dataIds.length === 0) {
|
||||
this.massActionsToggle = false;
|
||||
this.massActionType = null;
|
||||
} else {
|
||||
|
|
@ -649,19 +774,20 @@
|
|||
},
|
||||
|
||||
//triggered when master checkbox is clicked
|
||||
selectAll: function() {
|
||||
selectAll: function () {
|
||||
this.dataIds = [];
|
||||
|
||||
this.massActionsToggle = true;
|
||||
|
||||
if (this.allSelected) {
|
||||
if (this.gridCurrentData.hasOwnProperty("data")) {
|
||||
for (currentData in this.gridCurrentData.data) {
|
||||
for (let currentData in this.gridCurrentData.data) {
|
||||
|
||||
i = 0;
|
||||
for(currentId in this.gridCurrentData.data[currentData]) {
|
||||
if (i==0)
|
||||
let i = 0;
|
||||
for (let currentId in this.gridCurrentData.data[currentData]) {
|
||||
if (i == 0) {
|
||||
this.dataIds.push(this.gridCurrentData.data[currentData][this.filterIndex]);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
|
@ -669,9 +795,9 @@
|
|||
} else {
|
||||
for (currentData in this.gridCurrentData) {
|
||||
|
||||
i = 0;
|
||||
for(currentId in this.gridCurrentData[currentData]) {
|
||||
if (i==0)
|
||||
let i = 0;
|
||||
for (let currentId in this.gridCurrentData[currentData]) {
|
||||
if (i === 0)
|
||||
this.dataIds.push(this.gridCurrentData[currentData][currentId]);
|
||||
|
||||
i++;
|
||||
|
|
@ -681,16 +807,16 @@
|
|||
}
|
||||
},
|
||||
|
||||
doAction: function(e) {
|
||||
doAction: function (e) {
|
||||
var element = e.currentTarget;
|
||||
|
||||
if (confirm('{{__('ui::app.datagrid.massaction.delete') }}')) {
|
||||
axios.post(element.getAttribute('data-action'), {
|
||||
_token : element.getAttribute('data-token'),
|
||||
_method : element.getAttribute('data-method')
|
||||
}).then(function(response) {
|
||||
_token: element.getAttribute('data-token'),
|
||||
_method: element.getAttribute('data-method')
|
||||
}).then(function (response) {
|
||||
this.result = response;
|
||||
|
||||
|
||||
if (response.data.redirect) {
|
||||
window.location.href = response.data.redirect;
|
||||
} else {
|
||||
|
|
@ -706,13 +832,13 @@
|
|||
}
|
||||
},
|
||||
|
||||
captureColumn: function(id) {
|
||||
captureColumn: function (id) {
|
||||
element = document.getElementById(id);
|
||||
|
||||
console.log(element.innerHTML);
|
||||
},
|
||||
|
||||
removeMassActions: function() {
|
||||
removeMassActions: function () {
|
||||
this.dataIds = [];
|
||||
|
||||
this.massActionsToggle = false;
|
||||
|
|
@ -722,14 +848,14 @@
|
|||
this.massActionType = null;
|
||||
},
|
||||
|
||||
paginate: function(e) {
|
||||
paginate: function (e) {
|
||||
for (let i = 0; i < this.filters.length; i++) {
|
||||
if (this.filters[i].column == 'perPage') {
|
||||
this.filters.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.filters.push({"column":"perPage","cond":"eq","val": e.target.value});
|
||||
this.filters.push({"column": "perPage", "cond": "eq", "val": e.target.value});
|
||||
|
||||
this.makeURL();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,4 +126,13 @@ class FunctionalTester extends \Codeception\Actor
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function useDefaultTheme(): void
|
||||
{
|
||||
$channel = core()->getCurrentChannel();
|
||||
|
||||
if ($channel->theme !== 'default') {
|
||||
$channel->update(['theme' => 'default']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ namespace Tests\Functional\Checkout\Cart;
|
|||
|
||||
use FunctionalTester;
|
||||
use Webkul\Core\Helpers\Laravel5Helper;
|
||||
use Cart;
|
||||
|
||||
class CartCest
|
||||
{
|
||||
|
|
@ -12,7 +11,7 @@ class CartCest
|
|||
public $productWithQuantityBox;
|
||||
public $productWithoutQuantityBox;
|
||||
|
||||
public function _before(FunctionalTester $I)
|
||||
public function _before(FunctionalTester $I): void
|
||||
{
|
||||
$productConfig = [
|
||||
'productAttributes' => [],
|
||||
|
|
@ -27,9 +26,11 @@ class CartCest
|
|||
$this->productWithoutQuantityBox = $I->haveProduct(Laravel5Helper::DOWNLOADABLE_PRODUCT, $productConfig);
|
||||
}
|
||||
|
||||
public function checkCartWithQuantityBox(FunctionalTester $I)
|
||||
public function checkCartWithQuantityBox(FunctionalTester $I): void
|
||||
{
|
||||
Cart::addProduct($this->productWithQuantityBox->id, [
|
||||
$I->useDefaultTheme();
|
||||
|
||||
cart()->addProduct($this->productWithQuantityBox->id, [
|
||||
'_token' => session('_token'),
|
||||
'product_id' => $this->productWithQuantityBox->id,
|
||||
'quantity' => 1,
|
||||
|
|
@ -39,9 +40,9 @@ class CartCest
|
|||
$I->seeElement('#update_cart_button');
|
||||
}
|
||||
|
||||
public function checkCartWithoutQuantityBox(FunctionalTester $I)
|
||||
public function checkCartWithoutQuantityBox(FunctionalTester $I): void
|
||||
{
|
||||
Cart::addProduct($this->productWithoutQuantityBox->id, [
|
||||
cart()->addProduct($this->productWithoutQuantityBox->id, [
|
||||
'_token' => session('_token'),
|
||||
'product_id' => $this->productWithoutQuantityBox->id,
|
||||
'links' => $this->productWithoutQuantityBox->downloadable_links->pluck('id')->all(),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\Functional\Customer;
|
||||
|
||||
use Faker\Factory;
|
||||
use Webkul\Customer\Models\Customer;
|
||||
use Webkul\Customer\Models\CustomerAddress;
|
||||
use FunctionalTester;
|
||||
|
|
@ -10,6 +11,11 @@ class CustomerCest
|
|||
{
|
||||
public $fields = [];
|
||||
|
||||
public function _before(FunctionalTester $I): void
|
||||
{
|
||||
$I->useDefaultTheme();
|
||||
}
|
||||
|
||||
public function updateCustomerProfile(FunctionalTester $I): void
|
||||
{
|
||||
$customer = $I->loginAsCustomer();
|
||||
|
|
@ -32,8 +38,8 @@ class CustomerCest
|
|||
|
||||
public function updateCustomerAddress(FunctionalTester $I): void
|
||||
{
|
||||
$I->wantTo('Instantiate a european faker factory to have the vat provider available');
|
||||
$faker = \Faker\Factory::create('at_AT');
|
||||
// Instantiate a european faker factory to have the vat provider available
|
||||
$faker = Factory::create('at_AT');
|
||||
|
||||
$formCssSelector = '#customer-address-form';
|
||||
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@ use Webkul\Core\Helpers\Laravel5Helper;
|
|||
|
||||
class GuestCheckoutCest
|
||||
{
|
||||
private $faker,
|
||||
$productNoGuestCheckout, $productGuestCheckout;
|
||||
private $productNoGuestCheckout, $productGuestCheckout;
|
||||
|
||||
function _before(FunctionalTester $I)
|
||||
{
|
||||
$I->useDefaultTheme();
|
||||
|
||||
$this->faker = Factory::create();
|
||||
$faker = Factory::create();
|
||||
|
||||
$pConfigDefault = [
|
||||
'productInventory' => ['qty' => $this->faker->numberBetween(1, 1000)],
|
||||
'productInventory' => ['qty' => $faker->numberBetween(1, 1000)],
|
||||
'attributeValues' => [
|
||||
'status' => true,
|
||||
'new' => 1,
|
||||
|
|
@ -27,7 +27,7 @@ class GuestCheckoutCest
|
|||
],
|
||||
];
|
||||
$pConfigGuestCheckout = [
|
||||
'productInventory' => ['qty' => $this->faker->numberBetween(1, 1000)],
|
||||
'productInventory' => ['qty' => $faker->numberBetween(1, 1000)],
|
||||
'attributeValues' => [
|
||||
'status' => true,
|
||||
'new' => 1,
|
||||
|
|
@ -72,8 +72,8 @@ class GuestCheckoutCest
|
|||
$I->click(__('shop::app.checkout.cart.proceed-to-checkout'),
|
||||
'//a[@href="' . route('shop.checkout.onepage.index') . '"]');
|
||||
$I->seeCurrentRouteIs($example['expectedRoute']);
|
||||
$cart = Cart::getCart();
|
||||
$I->assertTrue(Cart::removeItem($cart->items[0]->id));
|
||||
$cart = cart()->getCart();
|
||||
$I->assertTrue(cart()->removeItem($cart->items[0]->id));
|
||||
}
|
||||
|
||||
protected function guestCheckoutProvider(): array
|
||||
|
|
|
|||
Loading…
Reference in New Issue