mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
List views (#2609)
* Wire up Create Entity Button to create route * Refactor permissions, we must also ensure the user company id and entity id matches at the Gate:: * Add translations for Status filters * Bug fix for initial list view not displaying * Apply actions to menu for list items * Wire up list view actions, individual * Place permission filters on datatable lists
This commit is contained in:
parent
cc53d08b4d
commit
0c1fc0d904
@ -8,8 +8,6 @@ use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ClientDatatable extends EntityDatatable
|
||||
{
|
||||
@ -28,7 +26,7 @@ class ClientDatatable extends EntityDatatable
|
||||
*/
|
||||
public function query(Request $request, int $company_id)
|
||||
{
|
||||
$data = $this->filter->apply($company_id)->paginate($request->input('per_page'));
|
||||
$data = $this->filter->apply($company_id, auth()->user())->paginate($request->input('per_page'));
|
||||
|
||||
return response()->json($this->buildActionColumn($data), 200);
|
||||
|
||||
@ -58,9 +56,7 @@ class ClientDatatable extends EntityDatatable
|
||||
|
||||
}
|
||||
*/
|
||||
if ($userId) {
|
||||
$query->where('clients.user_id', '=', $userId);
|
||||
}
|
||||
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ use Illuminate\Support\Collection;
|
||||
trait MakesActionMenu
|
||||
{
|
||||
/**
|
||||
* Returns all possible datatable actions
|
||||
* Returns all possible datatable actions
|
||||
* this list will be the single source of truth
|
||||
* for all Select Actions
|
||||
*
|
||||
* @return Collection collection instance of action items
|
||||
*/
|
||||
@ -26,24 +28,6 @@ trait MakesActionMenu
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the user permissions against the collection and returns
|
||||
* a Collection of available actions\.
|
||||
*
|
||||
* @param Collection $actions collection of possible actions
|
||||
* @param bool $isAdmin boolean defining if user is an administrator
|
||||
* @return Collection collection of filtered actions
|
||||
*/
|
||||
private function checkPermissions(Collection $actions, array $permissions, bool $is_admin) :Collection
|
||||
{
|
||||
|
||||
if($is_admin === TRUE)
|
||||
return $actions;
|
||||
|
||||
return $actions->whereIn('permission', $permissions);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the main actions collection down to the requested
|
||||
* actions for this menu
|
||||
@ -57,5 +41,24 @@ trait MakesActionMenu
|
||||
{
|
||||
|
||||
return $this->checkPermissions($this->actions()->whereIn('action', $actions), $permissions, $isAdmin);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the user permissions against the collection and returns
|
||||
* a Collection of available actions\.
|
||||
*
|
||||
* @param Collection $actions collection of possible actions
|
||||
* @param bool $isAdmin boolean defining if user is an administrator
|
||||
* @return Collection collection of filtered actions
|
||||
*/
|
||||
private function checkPermissions(Collection $actions, array $permissions, bool $is_admin) :Collection
|
||||
{
|
||||
|
||||
if($is_admin === TRUE)
|
||||
return $actions;
|
||||
|
||||
return $actions->whereIn('permission', $permissions);
|
||||
|
||||
}
|
||||
}
|
@ -2,9 +2,11 @@
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
/**
|
||||
* ClientFilters
|
||||
@ -114,7 +116,7 @@ class ClientFilters extends QueryFilters
|
||||
* @param int company_id
|
||||
* @return Illuminate\Database\Query\Builder
|
||||
*/
|
||||
public function baseQuery(int $company_id) : Builder
|
||||
public function baseQuery(int $company_id, User $user) : Builder
|
||||
{
|
||||
$query = DB::table('clients')
|
||||
->join('companies', 'companies.id', '=', 'clients.company_id')
|
||||
@ -144,6 +146,15 @@ class ClientFilters extends QueryFilters
|
||||
'clients.id_number'
|
||||
);
|
||||
|
||||
/**
|
||||
* If the user does not have permissions to view all invoices
|
||||
* limit the user to only the invoices they have created
|
||||
*/
|
||||
if (Gate::denies('view-list', Client::class)) {
|
||||
$query->where('clients.user_id', '=', $user->id);
|
||||
}
|
||||
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,9 @@
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
abstract class QueryFilters
|
||||
{
|
||||
@ -44,9 +43,9 @@ abstract class QueryFilters
|
||||
* @param Builder $builder
|
||||
* @return Builder
|
||||
*/
|
||||
public function apply(int $company_id)
|
||||
public function apply(int $company_id, User $user)
|
||||
{
|
||||
$this->builder = $this->baseQuery($company_id);
|
||||
$this->builder = $this->baseQuery($company_id, $user);
|
||||
|
||||
foreach ($this->filters() as $name => $value) {
|
||||
if (! method_exists($this, $name)) {
|
||||
|
@ -16,6 +16,7 @@ use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesMenu;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Yajra\DataTables\Facades\DataTables;
|
||||
use Yajra\DataTables\Html\Builder;
|
||||
@ -169,7 +170,7 @@ class ClientController extends Controller
|
||||
$action = request()->input('action');
|
||||
$ids = request()->input('ids');
|
||||
|
||||
$clients = Client::find($ids);
|
||||
$clients = Client::withTrashed()->find($ids);
|
||||
|
||||
$clients->each(function ($client, $key) use($action){
|
||||
ActionEntity::dispatchNow($client, $action);
|
||||
|
@ -146,6 +146,12 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
return collect($this->permissions())->flatten();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if permissions exist in the map
|
||||
*
|
||||
* @param string permission
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasPermission($permission) : bool
|
||||
{
|
||||
return $this->permissionsFlat()->contains($permission);
|
||||
|
@ -19,7 +19,7 @@ class ClientPolicy extends EntityPolicy
|
||||
*/
|
||||
public function create(User $user) : bool
|
||||
{
|
||||
return $user->hasPermission('create_client');
|
||||
return $user->isAdmin() || $user->hasPermission('create_client');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ class EntityPolicy
|
||||
* Fires before any of the custom policy methods
|
||||
*
|
||||
* Only fires if true, if false we continue.....
|
||||
*
|
||||
* Do not use this function!!!! We MUST also check company_id,
|
||||
*
|
||||
* @param User $user
|
||||
* @param $ability
|
||||
@ -21,12 +23,15 @@ class EntityPolicy
|
||||
*/
|
||||
public function before($user, $ability)
|
||||
{
|
||||
if($user->isAdmin())
|
||||
return true;
|
||||
//if($user->isAdmin())
|
||||
// return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has edit permissions
|
||||
* Checks if the user has edit permissions
|
||||
*
|
||||
* We MUST also check that the user can both edit a entity and also check the entity belongs to the users company!!!!!!
|
||||
*
|
||||
* @param User $user
|
||||
* @param $entity
|
||||
* @return bool
|
||||
@ -35,12 +40,16 @@ class EntityPolicy
|
||||
{
|
||||
$entity = strtolower(class_basename($entity));
|
||||
|
||||
return $user->hasPermission('edit_' . $entity) || $user->owns($entity);
|
||||
return ($user->isAdmin() && $entity->company_id == $user->company()->pivot->company_id)
|
||||
|| ($user->hasPermission('edit_' . $entity) && $entity->company_id == $user->company()->pivot->company_id)
|
||||
|| $user->owns($entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the user has view permissions
|
||||
*
|
||||
* We MUST also check that the user can both view a entity and also check the entity belongs to the users company!!!!!!
|
||||
* @param User $user
|
||||
* @param $entity
|
||||
* @return bool
|
||||
@ -49,6 +58,10 @@ class EntityPolicy
|
||||
{
|
||||
$entity = strtolower(class_basename($entity));
|
||||
|
||||
return $user->hasPermission('view_' . $entity) || $user->owns($entity);
|
||||
return ($user->isAdmin() && $entity->company_id == $user->company()->pivot->company_id)
|
||||
|| ($user->hasPermission('view_' . $entity) && $entity->company_id == $user->company()->pivot->company_id)
|
||||
|| $user->owns($entity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,15 @@ class AuthServiceProvider extends ServiceProvider
|
||||
return new MultiDatabaseUserProvider($this->app['hash'], $config['model']);
|
||||
|
||||
});
|
||||
|
||||
Gate::define('view-list', function ($user, $entity) {
|
||||
|
||||
$entity = strtolower(class_basename($entity));
|
||||
|
||||
return $user->hasPermission('view_' . $entity) || $user->isAdmin();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
3
public/js/client_edit.js
vendored
3
public/js/client_edit.js
vendored
@ -3946,6 +3946,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var form_1 = __importDefault(__webpack_require__("./resources/js/src/utils/form.ts"));
|
||||
var vue_1 = __importDefault(__webpack_require__("./node_modules/vue/dist/vue.common.js"));
|
||||
exports.default = {
|
||||
data: function () {
|
||||
return {
|
||||
@ -3973,7 +3974,7 @@ exports.default = {
|
||||
onSubmit: function () {
|
||||
var _this = this;
|
||||
this.form.put('/clients/' + this.hashed_id)
|
||||
.then(function (response) { return _this.$root.$refs.toastr.s("Saved client"); })
|
||||
.then(function (response) { return _this.$root.$refs.toastr.s(vue_1.default.prototype.trans('texts.updated_client')); })
|
||||
.catch(function (error) {
|
||||
_this.$root.$refs.toastr.e("Error saving client");
|
||||
});
|
||||
|
3
public/js/client_edit.min.js
vendored
3
public/js/client_edit.min.js
vendored
@ -3946,6 +3946,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var form_1 = __importDefault(__webpack_require__("./resources/js/src/utils/form.ts"));
|
||||
var vue_1 = __importDefault(__webpack_require__("./node_modules/vue/dist/vue.common.js"));
|
||||
exports.default = {
|
||||
data: function () {
|
||||
return {
|
||||
@ -3973,7 +3974,7 @@ exports.default = {
|
||||
onSubmit: function () {
|
||||
var _this = this;
|
||||
this.form.put('/clients/' + this.hashed_id)
|
||||
.then(function (response) { return _this.$root.$refs.toastr.s("Saved client"); })
|
||||
.then(function (response) { return _this.$root.$refs.toastr.s(vue_1.default.prototype.trans('texts.updated_client')); })
|
||||
.catch(function (error) {
|
||||
_this.$root.$refs.toastr.e("Error saving client");
|
||||
});
|
||||
|
134
public/js/client_list.js
vendored
134
public/js/client_list.js
vendored
@ -2777,6 +2777,9 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
/* harmony default export */ __webpack_exports__["default"] = ({
|
||||
props: {
|
||||
@ -2790,7 +2793,8 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
},
|
||||
methods: {
|
||||
itemAction: function itemAction(action, data, index) {
|
||||
console.log('custom-actions: ' + action, data.name, index);
|
||||
|
||||
this.$events.fire('single-action', { 'action': action, 'ids': [data.id] });
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -4314,7 +4318,7 @@ exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/cs
|
||||
|
||||
|
||||
// module
|
||||
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
|
||||
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
|
||||
|
||||
// exports
|
||||
|
||||
@ -4344,7 +4348,7 @@ exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/cs
|
||||
|
||||
|
||||
// module
|
||||
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
|
||||
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
|
||||
|
||||
// exports
|
||||
|
||||
@ -4389,7 +4393,7 @@ exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/cs
|
||||
|
||||
|
||||
// module
|
||||
exports.push([module.i, "\n.custom-actions button.ui.button {\n padding: 8px 8px;\n}\n.custom-actions button.ui.button > i.icon {\n margin: auto !important;\n}\n", ""]);
|
||||
exports.push([module.i, "\n.custom-actions button.ui.button {\n\t padding: 8px 8px;\n}\n.custom-actions button.ui.button > i.icon {\n\t margin: auto !important;\n}\n.dropdown-item {\n outline:0px; \n border:0px; \n font-weight: bold;\n}\n\n", ""]);
|
||||
|
||||
// exports
|
||||
|
||||
@ -6408,8 +6412,9 @@ exports.default = {
|
||||
mounted: function () {
|
||||
var _this = this;
|
||||
this.$events.$on('filter-set', function (eventData) { return _this.onFilterSet(); });
|
||||
this.$events.$on('bulk-action', function (eventData) { return _this.bulk(eventData, _this); });
|
||||
this.$events.$on('bulk-action', function (eventData) { return _this.bulkAction(eventData); });
|
||||
this.$events.$on('multi-select', function (eventData) { return _this.multiSelect(eventData); });
|
||||
this.$events.$on('single-action', function (eventData) { return _this.singleAction(eventData); });
|
||||
},
|
||||
methods: {
|
||||
onPaginationData: function (paginationData) {
|
||||
@ -6424,18 +6429,29 @@ exports.default = {
|
||||
this.moreParams = this.$store.getters['client_list/getQueryStringObject'];
|
||||
vue_1.default.nextTick(function () { return _this.$refs.vuetable.refresh(); });
|
||||
},
|
||||
bulk: function (action) {
|
||||
var _this = this;
|
||||
axios_1.default.post('/clients/bulk', {
|
||||
bulkAction: function (action) {
|
||||
var dataObj = {
|
||||
'action': action,
|
||||
'ids': this.$refs.vuetable.selectedTo
|
||||
})
|
||||
};
|
||||
this.postBulkAction(dataObj);
|
||||
},
|
||||
singleAction: function (dataObj) {
|
||||
console.dir(dataObj);
|
||||
this.postBulkAction(dataObj);
|
||||
},
|
||||
postBulkAction: function (dataObj) {
|
||||
var _this = this;
|
||||
axios_1.default.post('/clients/bulk', dataObj)
|
||||
.then(function (response) {
|
||||
_this.$root.$refs.toastr.s(vue_1.default.prototype.trans('texts.' + dataObj.action + 'd_client'));
|
||||
_this.$store.commit('client_list/setBulkCount', 0);
|
||||
_this.$refs.vuetable.selectedTo = [];
|
||||
_this.$refs.vuetable.refresh();
|
||||
// console.dir(response)
|
||||
})
|
||||
.catch(function (error) {
|
||||
this.$root.$refs.toastr.e("A error occurred");
|
||||
});
|
||||
},
|
||||
toggledCheckBox: function () {
|
||||
@ -6469,6 +6485,9 @@ exports.default = {
|
||||
},
|
||||
getBulkCount: function () {
|
||||
return this.$store.getters['client_list/getBulkCount'];
|
||||
},
|
||||
goToUrl: function (url) {
|
||||
location.href = url;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -6520,12 +6539,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var vue_multiselect_1 = __importDefault(__webpack_require__("./node_modules/vue-multiselect/dist/vue-multiselect.min.js"));
|
||||
exports.default = {
|
||||
components: { Multiselect: vue_multiselect_1.default },
|
||||
props: ['select_options'],
|
||||
data: function () {
|
||||
return {
|
||||
value: [],
|
||||
options: ['active', 'archived', 'deleted']
|
||||
options: this.select_options
|
||||
};
|
||||
},
|
||||
mounted: function () {
|
||||
this.$events.fire('multi-select', '');
|
||||
},
|
||||
methods: {
|
||||
onChange: function (value) {
|
||||
this.$store.commit('client_list/setStatusArray', value);
|
||||
@ -8027,7 +8050,16 @@ var render = function() {
|
||||
])
|
||||
]),
|
||||
_vm._v(" "),
|
||||
_c("div", { staticClass: "mr-auto p-2" }, [_c("vuetable-multi-select")], 1),
|
||||
_c(
|
||||
"div",
|
||||
{ staticClass: "mr-auto p-2" },
|
||||
[
|
||||
_c("vuetable-multi-select", {
|
||||
attrs: { select_options: _vm.listaction.multi_select }
|
||||
})
|
||||
],
|
||||
1
|
||||
),
|
||||
_vm._v(" "),
|
||||
_c("div", { staticClass: "ml-auto p-2" }, [_c("vuetable-query-filter")], 1),
|
||||
_vm._v(" "),
|
||||
@ -8039,7 +8071,7 @@ var render = function() {
|
||||
attrs: { disabled: _vm.isDisabled },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.$emit("bulk", "poota")
|
||||
_vm.goToUrl(_vm.listaction.create_entity.url)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -8298,7 +8330,9 @@ var render = function() {
|
||||
options: _vm.options,
|
||||
multiple: true,
|
||||
placeholder: _vm.trans("texts.status"),
|
||||
"preselect-first": true
|
||||
"preselect-first": true,
|
||||
label: "name",
|
||||
"track-by": "name"
|
||||
},
|
||||
on: { input: _vm.onChange },
|
||||
model: {
|
||||
@ -8403,7 +8437,7 @@ var render = function() {
|
||||
"aria-expanded": "false"
|
||||
}
|
||||
},
|
||||
[_vm._v("\n\tSelect\n\t")]
|
||||
[_vm._v("\n\t\t" + _vm._s(_vm.trans("texts.select")) + "\n\t\t")]
|
||||
),
|
||||
_vm._v(" "),
|
||||
_c(
|
||||
@ -8421,19 +8455,55 @@ var render = function() {
|
||||
)
|
||||
}),
|
||||
_vm._v(" "),
|
||||
_c(
|
||||
"a",
|
||||
{
|
||||
staticClass: "dropdown-item",
|
||||
attrs: { href: "#" },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.itemAction("view-item", _vm.rowData, _vm.rowIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
[_vm._v("One more item")]
|
||||
)
|
||||
_c("div", { staticClass: "dropdown-divider" }),
|
||||
_vm._v(" "),
|
||||
_vm.rowData.deleted_at == null
|
||||
? _c(
|
||||
"a",
|
||||
{
|
||||
staticClass: "dropdown-item",
|
||||
attrs: { href: "#" },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.itemAction("archive", _vm.rowData, _vm.rowIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
[_vm._v(_vm._s(_vm.trans("texts.archive")))]
|
||||
)
|
||||
: _vm._e(),
|
||||
_vm._v(" "),
|
||||
_vm.rowData.is_deleted == 1 || _vm.rowData.deleted_at != null
|
||||
? _c(
|
||||
"a",
|
||||
{
|
||||
staticClass: "dropdown-item",
|
||||
attrs: { href: "#" },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.itemAction("restore", _vm.rowData, _vm.rowIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
[_vm._v(_vm._s(_vm.trans("texts.restore")))]
|
||||
)
|
||||
: _vm._e(),
|
||||
_vm._v(" "),
|
||||
_vm.rowData.is_deleted == 0
|
||||
? _c(
|
||||
"a",
|
||||
{
|
||||
staticClass: "dropdown-item",
|
||||
attrs: { href: "#" },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.itemAction("delete", _vm.rowData, _vm.rowIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
[_vm._v(_vm._s(_vm.trans("texts.delete")))]
|
||||
)
|
||||
: _vm._e()
|
||||
],
|
||||
2
|
||||
)
|
||||
@ -24548,9 +24618,12 @@ exports.default = store;
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* State managment for the Client List View
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var state = {
|
||||
statuses: ['active'],
|
||||
statuses: [{ value: 'active' }],
|
||||
filter_text: '',
|
||||
bulk_count: 0
|
||||
};
|
||||
@ -24563,9 +24636,12 @@ var getters = {
|
||||
return state.filter_text;
|
||||
},
|
||||
getQueryStringObject: function (state) {
|
||||
var values = state.statuses.map(function (state, index, array) {
|
||||
return state.value;
|
||||
});
|
||||
var queryObj = {
|
||||
filter: state.filter_text,
|
||||
status: [].concat.apply([], state.statuses).join(",")
|
||||
status: [].concat.apply([], values).join(",")
|
||||
};
|
||||
return queryObj;
|
||||
}
|
||||
|
134
public/js/client_list.min.js
vendored
134
public/js/client_list.min.js
vendored
@ -2777,6 +2777,9 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
/* harmony default export */ __webpack_exports__["default"] = ({
|
||||
props: {
|
||||
@ -2790,7 +2793,8 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
},
|
||||
methods: {
|
||||
itemAction: function itemAction(action, data, index) {
|
||||
console.log('custom-actions: ' + action, data.name, index);
|
||||
|
||||
this.$events.fire('single-action', { 'action': action, 'ids': [data.id] });
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -4314,7 +4318,7 @@ exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/cs
|
||||
|
||||
|
||||
// module
|
||||
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
|
||||
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
|
||||
|
||||
// exports
|
||||
|
||||
@ -4344,7 +4348,7 @@ exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/cs
|
||||
|
||||
|
||||
// module
|
||||
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
|
||||
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
|
||||
|
||||
// exports
|
||||
|
||||
@ -4389,7 +4393,7 @@ exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/cs
|
||||
|
||||
|
||||
// module
|
||||
exports.push([module.i, "\n.custom-actions button.ui.button {\n padding: 8px 8px;\n}\n.custom-actions button.ui.button > i.icon {\n margin: auto !important;\n}\n", ""]);
|
||||
exports.push([module.i, "\n.custom-actions button.ui.button {\n\t padding: 8px 8px;\n}\n.custom-actions button.ui.button > i.icon {\n\t margin: auto !important;\n}\n.dropdown-item {\n outline:0px; \n border:0px; \n font-weight: bold;\n}\n\n", ""]);
|
||||
|
||||
// exports
|
||||
|
||||
@ -6408,8 +6412,9 @@ exports.default = {
|
||||
mounted: function () {
|
||||
var _this = this;
|
||||
this.$events.$on('filter-set', function (eventData) { return _this.onFilterSet(); });
|
||||
this.$events.$on('bulk-action', function (eventData) { return _this.bulk(eventData, _this); });
|
||||
this.$events.$on('bulk-action', function (eventData) { return _this.bulkAction(eventData); });
|
||||
this.$events.$on('multi-select', function (eventData) { return _this.multiSelect(eventData); });
|
||||
this.$events.$on('single-action', function (eventData) { return _this.singleAction(eventData); });
|
||||
},
|
||||
methods: {
|
||||
onPaginationData: function (paginationData) {
|
||||
@ -6424,18 +6429,29 @@ exports.default = {
|
||||
this.moreParams = this.$store.getters['client_list/getQueryStringObject'];
|
||||
vue_1.default.nextTick(function () { return _this.$refs.vuetable.refresh(); });
|
||||
},
|
||||
bulk: function (action) {
|
||||
var _this = this;
|
||||
axios_1.default.post('/clients/bulk', {
|
||||
bulkAction: function (action) {
|
||||
var dataObj = {
|
||||
'action': action,
|
||||
'ids': this.$refs.vuetable.selectedTo
|
||||
})
|
||||
};
|
||||
this.postBulkAction(dataObj);
|
||||
},
|
||||
singleAction: function (dataObj) {
|
||||
console.dir(dataObj);
|
||||
this.postBulkAction(dataObj);
|
||||
},
|
||||
postBulkAction: function (dataObj) {
|
||||
var _this = this;
|
||||
axios_1.default.post('/clients/bulk', dataObj)
|
||||
.then(function (response) {
|
||||
_this.$root.$refs.toastr.s(vue_1.default.prototype.trans('texts.' + dataObj.action + 'd_client'));
|
||||
_this.$store.commit('client_list/setBulkCount', 0);
|
||||
_this.$refs.vuetable.selectedTo = [];
|
||||
_this.$refs.vuetable.refresh();
|
||||
// console.dir(response)
|
||||
})
|
||||
.catch(function (error) {
|
||||
this.$root.$refs.toastr.e("A error occurred");
|
||||
});
|
||||
},
|
||||
toggledCheckBox: function () {
|
||||
@ -6469,6 +6485,9 @@ exports.default = {
|
||||
},
|
||||
getBulkCount: function () {
|
||||
return this.$store.getters['client_list/getBulkCount'];
|
||||
},
|
||||
goToUrl: function (url) {
|
||||
location.href = url;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -6520,12 +6539,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var vue_multiselect_1 = __importDefault(__webpack_require__("./node_modules/vue-multiselect/dist/vue-multiselect.min.js"));
|
||||
exports.default = {
|
||||
components: { Multiselect: vue_multiselect_1.default },
|
||||
props: ['select_options'],
|
||||
data: function () {
|
||||
return {
|
||||
value: [],
|
||||
options: ['active', 'archived', 'deleted']
|
||||
options: this.select_options
|
||||
};
|
||||
},
|
||||
mounted: function () {
|
||||
this.$events.fire('multi-select', '');
|
||||
},
|
||||
methods: {
|
||||
onChange: function (value) {
|
||||
this.$store.commit('client_list/setStatusArray', value);
|
||||
@ -8027,7 +8050,16 @@ var render = function() {
|
||||
])
|
||||
]),
|
||||
_vm._v(" "),
|
||||
_c("div", { staticClass: "mr-auto p-2" }, [_c("vuetable-multi-select")], 1),
|
||||
_c(
|
||||
"div",
|
||||
{ staticClass: "mr-auto p-2" },
|
||||
[
|
||||
_c("vuetable-multi-select", {
|
||||
attrs: { select_options: _vm.listaction.multi_select }
|
||||
})
|
||||
],
|
||||
1
|
||||
),
|
||||
_vm._v(" "),
|
||||
_c("div", { staticClass: "ml-auto p-2" }, [_c("vuetable-query-filter")], 1),
|
||||
_vm._v(" "),
|
||||
@ -8039,7 +8071,7 @@ var render = function() {
|
||||
attrs: { disabled: _vm.isDisabled },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.$emit("bulk", "poota")
|
||||
_vm.goToUrl(_vm.listaction.create_entity.url)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -8298,7 +8330,9 @@ var render = function() {
|
||||
options: _vm.options,
|
||||
multiple: true,
|
||||
placeholder: _vm.trans("texts.status"),
|
||||
"preselect-first": true
|
||||
"preselect-first": true,
|
||||
label: "name",
|
||||
"track-by": "name"
|
||||
},
|
||||
on: { input: _vm.onChange },
|
||||
model: {
|
||||
@ -8403,7 +8437,7 @@ var render = function() {
|
||||
"aria-expanded": "false"
|
||||
}
|
||||
},
|
||||
[_vm._v("\n\tSelect\n\t")]
|
||||
[_vm._v("\n\t\t" + _vm._s(_vm.trans("texts.select")) + "\n\t\t")]
|
||||
),
|
||||
_vm._v(" "),
|
||||
_c(
|
||||
@ -8421,19 +8455,55 @@ var render = function() {
|
||||
)
|
||||
}),
|
||||
_vm._v(" "),
|
||||
_c(
|
||||
"a",
|
||||
{
|
||||
staticClass: "dropdown-item",
|
||||
attrs: { href: "#" },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.itemAction("view-item", _vm.rowData, _vm.rowIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
[_vm._v("One more item")]
|
||||
)
|
||||
_c("div", { staticClass: "dropdown-divider" }),
|
||||
_vm._v(" "),
|
||||
_vm.rowData.deleted_at == null
|
||||
? _c(
|
||||
"a",
|
||||
{
|
||||
staticClass: "dropdown-item",
|
||||
attrs: { href: "#" },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.itemAction("archive", _vm.rowData, _vm.rowIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
[_vm._v(_vm._s(_vm.trans("texts.archive")))]
|
||||
)
|
||||
: _vm._e(),
|
||||
_vm._v(" "),
|
||||
_vm.rowData.is_deleted == 1 || _vm.rowData.deleted_at != null
|
||||
? _c(
|
||||
"a",
|
||||
{
|
||||
staticClass: "dropdown-item",
|
||||
attrs: { href: "#" },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.itemAction("restore", _vm.rowData, _vm.rowIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
[_vm._v(_vm._s(_vm.trans("texts.restore")))]
|
||||
)
|
||||
: _vm._e(),
|
||||
_vm._v(" "),
|
||||
_vm.rowData.is_deleted == 0
|
||||
? _c(
|
||||
"a",
|
||||
{
|
||||
staticClass: "dropdown-item",
|
||||
attrs: { href: "#" },
|
||||
on: {
|
||||
click: function($event) {
|
||||
_vm.itemAction("delete", _vm.rowData, _vm.rowIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
[_vm._v(_vm._s(_vm.trans("texts.delete")))]
|
||||
)
|
||||
: _vm._e()
|
||||
],
|
||||
2
|
||||
)
|
||||
@ -24548,9 +24618,12 @@ exports.default = store;
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* State managment for the Client List View
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var state = {
|
||||
statuses: ['active'],
|
||||
statuses: [{ value: 'active' }],
|
||||
filter_text: '',
|
||||
bulk_count: 0
|
||||
};
|
||||
@ -24563,9 +24636,12 @@ var getters = {
|
||||
return state.filter_text;
|
||||
},
|
||||
getQueryStringObject: function (state) {
|
||||
var values = state.statuses.map(function (state, index, array) {
|
||||
return state.value;
|
||||
});
|
||||
var queryObj = {
|
||||
filter: state.filter_text,
|
||||
status: [].concat.apply([], state.statuses).join(",")
|
||||
status: [].concat.apply([], values).join(",")
|
||||
};
|
||||
return queryObj;
|
||||
}
|
||||
|
@ -2,11 +2,14 @@
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Select
|
||||
{{ trans('texts.select') }}
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
|
||||
<a class="dropdown-item" :href="action.url" v-for="action in rowData.actions">{{ action.name }}</a>
|
||||
<a class="dropdown-item" href="#" @click="itemAction('view-item', rowData, rowIndex)">One more item</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" @click="itemAction('archive', rowData, rowIndex)" v-if="rowData.deleted_at == null">{{ trans('texts.archive') }}</a>
|
||||
<a class="dropdown-item" href="#" @click="itemAction('restore', rowData, rowIndex)" v-if="rowData.is_deleted == 1 || rowData.deleted_at != null">{{ trans('texts.restore') }}</a>
|
||||
<a class="dropdown-item" href="#" @click="itemAction('delete', rowData, rowIndex)" v-if="rowData.is_deleted == 0">{{ trans('texts.delete') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -25,7 +28,9 @@
|
||||
},
|
||||
methods: {
|
||||
itemAction (action, data, index) {
|
||||
console.log('custom-actions: ' + action, data.name, index)
|
||||
|
||||
this.$events.fire('single-action', {'action': action, 'ids': [data.id]})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -38,4 +43,10 @@
|
||||
.custom-actions button.ui.button > i.icon {
|
||||
margin: auto !important;
|
||||
}
|
||||
.dropdown-item {
|
||||
outline:0px;
|
||||
border:0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
</style>
|
@ -53,6 +53,7 @@
|
||||
|
||||
import Form from '../../utils/form';
|
||||
import Client from '../../models/client-model';
|
||||
import Vue from 'vue'
|
||||
|
||||
export default {
|
||||
data: function () {
|
||||
@ -79,7 +80,7 @@ export default {
|
||||
},
|
||||
onSubmit() {
|
||||
this.form.put('/clients/' + this.hashed_id)
|
||||
.then(response => this.$root.$refs.toastr.s("Saved client"))
|
||||
.then(response => this.$root.$refs.toastr.s( Vue.prototype.trans('texts.updated_client') ))
|
||||
.catch(error => {
|
||||
|
||||
this.$root.$refs.toastr.e("Error saving client");
|
||||
|
@ -62,8 +62,9 @@ export default {
|
||||
mounted() {
|
||||
|
||||
this.$events.$on('filter-set', eventData => this.onFilterSet())
|
||||
this.$events.$on('bulk-action', eventData => this.bulk(eventData, this))
|
||||
this.$events.$on('bulk-action', eventData => this.bulkAction(eventData))
|
||||
this.$events.$on('multi-select', eventData => this.multiSelect(eventData))
|
||||
this.$events.$on('single-action', eventData => this.singleAction(eventData))
|
||||
|
||||
},
|
||||
methods: {
|
||||
@ -85,22 +86,36 @@ export default {
|
||||
Vue.nextTick( () => this.$refs.vuetable.refresh())
|
||||
|
||||
},
|
||||
bulk (action){
|
||||
bulkAction (action){
|
||||
|
||||
axios.post('/clients/bulk', {
|
||||
'action' : action,
|
||||
'ids' : this.$refs.vuetable.selectedTo
|
||||
})
|
||||
.then((response) => {
|
||||
this.$store.commit('client_list/setBulkCount', 0)
|
||||
this.$refs.vuetable.selectedTo = []
|
||||
this.$refs.vuetable.refresh()
|
||||
|
||||
})
|
||||
.catch(function (error) {
|
||||
var dataObj = {
|
||||
'action' : action,
|
||||
'ids' : this.$refs.vuetable.selectedTo
|
||||
}
|
||||
this.postBulkAction(dataObj)
|
||||
|
||||
},
|
||||
singleAction(dataObj) {
|
||||
|
||||
console.dir(dataObj)
|
||||
|
||||
this.postBulkAction(dataObj)
|
||||
|
||||
},
|
||||
postBulkAction(dataObj) {
|
||||
|
||||
axios.post('/clients/bulk', dataObj)
|
||||
.then((response) => {
|
||||
this.$root.$refs.toastr.s( Vue.prototype.trans('texts.'+dataObj.action+'d_client') )
|
||||
this.$store.commit('client_list/setBulkCount', 0)
|
||||
this.$refs.vuetable.selectedTo = []
|
||||
this.$refs.vuetable.refresh()
|
||||
// console.dir(response)
|
||||
})
|
||||
.catch(function (error) {
|
||||
this.$root.$refs.toastr.e( "A error occurred" )
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
toggledCheckBox(){
|
||||
this.$store.commit('client_list/setBulkCount', this.$refs.vuetable.selectedTo.length)
|
||||
|
@ -18,7 +18,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mr-auto p-2">
|
||||
<vuetable-multi-select></vuetable-multi-select>
|
||||
<vuetable-multi-select :select_options="listaction.multi_select"></vuetable-multi-select>
|
||||
</div>
|
||||
|
||||
<div class="ml-auto p-2">
|
||||
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
|
||||
<div class="p-2">
|
||||
<button class="btn btn-primary btn-lg " v-on:click="$emit('bulk','poota')" :disabled="isDisabled">{{ trans('texts.new_client') }}</button>
|
||||
<button class="btn btn-primary btn-lg " @click="goToUrl(listaction.create_entity.url)" :disabled="isDisabled">{{ trans('texts.new_client') }}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -46,6 +46,9 @@
|
||||
},
|
||||
getBulkCount() {
|
||||
return this.$store.getters['client_list/getBulkCount']
|
||||
},
|
||||
goToUrl: function (url) {
|
||||
location.href=url
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -5,6 +5,8 @@
|
||||
:multiple="true"
|
||||
:placeholder="trans('texts.status')"
|
||||
:preselect-first="true"
|
||||
label="name"
|
||||
track-by="name"
|
||||
@input="onChange"
|
||||
></multiselect>
|
||||
</div>
|
||||
@ -16,12 +18,18 @@
|
||||
|
||||
export default {
|
||||
components: { Multiselect },
|
||||
props:['select_options'],
|
||||
data () {
|
||||
return {
|
||||
value : [],
|
||||
options: ['active', 'archived', 'deleted']
|
||||
options: this.select_options
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.$events.fire('multi-select', '')
|
||||
|
||||
},
|
||||
methods: {
|
||||
onChange (value) {
|
||||
|
||||
@ -34,7 +42,7 @@
|
||||
onSelect (option) {
|
||||
|
||||
if (option === 'Disable me!') this.isDisabled = true
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
/**
|
||||
* State managment for the Client List View
|
||||
*/
|
||||
|
||||
const state = {
|
||||
statuses: ['active'],
|
||||
statuses: [{value: 'active'}],
|
||||
filter_text: '',
|
||||
bulk_count : 0
|
||||
}
|
||||
@ -18,9 +22,13 @@ const getters = {
|
||||
},
|
||||
getQueryStringObject: state => {
|
||||
|
||||
var values = state.statuses.map(function (state, index, array) {
|
||||
return state.value;
|
||||
});
|
||||
|
||||
var queryObj = {
|
||||
filter: state.filter_text,
|
||||
status: [].concat.apply([], state.statuses).join(",")
|
||||
status: [].concat.apply([], values).join(",")
|
||||
}
|
||||
|
||||
return queryObj
|
||||
@ -47,7 +55,7 @@ const mutations = {
|
||||
setBulkCount(state, count) {
|
||||
|
||||
state.bulk_count = count
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,8 @@
|
||||
{{ Breadcrumbs::render('clients') }}
|
||||
|
||||
<div class="container-fluid" id="client_list">
|
||||
|
||||
<vue-toastr ref="toastr"></vue-toastr>
|
||||
|
||||
<list-actions :listaction="{{ $listaction }}"></list-actions>
|
||||
|
||||
<div style="background: #fff;">
|
||||
|
Loading…
x
Reference in New Issue
Block a user