User Settings (#2601)
* Datamapping JSON Settings * JSON Mapping * User Setting Defaults * Testing Json Mapper * Implemented User Settings - hydrated from JSON format
This commit is contained in:
parent
9204510193
commit
feafbd9826
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\DataMapper;
|
||||
|
||||
use App\Models\Client;
|
||||
|
||||
class DefaultSettings
|
||||
{
|
||||
|
||||
public static $per_page = 20;
|
||||
|
||||
public static function userSettings() : \stdClass
|
||||
{
|
||||
return (object)[
|
||||
class_basename(Client::class) => self::clientSettings(),
|
||||
];
|
||||
}
|
||||
|
||||
private static function clientSettings() : \stdClass
|
||||
{
|
||||
|
||||
return (object)[
|
||||
'datatable' => (object) [
|
||||
'per_page' => self::$per_page,
|
||||
'column_visibility' => (object)[
|
||||
'__checkbox' => true,
|
||||
'name' => true,
|
||||
'contact' => true,
|
||||
'email' => true,
|
||||
'client_created_at' => true,
|
||||
'last_login' => true,
|
||||
'balance' => true,
|
||||
'__component:client-actions' => true
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ use App\Models\Client;
|
|||
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;
|
||||
|
||||
|
|
@ -157,8 +158,11 @@ class ClientDatatable extends EntityDatatable
|
|||
|
||||
}
|
||||
|
||||
public function buildOptions()
|
||||
public function buildOptions() : Collection
|
||||
{
|
||||
|
||||
$visible = auth()->user()->getColumnVisibility(Client::class);
|
||||
|
||||
return collect([
|
||||
'per_page' => 20,
|
||||
'sort_order' => [
|
||||
|
|
@ -170,53 +174,59 @@ class ClientDatatable extends EntityDatatable
|
|||
],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => '__checkbox', // <----
|
||||
'name' => '__checkbox',
|
||||
'title' => '',
|
||||
'titleClass' => 'center aligned',
|
||||
'visible' => $visible->__checkbox,
|
||||
'dataClass' => 'center aligned'
|
||||
],
|
||||
[
|
||||
'name' => 'name',
|
||||
'title' => trans('texts.name'),
|
||||
'sortField' => 'name',
|
||||
'visible' => false,
|
||||
'visible' => $visible->name,
|
||||
'dataClass' => 'center aligned'
|
||||
],
|
||||
[
|
||||
'name' => 'contact',
|
||||
'title' => trans('texts.contact'),
|
||||
'sortField' => 'contact',
|
||||
'visible' => false,
|
||||
'visible' => $visible->contact,
|
||||
'dataClass' => 'center aligned'
|
||||
],
|
||||
[
|
||||
'name' => 'email',
|
||||
'title' => trans('texts.email'),
|
||||
'sortField' => 'email',
|
||||
'visible' => $visible->email,
|
||||
'dataClass' => 'center aligned'
|
||||
],
|
||||
[
|
||||
'name' => 'client_created_at',
|
||||
'title' => trans('texts.date_created'),
|
||||
'sortField' => 'client_created_at',
|
||||
'visible' => $visible->client_created_at,
|
||||
'dataClass' => 'center aligned'
|
||||
],
|
||||
[
|
||||
'name' => 'last_login',
|
||||
'title' => trans('texts.last_login'),
|
||||
'sortField' => 'last_login',
|
||||
'visible' => $visible->last_login,
|
||||
'dataClass' => 'center aligned'
|
||||
],
|
||||
[
|
||||
'name' => 'balance',
|
||||
'title' => trans('texts.balance'),
|
||||
'sortField' => 'balance',
|
||||
'visible' => $visible->balance,
|
||||
'dataClass' => 'center aligned'
|
||||
],
|
||||
[
|
||||
'name' => '__component:client-actions',
|
||||
'title' => '',
|
||||
'titleClass' => 'center aligned',
|
||||
'visible' => true,
|
||||
'dataClass' => 'center aligned'
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,6 +6,16 @@ class CompanyUser extends BaseModel
|
|||
{
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'settings' => 'collection',
|
||||
];
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->hasOne(Account::class);
|
||||
|
|
@ -13,11 +23,11 @@ class CompanyUser extends BaseModel
|
|||
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(User::class)->withPivot('permissions');
|
||||
return $this->hasOne(User::class)->withPivot('permissions', 'settings');
|
||||
}
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->hasOne(Company::class)->withPivot('permissions');
|
||||
return $this->hasOne(Company::class)->withPivot('permissions', 'settings');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Models;
|
|||
use App\Models\Traits\UserTrait;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use App\Utils\Traits\UserSettings;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
|
|
@ -18,6 +19,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
use PresentableTrait;
|
||||
use MakesHash;
|
||||
use UserSessionAttributes;
|
||||
use UserSettings;
|
||||
|
||||
protected $guard = 'user';
|
||||
|
||||
|
|
@ -55,16 +57,31 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
'slack_webhook_url',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns all companies a user has access to.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function companies()
|
||||
{
|
||||
return $this->belongsToMany(Company::class)->withPivot('permissions');
|
||||
return $this->belongsToMany(Company::class)->withPivot('permissions','settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current company
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function company()
|
||||
{
|
||||
return $this->companies()->where('company_id', $this->getCurrentCompanyId())->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a object of user permissions
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function permissions()
|
||||
{
|
||||
|
||||
|
|
@ -76,26 +93,63 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a object of User Settings
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function settings()
|
||||
{
|
||||
return json_decode($this->company()->pivot->settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean of the administrator status of the user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_admin()
|
||||
{
|
||||
return $this->company()->pivot->is_admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all user created contacts
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function contacts()
|
||||
{
|
||||
return $this->hasMany(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean value if the user owns the current Entity
|
||||
*
|
||||
* @param string Entity
|
||||
* @return bool
|
||||
*/
|
||||
public function owns($entity) : bool
|
||||
{
|
||||
return ! empty($entity->user_id) && $entity->user_id == $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens a stdClass representation of the User Permissions
|
||||
* into a Collection
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function permissionsFlat()
|
||||
{
|
||||
return collect($this->permissions())->flatten();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a array of permission for the mobile application
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function permissionsMap()
|
||||
{
|
||||
|
||||
|
|
@ -104,4 +158,5 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
|
||||
return array_combine($keys, $values);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1761,16 +1761,16 @@
|
|||
},
|
||||
{
|
||||
"name": "opis/closure",
|
||||
"version": "3.1.3",
|
||||
"version": "3.1.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opis/closure.git",
|
||||
"reference": "5e9095ce871a425ab87a854b285b7766de38a7d9"
|
||||
"reference": "41f5da65d75cf473e5ee582df8fc7f2c733ce9d6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/opis/closure/zipball/5e9095ce871a425ab87a854b285b7766de38a7d9",
|
||||
"reference": "5e9095ce871a425ab87a854b285b7766de38a7d9",
|
||||
"url": "https://api.github.com/repos/opis/closure/zipball/41f5da65d75cf473e5ee582df8fc7f2c733ce9d6",
|
||||
"reference": "41f5da65d75cf473e5ee582df8fc7f2c733ce9d6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1818,7 +1818,7 @@
|
|||
"serialization",
|
||||
"serialize"
|
||||
],
|
||||
"time": "2019-01-06T22:07:38+00:00"
|
||||
"time": "2019-01-14T14:45:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
|
|
|
|||
|
|
@ -227,7 +227,6 @@ return [
|
|||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
'Datatables' => Yajra\Datatables\Facades\Datatables::class,
|
||||
|
||||
/*
|
||||
* Dependency Facades
|
||||
|
|
|
|||
|
|
@ -151,6 +151,12 @@ class CreateUsersTable extends Migration
|
|||
$table->string('custom_label2')->nullable();
|
||||
$table->string('custom_value2')->nullable();
|
||||
|
||||
$table->string('custom_label3')->nullable();
|
||||
$table->string('custom_value3')->nullable();
|
||||
|
||||
$table->string('custom_label4')->nullable();
|
||||
$table->string('custom_value4')->nullable();
|
||||
|
||||
$table->string('custom_client_label1')->nullable();
|
||||
$table->string('custom_client_label2')->nullable();
|
||||
|
||||
|
|
@ -191,6 +197,7 @@ class CreateUsersTable extends Migration
|
|||
$table->unsignedInteger('account_id');
|
||||
$table->unsignedInteger('user_id')->index();
|
||||
$table->text('permissions');
|
||||
$table->text('settings');
|
||||
$table->boolean('is_owner')->default(false);
|
||||
$table->boolean('is_admin');
|
||||
$table->boolean('is_locked')->default(false); // locks user out of account
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
|
|
@ -47,11 +48,14 @@ class UsersTableSeeder extends Seeder
|
|||
'create_client'
|
||||
]);
|
||||
|
||||
$userSettings = DefaultSettings::userSettings();
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'permissions' => $userPermissions->toJson(),
|
||||
'settings' => json_encode($userSettings),
|
||||
'is_locked' => 0,
|
||||
]);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -4326,7 +4326,7 @@ exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/cs
|
|||
|
||||
|
||||
// module
|
||||
exports.push([module.i, "\n.pagination {\n margin: 0;\n float: right;\n}\n.pagination a.page {\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 10px;\n margin-right: 2px;\n}\n.pagination a.page.active {\n color: white;\n background-color: #337ab7;\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 10px;\n margin-right: 2px;\n}\n.pagination a.btn-nav {\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 7px;\n margin-right: 2px;\n}\n.pagination a.btn-nav.disabled {\n color: lightgray;\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 7px;\n margin-right: 2px;\n cursor: not-allowed;\n}\n.pagination-info {\n float: left;\n}\nth {\n background: #777777;\n color: #fff;\n}\n.sortable th i:hover {\n color: #fff;\n}\n\n", ""]);
|
||||
exports.push([module.i, "\n.pagination {\n margin: 0;\n float: right;\n}\n.pagination a.page {\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 10px;\n margin-right: 2px;\n}\n.pagination a.page.active {\n color: white;\n background-color: #337ab7;\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 10px;\n margin-right: 2px;\n}\n.pagination a.btn-nav {\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 7px;\n margin-right: 2px;\n}\n.pagination a.btn-nav.disabled {\n color: lightgray;\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 7px;\n margin-right: 2px;\n cursor: not-allowed;\n}\n.pagination-info {\n float: left;\n}\nth {\n background: #777777;\n color: #fff;\n}\n\n", ""]);
|
||||
|
||||
// exports
|
||||
|
||||
|
|
@ -22540,7 +22540,6 @@ exports.clearImmediate = (typeof self !== "undefined" && self.clearImmediate) ||
|
|||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = {
|
||||
mounted: function () {
|
||||
console.dir('loaded');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4326,7 +4326,7 @@ exports = module.exports = __webpack_require__("./node_modules/css-loader/lib/cs
|
|||
|
||||
|
||||
// module
|
||||
exports.push([module.i, "\n.pagination {\n margin: 0;\n float: right;\n}\n.pagination a.page {\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 10px;\n margin-right: 2px;\n}\n.pagination a.page.active {\n color: white;\n background-color: #337ab7;\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 10px;\n margin-right: 2px;\n}\n.pagination a.btn-nav {\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 7px;\n margin-right: 2px;\n}\n.pagination a.btn-nav.disabled {\n color: lightgray;\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 7px;\n margin-right: 2px;\n cursor: not-allowed;\n}\n.pagination-info {\n float: left;\n}\nth {\n background: #777777;\n color: #fff;\n}\n.sortable th i:hover {\n color: #fff;\n}\n\n", ""]);
|
||||
exports.push([module.i, "\n.pagination {\n margin: 0;\n float: right;\n}\n.pagination a.page {\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 10px;\n margin-right: 2px;\n}\n.pagination a.page.active {\n color: white;\n background-color: #337ab7;\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 10px;\n margin-right: 2px;\n}\n.pagination a.btn-nav {\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 7px;\n margin-right: 2px;\n}\n.pagination a.btn-nav.disabled {\n color: lightgray;\n border: 1px solid lightgray;\n border-radius: 3px;\n padding: 5px 7px;\n margin-right: 2px;\n cursor: not-allowed;\n}\n.pagination-info {\n float: left;\n}\nth {\n background: #777777;\n color: #fff;\n}\n\n", ""]);
|
||||
|
||||
// exports
|
||||
|
||||
|
|
@ -22540,7 +22540,6 @@ exports.clearImmediate = (typeof self !== "undefined" && self.clearImmediate) ||
|
|||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = {
|
||||
mounted: function () {
|
||||
console.dir('loaded');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -12,7 +12,7 @@ import Vue from 'vue'
|
|||
export default {
|
||||
|
||||
mounted() {
|
||||
console.dir('loaded');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,8 +135,5 @@ export default {
|
|||
background: #777777;
|
||||
color: #fff;
|
||||
}
|
||||
.sortable th i:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\DataMapper\DefaultSettings
|
||||
*/
|
||||
class DefaultTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
|
||||
parent::setUp();
|
||||
|
||||
}
|
||||
|
||||
public function testDefaultUserSettings()
|
||||
{
|
||||
$user_settings = DefaultSettings::userSettings();
|
||||
|
||||
$this->assertEquals($user_settings->Client->datatable->per_page, 20);
|
||||
}
|
||||
|
||||
public function testIsObject()
|
||||
{
|
||||
$user_settings = DefaultSettings::userSettings();
|
||||
|
||||
$this->assertInternalType('object',$user_settings->Client->datatable->column_visibility);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Models\Client;
|
||||
use Tests\TestCase;
|
||||
|
||||
class EvaluateStringTest extends TestCase
|
||||
{
|
||||
|
||||
public function testClassNameResolution()
|
||||
{
|
||||
$this->assertEquals(class_basename(Client::class), 'Client');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Utils\NumberHelper
|
||||
*/
|
||||
class NestedCollectionTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->map = (object)[
|
||||
'client' => (object)[
|
||||
'datatable' => (object)[
|
||||
'per_page' => 20,
|
||||
'column_visibility' => (object)[
|
||||
'__checkbox' => true,
|
||||
'name' => true,
|
||||
'contact' => true,
|
||||
'email' => true,
|
||||
'client_created_at' => true,
|
||||
'last_login' => true,
|
||||
'balance' => true,
|
||||
'__component:client-actions' => true,
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testPerPageAttribute()
|
||||
{
|
||||
$this->assertEquals($this->map->client->datatable->per_page, 20);
|
||||
}
|
||||
|
||||
public function testNameAttributeVisibility()
|
||||
{
|
||||
$this->assertEquals($this->map->client->datatable->column_visibility->name, true);
|
||||
}
|
||||
|
||||
public function testStringAsEntityProperty()
|
||||
{
|
||||
$entity = 'client';
|
||||
|
||||
$this->assertEquals($this->map->{$entity}->datatable->column_visibility->name, true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue