diff --git a/CloudVeilManager/app/Http/Controllers/Admin/AppUserActivationCrudController.php b/CloudVeilManager/app/Http/Controllers/Admin/AppUserActivationCrudController.php index 322a941..9a24ffe 100644 --- a/CloudVeilManager/app/Http/Controllers/Admin/AppUserActivationCrudController.php +++ b/CloudVeilManager/app/Http/Controllers/Admin/AppUserActivationCrudController.php @@ -4,6 +4,8 @@ use Backpack\CRUD\app\Http\Controllers\CrudController; use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD; +use App\Models\User; +use App\Models\SystemPlatform; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; @@ -23,7 +25,7 @@ class AppUserActivationCrudController extends CrudController } - /** + /** * Configure the CrudPanel object. Apply settings to all operations. * * @return void @@ -45,67 +47,56 @@ protected function setupListOperation() { $this->crud->setColumns([ [ - 'label' => 'User', - 'type' => 'select', - 'name' => 'user_id', - 'attribute' => 'name', - 'key' => 'user_name', + 'label' => 'Device', + 'type' => 'custom_html', + 'name' => 'device', + 'value' => function ($entry) { + $friendlyName = $entry->friendly_name ?: '-'; + $deviceId = $entry->device_id ?: '-'; + return '
' . e($friendlyName) . '
' . e($deviceId) . '
'; + }, + 'escaped' => false, 'priority' => 1 ], [ - 'label' => 'E-Mail', - 'type' => 'select', + 'label' => 'User', + 'type' => 'custom_html', 'name' => 'user_id', - 'attribute' => 'email', - 'key' => 'user_email', - 'priority' => 2 - ], - [ - 'label' => 'Device ID', - 'type' => 'text', - 'name' => 'device_id', - 'priority' => 1 - ], - [ - 'label' => 'IP Address', - 'type' => 'text', - 'name' => 'ip_address', + 'value' => function ($entry) { + $user = $entry->user; + if (!$user) { + return '-'; + } + return '
' . e($user->name) . '
' . e($user->email) . '
'; + }, + 'escaped' => false, 'priority' => 1 ], [ - 'label' => 'Bypass', - 'type' => 'text', - 'name' => 'bypass_formatted', + 'label' => 'Group', + 'type' => 'select', + 'name' => 'group_id', + 'attribute' => 'name', + 'entity' => 'group', + 'key' => 'group_name', 'priority' => 1 ], [ 'label' => 'Version', - 'type' => 'text', - 'name' => 'app_version', - 'priority' => 1 - ], - [ - 'label' => 'Updated at', - 'type' => 'datetime', - 'name' => 'updated_at', - 'priority' => 1 - ], - [ - 'label' => 'OS', - 'type' => 'text', - 'name' => 'os_formatted', + 'type' => 'custom_html', + 'name' => 'version', + 'value' => function ($entry) { + $appVersion = $entry->app_version ?: '-'; + $osFormatted = $entry->os_formatted ?: '-'; + return '
' . e($appVersion) . '
' . e($osFormatted) . '
'; + }, + 'escaped' => false, 'priority' => 1, - 'orderable' => true, + 'orderable' => true, 'orderLogic' => function ($query, $column, $columnDirection) { return $query->orderBy('platform_name', $columnDirection); } ], - [ - 'label' => 'Friendly Name', - 'type' => 'text', - 'name' => 'friendly_name', - 'priority' => 2 - ], [ 'label' => 'Identifier', 'type' => 'hidden', @@ -113,6 +104,59 @@ protected function setupListOperation() 'searchLogic' => 'text' ] ]); + + // Platform filter + CRUD::filter('platform_name') + ->type('dropdown') + ->label('Platform') + ->values([ + SystemPlatform::PLATFORM_WIN => 'Windows', + SystemPlatform::PLATFORM_OSX => 'macOS', + ]) + ->whenActive(function ($value) { + CRUD::addClause('where', 'platform_name', $value); + }); + + // App Version filter + CRUD::filter('app_version') + ->type('text') + ->label('App Version') + ->whenActive(function ($value) { + CRUD::addClause('where', 'app_version', 'LIKE', "%{$value}%"); + }); + + // User filter + CRUD::filter('user_id') + ->type('select2') + ->label('User') + ->values(function () { + return User::all()->pluck('name', 'id')->toArray(); + }) + ->whenActive(function ($value) { + CRUD::addClause('where', 'user_id', $value); + }); + + // Last Sync Time filter + CRUD::filter('last_sync_time') + ->type('date_range') + ->label('Last Sync Time') + ->whenActive(function ($value) { + $dates = json_decode($value); + if ($dates->from) { + CRUD::addClause('where', 'last_sync_time', '>=', $dates->from); + } + if ($dates->to) { + CRUD::addClause('where', 'last_sync_time', '<=', $dates->to . ' 23:59:59'); + } + }); + + // OS Version filter + CRUD::filter('os_version') + ->type('text') + ->label('OS Version') + ->whenActive(function ($value) { + CRUD::addClause('where', 'os_version', 'LIKE', "%{$value}%"); + }); } /** @@ -123,10 +167,10 @@ protected function setupListOperation() */ protected function setupCreateOperation() { - CRUD::setValidation([ - ]); + CRUD::setValidation([]); - $this->crud->addFields([ + $this->crud->addFields( + [ [ 'label' => 'User Name', 'type' => 'select', diff --git a/CloudVeilManager/app/Http/Controllers/Admin/UserCrudController.php b/CloudVeilManager/app/Http/Controllers/Admin/UserCrudController.php index 104c4cd..4aaaa47 100644 --- a/CloudVeilManager/app/Http/Controllers/Admin/UserCrudController.php +++ b/CloudVeilManager/app/Http/Controllers/Admin/UserCrudController.php @@ -61,8 +61,13 @@ protected function setupListOperation() ], [ 'label' => 'License Used', - 'type' => 'datetime', - 'name' => 'updated_at' + 'type' => 'text', + 'name' => 'license_used', + 'value' => function ($entry) { + $used = $entry->activations_used ?? 0; + $allowed = $entry->activations_allowed ?? 0; + return $used . ' of ' . $allowed; + } ], [ 'label' => 'Active', @@ -106,12 +111,13 @@ protected function setupFields($fromEdit = true) } CRUD::setValidation($validationRules); - $this->crud->addFields([ + $this->crud->addFields( + [ [ 'type' => 'custom_html', 'name' => 'activations', 'value' => ' - Show Activations + Show Activations ', 'tab' => 'Information', ], @@ -119,37 +125,30 @@ protected function setupFields($fromEdit = true) 'label' => 'User Full Name', 'type' => 'text', 'name' => 'name', - 'tab' => 'Information' + 'tab' => 'Information', + 'wrapper' => ['class' => 'form-group col-md-5'], ], [ 'label' => 'User E-Mail', 'type' => 'text', 'name' => 'email', - 'tab' => 'Information' + 'tab' => 'Information', + 'wrapper' => ['class' => 'form-group col-md-5'], ], [ 'label' => 'Enabled', 'type' => 'switch', 'name' => 'is_enabled', - 'tab' => 'Information' - ], - [ - 'label' => 'Customer ID', - 'type' => 'number', - 'name' => 'customer_id', - 'tab' => 'Information' - ], - [ - 'label' => 'Password', - 'type' => 'password', - 'name' => 'password', 'tab' => 'Information', - 'wrapper' => ['class' => 'form-group col-md-6'], + 'wrapper' => ['class' => 'form-group col-md-2 d-flex pt-3'], ], [ - 'label' => 'Password Confirm', - 'type' => 'password', - 'name' => 'password_verify', + 'label' => 'Group', + 'type' => 'select2', + 'entity' => 'group', + 'model' => 'App\Models\Group', + 'name' => 'group', + 'attribute' => 'name', 'tab' => 'Information', 'wrapper' => ['class' => 'form-group col-md-6'], ], @@ -157,16 +156,8 @@ protected function setupFields($fromEdit = true) 'label' => 'Activations Allowed', 'type' => 'number', 'name' => 'activations_allowed', - 'tab' => 'Information' - ], - [ - 'label' => 'Group', - 'type' => 'select2', - 'entity' => 'group', - 'model' => 'App\Models\Group', - 'name' => 'group', - 'attribute' => 'name', - 'tab' => 'Information' + 'tab' => 'Information', + 'wrapper' => ['class' => 'form-group col-md-6'], ], [ 'label' => 'Roles', @@ -175,21 +166,29 @@ protected function setupFields($fromEdit = true) 'model' => 'App\Models\Role', 'attribute' => 'display_name', 'name' => 'roles', - 'tab' => 'Information' + 'tab' => 'Information', + 'wrapper' => ['class' => 'form-group col-md-6'], ], [ - 'label' => 'Relaxed Policy Passcode', + 'label' => 'Password', 'type' => 'password', + 'name' => 'password', + 'tab' => 'Information', + 'wrapper' => ['class' => 'form-group col-md-6'], + ], + [ + 'label' => 'Relaxed Policy Passcode', + 'type' => 'password_revealable', 'name' => 'relaxed_policy_passcode', 'tab' => 'Information', - 'wrapper' => ['class' => 'form-group col-md-8'], + 'wrapper' => ['class' => 'form-group col-md-6'], ], [ 'label' => 'Enable Relaxed Policy Passcode', 'type' => 'switch', 'name' => 'enable_relaxed_policy_passcode', 'tab' => 'Information', - 'wrapper' => ['class' => 'form-group col-md-2 d-flex pt-3'], + 'wrapper' => ['class' => 'form-group col-md-6 d-flex pt-3'], ], [ 'name' => 'BypassesPermitted', @@ -334,7 +333,8 @@ protected function setupUpdateOperation() $this->setupFields(true); } - protected function setupCreateOperation() { + protected function setupCreateOperation() + { $this->setupFields(false); } diff --git a/CloudVeilManager/config/backpack/ui.php b/CloudVeilManager/config/backpack/ui.php index ccd73d2..0d35c87 100644 --- a/CloudVeilManager/config/backpack/ui.php +++ b/CloudVeilManager/config/backpack/ui.php @@ -115,6 +115,8 @@ // JS files that are loaded in all pages, using Laravel's asset() helper 'scripts' => [ + // Bootstrap 5 JavaScript - required for dropdowns and collapses + 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js', // 'js/example.js', // 'https://cdn.jsdelivr.net/npm/vue@2.4.4/dist/vue.min.js', // 'https://cdn.jsdelivr.net/npm/react@16/umd/react.production.min.js', diff --git a/CloudVeilManager/resources/js/bootstrap.js b/CloudVeilManager/resources/js/bootstrap.js index 4d73ffb..f22804c 100644 --- a/CloudVeilManager/resources/js/bootstrap.js +++ b/CloudVeilManager/resources/js/bootstrap.js @@ -1,4 +1,6 @@ -import 'bootstrap'; +// Bootstrap is loaded via CDN in config/backpack/ui.php +// This ensures window.bootstrap is available globally for Backpack's JavaScript +// import 'bootstrap'; /** * We'll load the axios HTTP library which allows us to easily issue requests diff --git a/CloudVeilManager/resources/views/vendor/backpack/crud/fields/password_revealable.blade.php b/CloudVeilManager/resources/views/vendor/backpack/crud/fields/password_revealable.blade.php new file mode 100644 index 0000000..444e786 --- /dev/null +++ b/CloudVeilManager/resources/views/vendor/backpack/crud/fields/password_revealable.blade.php @@ -0,0 +1,73 @@ +{{-- password_revealable --}} + +@php + $field['value'] = old_empty_or_null($field['name'], '') ?? ($field['value'] ?? ($field['default'] ?? '')); + // autocomplete off, if not otherwise specified + if (!isset($field['attributes']['autocomplete'])) { + $field['attributes']['autocomplete'] = "off"; + } +@endphp + +@include('crud::fields.inc.wrapper_start') + + @include('crud::fields.inc.translatable_icon') + +
+ + + + + + + + + + +
+ + {{-- HINT --}} + @if (isset($field['hint'])) +

{!! $field['hint'] !!}

+ @endif +@include('crud::fields.inc.wrapper_end') + +{{-- FIELD JS - will be loaded in the after_scripts section --}} +@push('crud_fields_scripts') + +@endpush +