mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Generated Datatable Action Menus (#2590)
* Style Datatable * Component Actions * Harvest user permissions from Pivot table * Pad out permissions * Client actions * Fixes for travis * Client Datatables * Menu permissions * Tests for menu permissions * Action menu * Implement query builder filter * Flatten user permissions * Implement rendering of client action dropdowns * Generated Action Menus
This commit is contained in:
parent
43342fb98b
commit
0faf91dd5d
@ -54,6 +54,7 @@ before_script:
|
||||
- php artisan migrate --database=db-ninja-02 --seed --no-interaction
|
||||
- php artisan optimize
|
||||
- npm install
|
||||
- npm install @types/bluebird @types/core-js@0.9.36
|
||||
- npm run production
|
||||
# migrate and seed the database
|
||||
# Start webserver on ninja.test:8000
|
||||
|
@ -6,7 +6,7 @@
|
||||
[](https://codecov.io/gh/invoiceninja/invoiceninja)
|
||||
[](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
|
||||
|
||||
**Invoice Ninja v 5.0** is coming soon!
|
||||
**Invoice Ninja v 2.0** is coming soon!
|
||||
|
||||
We will be using the lessons learnt in Invoice Ninja 4.0 to build a bigger better platform to work from. If you would like to contribute to the project we will gladly accept contributions for code, user guides, bug tracking and feedback! Please consider the following guidelines prior to submitting a pull request:
|
||||
|
||||
@ -22,8 +22,8 @@ Where practical code should be strongly typed, ie your methods must return a typ
|
||||
|
||||
`public function doThis() : void`
|
||||
|
||||
PHP > 7.1 allows the return type Nullable so there should be no circumstance a type cannot be return by using the following:
|
||||
PHP >= 7.1 allows the return type Nullable so there should be no circumstance a type cannot be return by using the following:
|
||||
|
||||
`public function doThat() ?:string`
|
||||
|
||||
Please include tests with PRs to ensure your code works well and integrates with the rest of the project. Please ensure suitable unit/functional/acceptance tests are included to provide code coverage.
|
||||
To improve chances of PRs being merged please include teststo ensure your code works well and integrates with the rest of the project.
|
||||
|
@ -3,94 +3,158 @@
|
||||
namespace App\Datatables;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ClientDatatable
|
||||
class ClientDatatable extends EntityDatatable
|
||||
{
|
||||
use MakesHash;
|
||||
use MakesActionMenu;
|
||||
|
||||
/**
|
||||
* ?sort=&page=1&per_page=20
|
||||
*/
|
||||
public static function query(Request $request, int $company_id)
|
||||
{
|
||||
/**
|
||||
*
|
||||
* $sort_col is returned col|asc
|
||||
* needs to be exploded
|
||||
*
|
||||
*/
|
||||
$sort_col = explode("|", $request->input('sort'));
|
||||
/**
|
||||
* ?sort=&page=1&per_page=20
|
||||
*/
|
||||
public function query(Request $request, int $company_id)
|
||||
{
|
||||
/**
|
||||
*
|
||||
* $sort_col is returned col|asc
|
||||
* needs to be exploded
|
||||
*
|
||||
*/
|
||||
$sort_col = explode("|", $request->input('sort'));
|
||||
|
||||
return response()->json(self::find($company_id, $request->input('filter'))->orderBy($sort_col[0], $sort_col[1])->paginate($request->input('per_page')), 200);
|
||||
}
|
||||
$data = $this->find($company_id, $request->input('filter'))
|
||||
->orderBy($sort_col[0], $sort_col[1])
|
||||
->paginate($request->input('per_page'));
|
||||
|
||||
return response()
|
||||
->json($this->buildActionColumn($data), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static function find(int $company_id, $filter, $userId = false)
|
||||
{
|
||||
$query = DB::table('clients')
|
||||
->join('companies', 'companies.id', '=', 'clients.company_id')
|
||||
->join('client_contacts', 'client_contacts.client_id', '=', 'clients.id')
|
||||
->where('clients.company_id', '=', $company_id)
|
||||
->where('client_contacts.is_primary', '=', true)
|
||||
->where('client_contacts.deleted_at', '=', null)
|
||||
//->whereRaw('(clients.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
|
||||
->select(
|
||||
DB::raw('COALESCE(clients.currency_id, companies.currency_id) currency_id'),
|
||||
DB::raw('COALESCE(clients.country_id, companies.country_id) country_id'),
|
||||
DB::raw("CONCAT(COALESCE(client_contacts.first_name, ''), ' ', COALESCE(client_contacts.last_name, '')) contact"),
|
||||
'clients.id',
|
||||
'clients.name',
|
||||
'clients.private_notes',
|
||||
'client_contacts.first_name',
|
||||
'client_contacts.last_name',
|
||||
'clients.balance',
|
||||
'clients.last_login',
|
||||
'clients.created_at',
|
||||
'clients.created_at as client_created_at',
|
||||
'client_contacts.phone',
|
||||
'client_contacts.email',
|
||||
'clients.deleted_at',
|
||||
'clients.is_deleted',
|
||||
'clients.user_id',
|
||||
'clients.id_number'
|
||||
);
|
||||
private function find(int $company_id, $filter, $userId = false)
|
||||
{
|
||||
$query = DB::table('clients')
|
||||
->join('companies', 'companies.id', '=', 'clients.company_id')
|
||||
->join('client_contacts', 'client_contacts.client_id', '=', 'clients.id')
|
||||
->where('clients.company_id', '=', $company_id)
|
||||
->where('client_contacts.is_primary', '=', true)
|
||||
->where('client_contacts.deleted_at', '=', null)
|
||||
//->whereRaw('(clients.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
|
||||
->select(
|
||||
DB::raw('COALESCE(clients.currency_id, companies.currency_id) currency_id'),
|
||||
DB::raw('COALESCE(clients.country_id, companies.country_id) country_id'),
|
||||
DB::raw("CONCAT(COALESCE(client_contacts.first_name, ''), ' ', COALESCE(client_contacts.last_name, '')) contact"),
|
||||
'clients.id',
|
||||
'clients.name',
|
||||
'clients.private_notes',
|
||||
'client_contacts.first_name',
|
||||
'client_contacts.last_name',
|
||||
'clients.balance',
|
||||
'clients.last_login',
|
||||
'clients.created_at',
|
||||
'clients.created_at as client_created_at',
|
||||
'client_contacts.phone',
|
||||
'client_contacts.email',
|
||||
'clients.deleted_at',
|
||||
'clients.is_deleted',
|
||||
'clients.user_id',
|
||||
'clients.id_number'
|
||||
);
|
||||
/*
|
||||
if(Auth::user()->account->customFieldsOption('client1_filter')) {
|
||||
$query->addSelect('clients.custom_value1');
|
||||
}
|
||||
if(Auth::user()->account->customFieldsOption('client1_filter')) {
|
||||
$query->addSelect('clients.custom_value1');
|
||||
}
|
||||
|
||||
if(Auth::user()->account->customFieldsOption('client2_filter')) {
|
||||
$query->addSelect('clients.custom_value2');
|
||||
}
|
||||
if(Auth::user()->account->customFieldsOption('client2_filter')) {
|
||||
$query->addSelect('clients.custom_value2');
|
||||
}
|
||||
|
||||
$this->applyFilters($query, ENTITY_CLIENT);
|
||||
$this->applyFilters($query, ENTITY_CLIENT);
|
||||
*/
|
||||
if ($filter) {
|
||||
$query->where(function ($query) use ($filter) {
|
||||
$query->where('clients.name', 'like', '%'.$filter.'%')
|
||||
->orWhere('clients.id_number', 'like', '%'.$filter.'%')
|
||||
->orWhere('client_contacts.first_name', 'like', '%'.$filter.'%')
|
||||
->orWhere('client_contacts.last_name', 'like', '%'.$filter.'%')
|
||||
->orWhere('client_contacts.email', 'like', '%'.$filter.'%');
|
||||
});
|
||||
if ($filter) {
|
||||
$query->where(function ($query) use ($filter) {
|
||||
$query->where('clients.name', 'like', '%'.$filter.'%')
|
||||
->orWhere('clients.id_number', 'like', '%'.$filter.'%')
|
||||
->orWhere('client_contacts.first_name', 'like', '%'.$filter.'%')
|
||||
->orWhere('client_contacts.last_name', 'like', '%'.$filter.'%')
|
||||
->orWhere('client_contacts.email', 'like', '%'.$filter.'%');
|
||||
});
|
||||
/*
|
||||
if(Auth::user()->account->customFieldsOption('client1_filter')) {
|
||||
$query->orWhere('clients.custom_value1', 'like' , '%'.$filter.'%');
|
||||
}
|
||||
if(Auth::user()->account->customFieldsOption('client1_filter')) {
|
||||
$query->orWhere('clients.custom_value1', 'like' , '%'.$filter.'%');
|
||||
}
|
||||
|
||||
if(Auth::user()->account->customFieldsOption('client2_filter')) {
|
||||
$query->orWhere('clients.custom_value2', 'like' , '%'.$filter.'%');
|
||||
}
|
||||
if(Auth::user()->account->customFieldsOption('client2_filter')) {
|
||||
$query->orWhere('clients.custom_value2', 'like' , '%'.$filter.'%');
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if ($userId) {
|
||||
$query->where('clients.user_id', '=', $userId);
|
||||
}
|
||||
if ($userId) {
|
||||
$query->where('clients.user_id', '=', $userId);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action dropdown menu
|
||||
*
|
||||
* @param $data Std Class of client datatable rows
|
||||
* @return object Rendered action column items
|
||||
*/
|
||||
private function buildActionColumn($data) : object
|
||||
{
|
||||
|
||||
//if(auth()->user()->is_admin())
|
||||
//todo permissions are only mocked here, when user permissions have been implemented this needs to be refactored.
|
||||
|
||||
$permissions = [
|
||||
'view_client',
|
||||
'edit_client',
|
||||
'create_task',
|
||||
'create_invoice',
|
||||
'create_payment',
|
||||
'create_credit',
|
||||
'create_expense'
|
||||
];
|
||||
|
||||
$requested_actions = [
|
||||
'view_client_client_id',
|
||||
'edit_client_client_id',
|
||||
'create_task_client_id',
|
||||
'create_invoice_client_id',
|
||||
'create_payment_client_id',
|
||||
'create_credit_client_id',
|
||||
'create_expense_client_id'
|
||||
];
|
||||
|
||||
$is_admin = false;
|
||||
|
||||
$actions = $this->filterActions($requested_actions, $permissions, $is_admin);
|
||||
|
||||
$data->map(function ($row) use ($actions) {
|
||||
|
||||
$updated_actions = $actions->map(function ($action) use($row){
|
||||
|
||||
$action['url'] = route($action['route'], [$action['key'] => $this->encodePrimaryKey($row->id)]);
|
||||
return $action;
|
||||
|
||||
});
|
||||
|
||||
$row->actions = $updated_actions;
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
@ -6,33 +6,6 @@ namespace App\Datatables;
|
||||
class EntityDatatable
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the columns to be displayed and their key/values
|
||||
* @return array Columns and key/value option pairs
|
||||
*
|
||||
* To be used to show/hide columns
|
||||
*/
|
||||
public function columns()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display options for the ajax request
|
||||
* @return array url, type, data
|
||||
*/
|
||||
public function ajax()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the datatable
|
||||
* @return DataTable returns a DataTable instance
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
61
app/Datatables/MakesActionMenu.php
Normal file
61
app/Datatables/MakesActionMenu.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Datatables;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
trait MakesActionMenu
|
||||
{
|
||||
/**
|
||||
* Returns all possible datatable actions
|
||||
*
|
||||
* @return Collection collection instance of action items
|
||||
*/
|
||||
public function actions() :Collection
|
||||
{
|
||||
|
||||
return collect([
|
||||
['action' => 'view_client_client_id', 'permission' => 'view_client', 'route' => 'clients.show', 'key' => 'client_id', 'name' => trans('texts.view')],
|
||||
['action' => 'edit_client_client_id', 'permission' => 'edit_client', 'route' => 'clients.edit', 'key' => 'client_id', 'name' => trans('texts.edit')],
|
||||
['action' => 'create_task_client_id', 'permission' => 'create_task', 'route' => 'tasks.create', 'key' => 'client_id', 'name' => trans('texts.new_task')],
|
||||
['action' => 'create_invoice_client_id', 'permission' => 'create_invoice', 'route' => 'invoices.create', 'key' => 'client_id', 'name' => trans('texts.new_invoice')],
|
||||
['action' => 'enter_payment_client_id', 'permission' => 'create_payment', 'route' => 'payments.create', 'key' => 'client_id', 'name' => trans('texts.enter_payment')],
|
||||
['action' => 'enter_credit_client_id', 'permission' => 'create_credit', 'route' => 'credits.create', 'key' => 'client_id', 'name' => trans('texts.enter_credit')],
|
||||
['action' => 'enter_expense_client_id', 'permission' => 'create_expense', 'route' => 'expenses.create', 'key' => 'client_id', 'name' => trans('texts.enter_expense')]
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the user permissions against the collection and returns
|
||||
* a Collection of available actions\.
|
||||
*
|
||||
* @param Collection $actions collection of possible actions
|
||||
* @param bool $is_admin 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
|
||||
*
|
||||
* @param array $actions Array of actions requested
|
||||
* @param array $permissions Array of user permissions
|
||||
* @param bool $is_admin Boolean is_admin
|
||||
* @return Collection collection of filtered actions available to the user
|
||||
*/
|
||||
public function filterActions(array $actions, array $permissions, bool $is_admin) :Collection
|
||||
{
|
||||
|
||||
return $this->checkPermissions($this->actions()->whereIn('action', $actions), $permissions, $is_admin);
|
||||
}
|
||||
}
|
58
app/Filters/ClientFilters.php
Normal file
58
app/Filters/ClientFilters.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class ClientFilters extends QueryFilters
|
||||
{
|
||||
|
||||
/**
|
||||
* Filters by due_date
|
||||
*
|
||||
* @param string $due_date
|
||||
* @return Builder
|
||||
*/
|
||||
public function balance($balance)
|
||||
{
|
||||
$parts = $this->split($balance);
|
||||
|
||||
return $this->builder->where('balance', $parts->operator, $parts->value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter by popularity.
|
||||
*
|
||||
//* @param string $order
|
||||
//* @return Builder
|
||||
|
||||
public function popular($order = 'desc')
|
||||
{
|
||||
return $this->builder->orderBy('views', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by difficulty.
|
||||
*
|
||||
* @param string $level
|
||||
* @return Builder
|
||||
|
||||
public function difficulty($level)
|
||||
{
|
||||
return $this->builder->where('difficulty', $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by length.
|
||||
*
|
||||
* @param string $order
|
||||
* @return Builder
|
||||
|
||||
public function length($order = 'asc')
|
||||
{
|
||||
return $this->builder->orderBy('length', $order);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
110
app/Filters/QueryFilters.php
Normal file
110
app/Filters/QueryFilters.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
abstract class QueryFilters
|
||||
{
|
||||
/**
|
||||
* The request object.
|
||||
*
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The builder instance.
|
||||
*
|
||||
* @var Builder
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
/**
|
||||
* Create a new QueryFilters instance.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filters to the builder.
|
||||
*
|
||||
* @param Builder $builder
|
||||
* @return Builder
|
||||
*/
|
||||
public function apply(Builder $builder)
|
||||
{
|
||||
$this->builder = $builder;
|
||||
|
||||
foreach ($this->filters() as $name => $value) {
|
||||
if (! method_exists($this, $name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strlen($value)) {
|
||||
$this->$name($value);
|
||||
} else {
|
||||
$this->$name();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all request filters data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filters()
|
||||
{
|
||||
return $this->request->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Explodes the value by delimiter
|
||||
*
|
||||
* @param string $value
|
||||
* @return array
|
||||
*/
|
||||
public function split($value) : stdClass
|
||||
{
|
||||
$exploded_array = explode(":", $value);
|
||||
|
||||
$parts = new stdClass;
|
||||
|
||||
$parts->value = $exploded_array[0];
|
||||
$parts->operator = $this->operatorConvertor($exploded_array[1]);
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
private function operatorConvertor(string $operator) : string
|
||||
{
|
||||
switch ($operator) {
|
||||
case 'lt':
|
||||
return '<';
|
||||
break;
|
||||
case 'gt':
|
||||
return '>';
|
||||
break;
|
||||
case 'lte':
|
||||
return '<=';
|
||||
break;
|
||||
case 'gte':
|
||||
return '>=';
|
||||
break;
|
||||
case 'eq':
|
||||
return '=';
|
||||
break;
|
||||
default:
|
||||
return '=';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -27,16 +27,19 @@ class ClientController extends Controller
|
||||
|
||||
protected $clientRepo;
|
||||
|
||||
public function __construct(ClientRepository $clientRepo)
|
||||
protected $clientDatatable;
|
||||
|
||||
public function __construct(ClientRepository $clientRepo, ClientDatatable $clientDatatable)
|
||||
{
|
||||
$this->clientRepo = $clientRepo;
|
||||
$this->clientDatatable = $clientDatatable;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
if(request('page'))
|
||||
return ClientDatatable::query(request(), $this->getCurrentCompanyId());
|
||||
return $this->clientDatatable->query(request(), $this->getCurrentCompanyId());
|
||||
|
||||
return view('client.vue_list');
|
||||
/*
|
||||
|
@ -23,6 +23,7 @@ class DashboardController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// dd(json_decode(auth()->user()->permissions(),true));
|
||||
return view('dashboard.index');
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,11 @@ class CompanyUser extends BaseModel
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(User::class);
|
||||
return $this->hasOne(User::class)->withPivot('permissions');
|
||||
}
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->hasOne(Company::class)->withPivot('permissions');
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\SetsUserSessionAttributes;
|
||||
use App\Models\Traits\UserTrait;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
@ -17,7 +17,8 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
use SoftDeletes;
|
||||
use PresentableTrait;
|
||||
use MakesHash;
|
||||
|
||||
use UserSessionAttributes;
|
||||
|
||||
protected $guard = 'user';
|
||||
|
||||
protected $dates = ['deleted_at'];
|
||||
@ -54,11 +55,30 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
'slack_webhook_url',
|
||||
];
|
||||
|
||||
|
||||
|
||||
public function companies()
|
||||
{
|
||||
return $this->belongsToMany(Company::class);
|
||||
return $this->belongsToMany(Company::class)->withPivot('permissions');
|
||||
}
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->companies()->where('company_id', $this->getCurrentCompanyId())->first();
|
||||
}
|
||||
|
||||
public function permissions()
|
||||
{
|
||||
|
||||
$permissions = json_decode($this->company()->pivot->permissions);
|
||||
|
||||
if (! $permissions)
|
||||
return [];
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
public function is_admin()
|
||||
{
|
||||
return $this->company()->pivot->is_admin;
|
||||
}
|
||||
|
||||
public function contacts()
|
||||
@ -66,9 +86,22 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
return $this->hasMany(Contact::class);
|
||||
}
|
||||
|
||||
|
||||
public function owns($entity)
|
||||
public function owns($entity) : bool
|
||||
{
|
||||
return ! empty($entity->user_id) && $entity->user_id == $this->id;
|
||||
}
|
||||
|
||||
public function permissionsFlat()
|
||||
{
|
||||
return collect($this->permissions())->flatten();
|
||||
}
|
||||
|
||||
public function permissionsMap()
|
||||
{
|
||||
|
||||
$keys = array_values((array) $this->permissions());
|
||||
$values = array_fill(0, count($keys), true);
|
||||
|
||||
return array_combine($keys, $values);
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +37,21 @@ class UsersTableSeeder extends Seeder
|
||||
'confirmation_code' => $this->createDbHash(config('database.default'))
|
||||
]);
|
||||
|
||||
|
||||
$userPermissions = collect([
|
||||
'view_invoice',
|
||||
'view_client',
|
||||
'edit_client',
|
||||
'edit_invoice',
|
||||
'create_invoice',
|
||||
'create_client'
|
||||
]);
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'permissions' => $userPermissions->toJson(),
|
||||
'is_locked' => 0,
|
||||
]);
|
||||
|
||||
|
267
public/js/client_list.js
vendored
267
public/js/client_list.js
vendored
@ -1190,6 +1190,45 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/babel-loader/lib/index.js?{\"cacheDirectory\":true,\"presets\":[[\"env\",{\"modules\":false,\"targets\":{\"browsers\":[\"> 2%\"],\"uglify\":true}}]],\"plugins\":[\"transform-object-rest-spread\",[\"transform-runtime\",{\"polyfill\":false,\"helpers\":false}]]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
/* harmony default export */ __webpack_exports__["default"] = ({
|
||||
props: {
|
||||
rowData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
rowIndex: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
itemAction: function itemAction(action, data, index) {
|
||||
console.log('custom-actions: ' + action, data.name, index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/babel-loader/lib/index.js?{\"cacheDirectory\":true,\"presets\":[[\"env\",{\"modules\":false,\"targets\":{\"browsers\":[\"> 2%\"],\"uglify\":true}}]],\"plugins\":[\"transform-object-rest-spread\",[\"transform-runtime\",{\"polyfill\":false,\"helpers\":false}]]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./resources/js/src/components/util/VuetablePaginationBootstrap.vue":
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
@ -2694,6 +2733,21 @@ exports.push([module.i, "\n.form-inline > * {\n margin:5px 10px;\n}\n", ""]);
|
||||
// exports
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/css-base.js")(false);
|
||||
// imports
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/css-loader/lib/css-base.js":
|
||||
@ -3249,6 +3303,7 @@ var VuetablePagination_vue_1 = __importDefault(__webpack_require__("./node_modul
|
||||
var VuetablePaginationInfo_vue_1 = __importDefault(__webpack_require__("./node_modules/vuetable-2/src/components/VuetablePaginationInfo.vue"));
|
||||
var vue_1 = __importDefault(__webpack_require__("./node_modules/vue/dist/vue.common.js"));
|
||||
var vue_events_1 = __importDefault(__webpack_require__("./node_modules/vue-events/dist/index.js"));
|
||||
var VuetableCss_1 = __importDefault(__webpack_require__("./resources/js/src/components/util/VuetableCss.ts"));
|
||||
vue_1.default.use(vue_events_1.default);
|
||||
exports.default = {
|
||||
components: {
|
||||
@ -3258,6 +3313,7 @@ exports.default = {
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
css: VuetableCss_1.default,
|
||||
sortOrder: [
|
||||
{
|
||||
field: 'name',
|
||||
@ -3304,31 +3360,14 @@ exports.default = {
|
||||
name: 'balance',
|
||||
sortField: 'balance',
|
||||
dataClass: 'center aligned'
|
||||
}
|
||||
],
|
||||
css: {
|
||||
table: {
|
||||
tableClass: 'table table-striped table-bordered table-hovered',
|
||||
loadingClass: 'loading',
|
||||
ascendingIcon: 'glyphicon glyphicon-chevron-up',
|
||||
descendingIcon: 'glyphicon glyphicon-chevron-down',
|
||||
handleIcon: 'glyphicon glyphicon-menu-hamburger',
|
||||
},
|
||||
pagination: {
|
||||
infoClass: 'pull-left',
|
||||
wrapperClass: 'vuetable-pagination pull-right',
|
||||
activeClass: 'btn-primary',
|
||||
disabledClass: 'disabled',
|
||||
pageClass: 'btn btn-border',
|
||||
linkClass: 'btn btn-border',
|
||||
icons: {
|
||||
first: '',
|
||||
prev: '',
|
||||
next: '',
|
||||
last: '',
|
||||
},
|
||||
{
|
||||
name: '__component:client-actions',
|
||||
title: '',
|
||||
titleClass: 'center aligned',
|
||||
dataClass: 'center aligned'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
//props: ['list'],
|
||||
@ -4795,6 +4834,7 @@ var render = function() {
|
||||
"per-page": 20,
|
||||
"sort-order": _vm.sortOrder,
|
||||
"append-params": _vm.moreParams,
|
||||
css: _vm.css.table,
|
||||
"pagination-path": ""
|
||||
},
|
||||
on: { "vuetable:pagination-data": _vm.onPaginationData }
|
||||
@ -5060,6 +5100,74 @@ if (false) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/vue-loader/lib/template-compiler/index.js?{\"id\":\"data-v-d9a40d24\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
var render = function() {
|
||||
var _vm = this
|
||||
var _h = _vm.$createElement
|
||||
var _c = _vm._self._c || _h
|
||||
return _c("div", { staticClass: "dropdown" }, [
|
||||
_c(
|
||||
"button",
|
||||
{
|
||||
staticClass: "btn btn-secondary dropdown-toggle",
|
||||
attrs: {
|
||||
type: "button",
|
||||
id: "dropdownMenu",
|
||||
"data-toggle": "dropdown",
|
||||
"aria-haspopup": "true",
|
||||
"aria-expanded": "false"
|
||||
}
|
||||
},
|
||||
[_vm._v("\n\tSelect\n\t")]
|
||||
),
|
||||
_vm._v(" "),
|
||||
_c(
|
||||
"div",
|
||||
{
|
||||
staticClass: "dropdown-menu",
|
||||
attrs: { "aria-labelledby": "dropdownMenu" }
|
||||
},
|
||||
[
|
||||
_vm._l(_vm.rowData.actions, function(action) {
|
||||
return _c(
|
||||
"a",
|
||||
{ staticClass: "dropdown-item", attrs: { href: action.url } },
|
||||
[_vm._v(_vm._s(action.name))]
|
||||
)
|
||||
}),
|
||||
_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")]
|
||||
)
|
||||
],
|
||||
2
|
||||
)
|
||||
])
|
||||
}
|
||||
var staticRenderFns = []
|
||||
render._withStripped = true
|
||||
module.exports = { render: render, staticRenderFns: staticRenderFns }
|
||||
if (false) {
|
||||
module.hot.accept()
|
||||
if (module.hot.data) {
|
||||
require("vue-hot-reload-api") .rerender("data-v-d9a40d24", module.exports)
|
||||
}
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/vue-style-loader/index.js!./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-15965e3b\",\"scoped\":true,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./node_modules/vuetable-2/src/components/Vuetable.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
@ -5141,6 +5249,33 @@ if(false) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/vue-style-loader/index.js!./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
// style-loader: Adds some css to the DOM by adding a <style> tag
|
||||
|
||||
// load the styles
|
||||
var content = __webpack_require__("./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./resources/js/src/components/client/ClientActions.vue");
|
||||
if(typeof content === 'string') content = [[module.i, content, '']];
|
||||
if(content.locals) module.exports = content.locals;
|
||||
// add the styles to the DOM
|
||||
var update = __webpack_require__("./node_modules/vue-style-loader/lib/addStylesClient.js")("c5093156", content, false, {});
|
||||
// Hot Module Replacement
|
||||
if(false) {
|
||||
// When the styles change, update the <style> tags
|
||||
if(!content.locals) {
|
||||
module.hot.accept("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!../../../../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./ClientActions.vue", function() {
|
||||
var newContent = require("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!../../../../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./ClientActions.vue");
|
||||
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
|
||||
update(newContent);
|
||||
});
|
||||
}
|
||||
// When the module is disposed, remove the <style> tags
|
||||
module.hot.dispose(function() { update(); });
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/vue-style-loader/lib/addStylesClient.js":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
@ -18192,6 +18327,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//import * as Vue from 'vue';
|
||||
var vue_1 = __importDefault(__webpack_require__("./node_modules/vue/dist/vue.common.js"));
|
||||
vue_1.default.component('client-list', __webpack_require__("./resources/js/src/components/client/ClientList.vue"));
|
||||
vue_1.default.component('client-actions', __webpack_require__("./resources/js/src/components/client/ClientActions.vue"));
|
||||
vue_1.default.component('vuetable', __webpack_require__("./node_modules/vuetable-2/src/components/Vuetable.vue"));
|
||||
vue_1.default.component('vuetable-pagination', __webpack_require__("./node_modules/vuetable-2/src/components/VuetablePagination.vue"));
|
||||
vue_1.default.component('vuetable-pagination-bootstrap', __webpack_require__("./resources/js/src/components/util/VuetablePaginationBootstrap.vue"));
|
||||
@ -18203,6 +18339,58 @@ window.onload = function () {
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
var disposed = false
|
||||
function injectStyle (ssrContext) {
|
||||
if (disposed) return
|
||||
__webpack_require__("./node_modules/vue-style-loader/index.js!./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./resources/js/src/components/client/ClientActions.vue")
|
||||
}
|
||||
var normalizeComponent = __webpack_require__("./node_modules/vue-loader/lib/component-normalizer.js")
|
||||
/* script */
|
||||
var __vue_script__ = __webpack_require__("./node_modules/babel-loader/lib/index.js?{\"cacheDirectory\":true,\"presets\":[[\"env\",{\"modules\":false,\"targets\":{\"browsers\":[\"> 2%\"],\"uglify\":true}}]],\"plugins\":[\"transform-object-rest-spread\",[\"transform-runtime\",{\"polyfill\":false,\"helpers\":false}]]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./resources/js/src/components/client/ClientActions.vue")
|
||||
/* template */
|
||||
var __vue_template__ = __webpack_require__("./node_modules/vue-loader/lib/template-compiler/index.js?{\"id\":\"data-v-d9a40d24\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./resources/js/src/components/client/ClientActions.vue")
|
||||
/* template functional */
|
||||
var __vue_template_functional__ = false
|
||||
/* styles */
|
||||
var __vue_styles__ = injectStyle
|
||||
/* scopeId */
|
||||
var __vue_scopeId__ = null
|
||||
/* moduleIdentifier (server only) */
|
||||
var __vue_module_identifier__ = null
|
||||
var Component = normalizeComponent(
|
||||
__vue_script__,
|
||||
__vue_template__,
|
||||
__vue_template_functional__,
|
||||
__vue_styles__,
|
||||
__vue_scopeId__,
|
||||
__vue_module_identifier__
|
||||
)
|
||||
Component.options.__file = "resources/js/src/components/client/ClientActions.vue"
|
||||
|
||||
/* hot reload */
|
||||
if (false) {(function () {
|
||||
var hotAPI = require("vue-hot-reload-api")
|
||||
hotAPI.install(require("vue"), false)
|
||||
if (!hotAPI.compatible) return
|
||||
module.hot.accept()
|
||||
if (!module.hot.data) {
|
||||
hotAPI.createRecord("data-v-d9a40d24", Component.options)
|
||||
} else {
|
||||
hotAPI.reload("data-v-d9a40d24", Component.options)
|
||||
}
|
||||
module.hot.dispose(function (data) {
|
||||
disposed = true
|
||||
})
|
||||
})()}
|
||||
|
||||
module.exports = Component.exports
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/src/components/client/ClientList.vue":
|
||||
@ -18255,6 +18443,39 @@ if (false) {(function () {
|
||||
module.exports = Component.exports
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/src/components/util/VuetableCss.ts":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = {
|
||||
table: {
|
||||
tableClass: 'table table-bordered table-hover',
|
||||
loadingClass: 'loading',
|
||||
ascendingIcon: 'fa fa-angle-double-up',
|
||||
descendingIcon: 'fa fa-angle-double-down',
|
||||
handleIcon: 'glyphicon glyphicon-menu-hamburger',
|
||||
},
|
||||
pagination: {
|
||||
infoClass: 'pull-left',
|
||||
wrapperClass: 'vuetable-pagination pull-right',
|
||||
activeClass: 'btn-primary',
|
||||
disabledClass: 'disabled',
|
||||
pageClass: 'btn btn-border',
|
||||
linkClass: 'btn btn-border',
|
||||
icons: {
|
||||
first: '',
|
||||
prev: '',
|
||||
next: '',
|
||||
last: '',
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/src/components/util/VuetableFilterBar.vue":
|
||||
|
267
public/js/client_list.min.js
vendored
267
public/js/client_list.min.js
vendored
@ -1190,6 +1190,45 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/babel-loader/lib/index.js?{\"cacheDirectory\":true,\"presets\":[[\"env\",{\"modules\":false,\"targets\":{\"browsers\":[\"> 2%\"],\"uglify\":true}}]],\"plugins\":[\"transform-object-rest-spread\",[\"transform-runtime\",{\"polyfill\":false,\"helpers\":false}]]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
/* harmony default export */ __webpack_exports__["default"] = ({
|
||||
props: {
|
||||
rowData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
rowIndex: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
itemAction: function itemAction(action, data, index) {
|
||||
console.log('custom-actions: ' + action, data.name, index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/babel-loader/lib/index.js?{\"cacheDirectory\":true,\"presets\":[[\"env\",{\"modules\":false,\"targets\":{\"browsers\":[\"> 2%\"],\"uglify\":true}}]],\"plugins\":[\"transform-object-rest-spread\",[\"transform-runtime\",{\"polyfill\":false,\"helpers\":false}]]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./resources/js/src/components/util/VuetablePaginationBootstrap.vue":
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
@ -2694,6 +2733,21 @@ exports.push([module.i, "\n.form-inline > * {\n margin:5px 10px;\n}\n", ""]);
|
||||
// exports
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/css-base.js")(false);
|
||||
// imports
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/css-loader/lib/css-base.js":
|
||||
@ -3249,6 +3303,7 @@ var VuetablePagination_vue_1 = __importDefault(__webpack_require__("./node_modul
|
||||
var VuetablePaginationInfo_vue_1 = __importDefault(__webpack_require__("./node_modules/vuetable-2/src/components/VuetablePaginationInfo.vue"));
|
||||
var vue_1 = __importDefault(__webpack_require__("./node_modules/vue/dist/vue.common.js"));
|
||||
var vue_events_1 = __importDefault(__webpack_require__("./node_modules/vue-events/dist/index.js"));
|
||||
var VuetableCss_1 = __importDefault(__webpack_require__("./resources/js/src/components/util/VuetableCss.ts"));
|
||||
vue_1.default.use(vue_events_1.default);
|
||||
exports.default = {
|
||||
components: {
|
||||
@ -3258,6 +3313,7 @@ exports.default = {
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
css: VuetableCss_1.default,
|
||||
sortOrder: [
|
||||
{
|
||||
field: 'name',
|
||||
@ -3304,31 +3360,14 @@ exports.default = {
|
||||
name: 'balance',
|
||||
sortField: 'balance',
|
||||
dataClass: 'center aligned'
|
||||
}
|
||||
],
|
||||
css: {
|
||||
table: {
|
||||
tableClass: 'table table-striped table-bordered table-hovered',
|
||||
loadingClass: 'loading',
|
||||
ascendingIcon: 'glyphicon glyphicon-chevron-up',
|
||||
descendingIcon: 'glyphicon glyphicon-chevron-down',
|
||||
handleIcon: 'glyphicon glyphicon-menu-hamburger',
|
||||
},
|
||||
pagination: {
|
||||
infoClass: 'pull-left',
|
||||
wrapperClass: 'vuetable-pagination pull-right',
|
||||
activeClass: 'btn-primary',
|
||||
disabledClass: 'disabled',
|
||||
pageClass: 'btn btn-border',
|
||||
linkClass: 'btn btn-border',
|
||||
icons: {
|
||||
first: '',
|
||||
prev: '',
|
||||
next: '',
|
||||
last: '',
|
||||
},
|
||||
{
|
||||
name: '__component:client-actions',
|
||||
title: '',
|
||||
titleClass: 'center aligned',
|
||||
dataClass: 'center aligned'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
//props: ['list'],
|
||||
@ -4795,6 +4834,7 @@ var render = function() {
|
||||
"per-page": 20,
|
||||
"sort-order": _vm.sortOrder,
|
||||
"append-params": _vm.moreParams,
|
||||
css: _vm.css.table,
|
||||
"pagination-path": ""
|
||||
},
|
||||
on: { "vuetable:pagination-data": _vm.onPaginationData }
|
||||
@ -5060,6 +5100,74 @@ if (false) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/vue-loader/lib/template-compiler/index.js?{\"id\":\"data-v-d9a40d24\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
var render = function() {
|
||||
var _vm = this
|
||||
var _h = _vm.$createElement
|
||||
var _c = _vm._self._c || _h
|
||||
return _c("div", { staticClass: "dropdown" }, [
|
||||
_c(
|
||||
"button",
|
||||
{
|
||||
staticClass: "btn btn-secondary dropdown-toggle",
|
||||
attrs: {
|
||||
type: "button",
|
||||
id: "dropdownMenu",
|
||||
"data-toggle": "dropdown",
|
||||
"aria-haspopup": "true",
|
||||
"aria-expanded": "false"
|
||||
}
|
||||
},
|
||||
[_vm._v("\n\tSelect\n\t")]
|
||||
),
|
||||
_vm._v(" "),
|
||||
_c(
|
||||
"div",
|
||||
{
|
||||
staticClass: "dropdown-menu",
|
||||
attrs: { "aria-labelledby": "dropdownMenu" }
|
||||
},
|
||||
[
|
||||
_vm._l(_vm.rowData.actions, function(action) {
|
||||
return _c(
|
||||
"a",
|
||||
{ staticClass: "dropdown-item", attrs: { href: action.url } },
|
||||
[_vm._v(_vm._s(action.name))]
|
||||
)
|
||||
}),
|
||||
_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")]
|
||||
)
|
||||
],
|
||||
2
|
||||
)
|
||||
])
|
||||
}
|
||||
var staticRenderFns = []
|
||||
render._withStripped = true
|
||||
module.exports = { render: render, staticRenderFns: staticRenderFns }
|
||||
if (false) {
|
||||
module.hot.accept()
|
||||
if (module.hot.data) {
|
||||
require("vue-hot-reload-api") .rerender("data-v-d9a40d24", module.exports)
|
||||
}
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/vue-style-loader/index.js!./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-15965e3b\",\"scoped\":true,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./node_modules/vuetable-2/src/components/Vuetable.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
@ -5141,6 +5249,33 @@ if(false) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/vue-style-loader/index.js!./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
// style-loader: Adds some css to the DOM by adding a <style> tag
|
||||
|
||||
// load the styles
|
||||
var content = __webpack_require__("./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./resources/js/src/components/client/ClientActions.vue");
|
||||
if(typeof content === 'string') content = [[module.i, content, '']];
|
||||
if(content.locals) module.exports = content.locals;
|
||||
// add the styles to the DOM
|
||||
var update = __webpack_require__("./node_modules/vue-style-loader/lib/addStylesClient.js")("c5093156", content, false, {});
|
||||
// Hot Module Replacement
|
||||
if(false) {
|
||||
// When the styles change, update the <style> tags
|
||||
if(!content.locals) {
|
||||
module.hot.accept("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!../../../../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./ClientActions.vue", function() {
|
||||
var newContent = require("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!../../../../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./ClientActions.vue");
|
||||
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
|
||||
update(newContent);
|
||||
});
|
||||
}
|
||||
// When the module is disposed, remove the <style> tags
|
||||
module.hot.dispose(function() { update(); });
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./node_modules/vue-style-loader/lib/addStylesClient.js":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
@ -18192,6 +18327,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//import * as Vue from 'vue';
|
||||
var vue_1 = __importDefault(__webpack_require__("./node_modules/vue/dist/vue.common.js"));
|
||||
vue_1.default.component('client-list', __webpack_require__("./resources/js/src/components/client/ClientList.vue"));
|
||||
vue_1.default.component('client-actions', __webpack_require__("./resources/js/src/components/client/ClientActions.vue"));
|
||||
vue_1.default.component('vuetable', __webpack_require__("./node_modules/vuetable-2/src/components/Vuetable.vue"));
|
||||
vue_1.default.component('vuetable-pagination', __webpack_require__("./node_modules/vuetable-2/src/components/VuetablePagination.vue"));
|
||||
vue_1.default.component('vuetable-pagination-bootstrap', __webpack_require__("./resources/js/src/components/util/VuetablePaginationBootstrap.vue"));
|
||||
@ -18203,6 +18339,58 @@ window.onload = function () {
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/src/components/client/ClientActions.vue":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
var disposed = false
|
||||
function injectStyle (ssrContext) {
|
||||
if (disposed) return
|
||||
__webpack_require__("./node_modules/vue-style-loader/index.js!./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-d9a40d24\",\"scoped\":false,\"hasInlineConfig\":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./resources/js/src/components/client/ClientActions.vue")
|
||||
}
|
||||
var normalizeComponent = __webpack_require__("./node_modules/vue-loader/lib/component-normalizer.js")
|
||||
/* script */
|
||||
var __vue_script__ = __webpack_require__("./node_modules/babel-loader/lib/index.js?{\"cacheDirectory\":true,\"presets\":[[\"env\",{\"modules\":false,\"targets\":{\"browsers\":[\"> 2%\"],\"uglify\":true}}]],\"plugins\":[\"transform-object-rest-spread\",[\"transform-runtime\",{\"polyfill\":false,\"helpers\":false}]]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./resources/js/src/components/client/ClientActions.vue")
|
||||
/* template */
|
||||
var __vue_template__ = __webpack_require__("./node_modules/vue-loader/lib/template-compiler/index.js?{\"id\":\"data-v-d9a40d24\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./resources/js/src/components/client/ClientActions.vue")
|
||||
/* template functional */
|
||||
var __vue_template_functional__ = false
|
||||
/* styles */
|
||||
var __vue_styles__ = injectStyle
|
||||
/* scopeId */
|
||||
var __vue_scopeId__ = null
|
||||
/* moduleIdentifier (server only) */
|
||||
var __vue_module_identifier__ = null
|
||||
var Component = normalizeComponent(
|
||||
__vue_script__,
|
||||
__vue_template__,
|
||||
__vue_template_functional__,
|
||||
__vue_styles__,
|
||||
__vue_scopeId__,
|
||||
__vue_module_identifier__
|
||||
)
|
||||
Component.options.__file = "resources/js/src/components/client/ClientActions.vue"
|
||||
|
||||
/* hot reload */
|
||||
if (false) {(function () {
|
||||
var hotAPI = require("vue-hot-reload-api")
|
||||
hotAPI.install(require("vue"), false)
|
||||
if (!hotAPI.compatible) return
|
||||
module.hot.accept()
|
||||
if (!module.hot.data) {
|
||||
hotAPI.createRecord("data-v-d9a40d24", Component.options)
|
||||
} else {
|
||||
hotAPI.reload("data-v-d9a40d24", Component.options)
|
||||
}
|
||||
module.hot.dispose(function (data) {
|
||||
disposed = true
|
||||
})
|
||||
})()}
|
||||
|
||||
module.exports = Component.exports
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/src/components/client/ClientList.vue":
|
||||
@ -18255,6 +18443,39 @@ if (false) {(function () {
|
||||
module.exports = Component.exports
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/src/components/util/VuetableCss.ts":
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = {
|
||||
table: {
|
||||
tableClass: 'table table-bordered table-hover',
|
||||
loadingClass: 'loading',
|
||||
ascendingIcon: 'fa fa-angle-double-up',
|
||||
descendingIcon: 'fa fa-angle-double-down',
|
||||
handleIcon: 'glyphicon glyphicon-menu-hamburger',
|
||||
},
|
||||
pagination: {
|
||||
infoClass: 'pull-left',
|
||||
wrapperClass: 'vuetable-pagination pull-right',
|
||||
activeClass: 'btn-primary',
|
||||
disabledClass: 'disabled',
|
||||
pageClass: 'btn btn-border',
|
||||
linkClass: 'btn btn-border',
|
||||
icons: {
|
||||
first: '',
|
||||
prev: '',
|
||||
next: '',
|
||||
last: '',
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/js/src/components/util/VuetableFilterBar.vue":
|
||||
|
@ -3,6 +3,7 @@ import Vue from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
Vue.component('client-list', require('../components/client/ClientList.vue'));
|
||||
Vue.component('client-actions', require('../components/client/ClientActions.vue'));
|
||||
Vue.component('vuetable', require('vuetable-2/src/components/Vuetable'));
|
||||
Vue.component('vuetable-pagination', require('vuetable-2/src/components/VuetablePagination'));
|
||||
Vue.component('vuetable-pagination-bootstrap', require('../components/util/VuetablePaginationBootstrap'));
|
||||
|
41
resources/js/src/components/client/ClientActions.vue
Normal file
41
resources/js/src/components/client/ClientActions.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
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>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
rowData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
rowIndex: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
itemAction (action, data, index) {
|
||||
console.log('custom-actions: ' + action, data.name, index)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.custom-actions button.ui.button {
|
||||
padding: 8px 8px;
|
||||
}
|
||||
.custom-actions button.ui.button > i.icon {
|
||||
margin: auto !important;
|
||||
}
|
||||
</style>
|
@ -9,6 +9,7 @@
|
||||
:per-page="20"
|
||||
:sort-order="sortOrder"
|
||||
:append-params="moreParams"
|
||||
:css="css.table"
|
||||
pagination-path=""
|
||||
@vuetable:pagination-data="onPaginationData"></vuetable>
|
||||
|
||||
@ -33,6 +34,7 @@ import VuetablePagination from 'vuetable-2/src/components/VuetablePagination.vue
|
||||
import VuetablePaginationInfo from 'vuetable-2/src/components/VuetablePaginationInfo.vue'
|
||||
import Vue from 'vue'
|
||||
import VueEvents from 'vue-events'
|
||||
import VuetableCss from '../util/VuetableCss'
|
||||
|
||||
Vue.use(VueEvents)
|
||||
|
||||
@ -44,6 +46,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
css: VuetableCss,
|
||||
sortOrder: [
|
||||
{
|
||||
field: 'name',
|
||||
@ -90,31 +93,14 @@ export default {
|
||||
name: 'balance',
|
||||
sortField: 'balance',
|
||||
dataClass: 'center aligned'
|
||||
}
|
||||
],
|
||||
css: {
|
||||
table: {
|
||||
tableClass: 'table table-striped table-bordered table-hovered',
|
||||
loadingClass: 'loading',
|
||||
ascendingIcon: 'glyphicon glyphicon-chevron-up',
|
||||
descendingIcon: 'glyphicon glyphicon-chevron-down',
|
||||
handleIcon: 'glyphicon glyphicon-menu-hamburger',
|
||||
},
|
||||
pagination: {
|
||||
infoClass: 'pull-left',
|
||||
wrapperClass: 'vuetable-pagination pull-right',
|
||||
activeClass: 'btn-primary',
|
||||
disabledClass: 'disabled',
|
||||
pageClass: 'btn btn-border',
|
||||
linkClass: 'btn btn-border',
|
||||
icons: {
|
||||
first: '',
|
||||
prev: '',
|
||||
next: '',
|
||||
last: '',
|
||||
},
|
||||
{
|
||||
name: '__component:client-actions', // <----
|
||||
title: '',
|
||||
titleClass: 'center aligned',
|
||||
dataClass: 'center aligned'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
//props: ['list'],
|
||||
|
23
resources/js/src/components/util/VuetableCss.ts
Normal file
23
resources/js/src/components/util/VuetableCss.ts
Normal file
@ -0,0 +1,23 @@
|
||||
export default {
|
||||
table: {
|
||||
tableClass: 'table table-bordered table-hover',
|
||||
loadingClass: 'loading',
|
||||
ascendingIcon: 'fa fa-angle-double-up',
|
||||
descendingIcon: 'fa fa-angle-double-down',
|
||||
handleIcon: 'glyphicon glyphicon-menu-hamburger',
|
||||
},
|
||||
pagination: {
|
||||
infoClass: 'pull-left',
|
||||
wrapperClass: 'vuetable-pagination pull-right',
|
||||
activeClass: 'btn-primary',
|
||||
disabledClass: 'disabled',
|
||||
pageClass: 'btn btn-border',
|
||||
linkClass: 'btn btn-border',
|
||||
icons: {
|
||||
first: '',
|
||||
prev: '',
|
||||
next: '',
|
||||
last: '',
|
||||
},
|
||||
}
|
||||
}
|
@ -41,10 +41,14 @@ Route::group(['middleware' => ['auth:user', 'db']], function () {
|
||||
|
||||
Route::resource('dashboard', 'DashboardController'); // name = (dashboard. index / create / show / update / destroy / edit
|
||||
Route::get('logout', 'Auth\LoginController@logout')->name('user.logout');
|
||||
Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit
|
||||
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::resource('user', 'UserProfileController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::get('settings', 'SettingsController@index')->name('user.settings');
|
||||
Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit
|
||||
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
|
||||
Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit
|
||||
Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit
|
||||
Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit
|
||||
Route::resource('user', 'UserProfileController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::get('settings', 'SettingsController@index')->name('user.settings');
|
||||
|
||||
|
||||
|
||||
|
@ -5,10 +5,10 @@ namespace Tests\Feature;
|
||||
use App\Models\Account;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class LoginTest extends TestCase
|
||||
{
|
||||
|
138
tests/Unit/CompareCollectionTest.php
Normal file
138
tests/Unit/CompareCollectionTest.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Utils\NumberHelper
|
||||
*/
|
||||
class CompareCollectionTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->map = collect([
|
||||
['action' => 'view_client_client_id', 'permission' => 'view_client', 'route' => 'clients.show', 'key' => 'client_id', 'name' => trans('texts.view')],
|
||||
['action' => 'edit_client_client_id', 'permission' => 'edit_client', 'route' => 'clients.edit', 'key' => 'client_id', 'name' => trans('texts.edit')],
|
||||
['action' => 'create_task_client_id', 'permission' => 'create_task', 'route' => 'task.create', 'key' => 'client_id', 'name' => trans('texts.new_task')],
|
||||
['action' => 'create_invoice_client_id', 'permission' => 'create_invoice', 'route' => 'invoice.create', 'key' => 'client_id', 'name' => trans('texts.new_invoice')],
|
||||
['action' => 'enter_payment_client_id', 'permission' => 'create_payment', 'route' => 'payment.create', 'key' => 'client_id', 'name' => trans('texts.enter_payment')],
|
||||
['action' => 'enter_credit_client_id', 'permission' => 'create_credit', 'route' => 'credit.create', 'key' => 'client_id', 'name' => trans('texts.enter_credit')],
|
||||
['action' => 'enter_expense_client_id', 'permission' => 'create_expense', 'route' => 'expense.create', 'key' => 'client_id', 'name' => trans('texts.enter_expense')]
|
||||
]);
|
||||
|
||||
$this->view_permission = ['view_client'];
|
||||
|
||||
$this->edit_permission = ['view_client', 'edit_client'];
|
||||
|
||||
$this->is_admin = true;
|
||||
|
||||
$this->is_not_admin = false;
|
||||
|
||||
}
|
||||
|
||||
public function testCompareResultOfComparison()
|
||||
{
|
||||
|
||||
$this->assertEquals(7, $this->map->count());
|
||||
|
||||
}
|
||||
|
||||
public function testViewPermission()
|
||||
{
|
||||
|
||||
$this->assertEquals(1, $this->checkPermissions($this->view_permission, $this->is_not_admin)->count());
|
||||
|
||||
}
|
||||
|
||||
public function testViewAndEditPermission()
|
||||
{
|
||||
|
||||
$this->assertEquals(2, $this->checkPermissions($this->edit_permission, $this->is_not_admin)->count());
|
||||
|
||||
}
|
||||
|
||||
public function testAdminPermissions()
|
||||
{
|
||||
|
||||
$this->assertEquals(7, $this->checkPermissions($this->view_permission, $this->is_admin)->count());
|
||||
|
||||
}
|
||||
|
||||
public function testActionViewClientFilter()
|
||||
{
|
||||
$actions = [
|
||||
'view_client_client_id'
|
||||
];
|
||||
|
||||
$this->assertEquals(1, $this->map->whereIn('action', $actions)->count());
|
||||
}
|
||||
|
||||
public function testNoActionClientFilter()
|
||||
{
|
||||
$actions = [
|
||||
''
|
||||
];
|
||||
|
||||
$this->assertEquals(0, $this->map->whereIn('action', $actions)->count());
|
||||
}
|
||||
|
||||
public function testActionsAndPermissionsFilter()
|
||||
{
|
||||
$actions = [
|
||||
'view_client_client_id'
|
||||
|
||||
];
|
||||
|
||||
$this->filterActions($actions);
|
||||
|
||||
$this->assertEquals(1, $this->checkPermissions($this->view_permission, $this->is_not_admin)->count());
|
||||
|
||||
}
|
||||
|
||||
public function testActionAndPermissionsFilterFailure()
|
||||
{
|
||||
$actions = [
|
||||
'edit_client_client_id'
|
||||
|
||||
];
|
||||
|
||||
$data = $this->filterActions($actions);
|
||||
|
||||
$this->assertEquals(0, $data->whereIn('permission', $this->view_permission)->count());
|
||||
|
||||
}
|
||||
|
||||
public function testEditActionAndPermissionsFilter()
|
||||
{
|
||||
$actions = [
|
||||
'edit_client_client_id'
|
||||
|
||||
];
|
||||
|
||||
$data = $this->filterActions($actions);
|
||||
$this->assertEquals(1, $data->whereIn('permission', $this->edit_permission)->count());
|
||||
|
||||
}
|
||||
|
||||
public function checkPermissions($permission, $is_admin)
|
||||
{
|
||||
|
||||
if($is_admin === TRUE)
|
||||
return $this->map;
|
||||
|
||||
return $this->map->whereIn('permission', $permission);
|
||||
|
||||
}
|
||||
|
||||
public function filterActions($actions)
|
||||
{
|
||||
return $this->map->whereIn('action', $actions);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user