From b1d62cc4784ed69ddadb87cc8ecdc407dba3be24 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Wed, 16 Oct 2024 17:23:22 -0500 Subject: [PATCH 001/105] initial untested --- _ide_helper_actions.php | 44 +++ .../CreateCheckoutRequest.php | 75 ++++ app/Http/Controllers/ViewAssetsController.php | 59 +-- composer.json | 4 +- composer.lock | 346 +++++++++++++++++- 5 files changed, 470 insertions(+), 58 deletions(-) create mode 100644 _ide_helper_actions.php create mode 100644 app/Actions/CheckoutRequests/CreateCheckoutRequest.php diff --git a/_ide_helper_actions.php b/_ide_helper_actions.php new file mode 100644 index 0000000000..a420493954 --- /dev/null +++ b/_ide_helper_actions.php @@ -0,0 +1,44 @@ +user(); + + // Check if the asset exists and is requestable + if (is_null($asset = Asset::RequestableAssets()->find($assetId))) { + return redirect()->route('requestable-assets') + ->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')); + } + if (!Company::isCurrentUserHasAccess($asset)) { + return redirect()->route('requestable-assets') + ->with('error', trans('general.insufficient_permissions')); + } + + $data['item'] = $asset; + $data['target'] = auth()->user(); + $data['item_quantity'] = 1; + $settings = Setting::getSettings(); + + $logaction = new Actionlog(); + $logaction->item_id = $data['asset_id'] = $asset->id; + $logaction->item_type = $data['item_type'] = Asset::class; + $logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s'); + + if ($user->location_id) { + $logaction->location_id = $user->location_id; + } + $logaction->target_id = $data['user_id'] = auth()->id(); + $logaction->target_type = User::class; + + // If it's already requested, cancel the request. + if ($asset->isRequestedBy(auth()->user())) { + $asset->cancelRequest(); + $asset->decrement('requests_counter', 1); + + $logaction->logaction('request canceled'); + try { + $settings->notify(new RequestAssetCancelation($data)); + } catch (\Exception $e) { + Log::warning($e); + } + return redirect()->route('requestable-assets') + ->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); + } + + $logaction->logaction('requested'); + $asset->request(); + $asset->increment('requests_counter', 1); + try { + $settings->notify(new RequestAssetNotification($data)); + } catch (\Exception $e) { + Log::warning($e); + } + + return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); + } +} diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 12c300e5bd..d9c1f0facf 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -2,10 +2,10 @@ namespace App\Http\Controllers; +use App\Actions\CheckoutRequests\CreateCheckoutRequest; use App\Models\Actionlog; use App\Models\Asset; use App\Models\AssetModel; -use App\Models\Company; use App\Models\Setting; use App\Models\User; use App\Notifications\RequestAssetCancelation; @@ -13,7 +13,6 @@ use App\Notifications\RequestAssetNotification; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use \Illuminate\Contracts\View\View; -use Log; /** * This controller handles all actions related to the ability for users @@ -144,61 +143,9 @@ class ViewAssetsController extends Controller * Process a specific requested asset * @param null $assetId */ - public function getRequestAsset($assetId = null) : RedirectResponse + public function getRequestAsset($assetId = null): void { - $user = auth()->user(); - - // Check if the asset exists and is requestable - if (is_null($asset = Asset::RequestableAssets()->find($assetId))) { - return redirect()->route('requestable-assets') - ->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')); - } - if (! Company::isCurrentUserHasAccess($asset)) { - return redirect()->route('requestable-assets') - ->with('error', trans('general.insufficient_permissions')); - } - - $data['item'] = $asset; - $data['target'] = auth()->user(); - $data['item_quantity'] = 1; - $settings = Setting::getSettings(); - - $logaction = new Actionlog(); - $logaction->item_id = $data['asset_id'] = $asset->id; - $logaction->item_type = $data['item_type'] = Asset::class; - $logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s'); - - if ($user->location_id) { - $logaction->location_id = $user->location_id; - } - $logaction->target_id = $data['user_id'] = auth()->id(); - $logaction->target_type = User::class; - - // If it's already requested, cancel the request. - if ($asset->isRequestedBy(auth()->user())) { - $asset->cancelRequest(); - $asset->decrement('requests_counter', 1); - - $logaction->logaction('request canceled'); - try { - $settings->notify(new RequestAssetCancelation($data)); - } catch (\Exception $e) { - Log::warning($e); - } - return redirect()->route('requestable-assets') - ->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); - } - - $logaction->logaction('requested'); - $asset->request(); - $asset->increment('requests_counter', 1); - try { - $settings->notify(new RequestAssetNotification($data)); - } catch (\Exception $e) { - Log::warning($e); - } - - return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); + CreateCheckoutRequest::run($assetId); } public function getRequestedAssets() : View diff --git a/composer.json b/composer.json index 6d89312578..c96b11e1bc 100644 --- a/composer.json +++ b/composer.json @@ -50,6 +50,7 @@ "league/csv": "^9.7", "league/flysystem-aws-s3-v3": "^3.0", "livewire/livewire": "^3.5", + "lorisleiva/laravel-actions": "^2.8", "neitanod/forceutf8": "^2.0", "nesbot/carbon": "^2.32", "nunomaduro/collision": "^7.0", @@ -84,7 +85,8 @@ "squizlabs/php_codesniffer": "^3.5", "symfony/css-selector": "^4.4", "symfony/dom-crawler": "^4.4", - "vimeo/psalm": "^5.13" + "vimeo/psalm": "^5.13", + "wulfheart/laravel-actions-ide-helper": "^0.8.0" }, "extra": { "laravel": { diff --git a/composer.lock b/composer.lock index 3f79921b26..09504209f0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3819ab4ef72eb77fabe494c0e746b83b", + "content-hash": "9bbdc0f3c60b21a77208b4c65c2f5bdb", "packages": [ { "name": "alek13/slack", @@ -4535,6 +4535,154 @@ ], "time": "2024-10-01T12:40:06+00:00" }, + { + "name": "lorisleiva/laravel-actions", + "version": "v2.8.4", + "source": { + "type": "git", + "url": "https://github.com/lorisleiva/laravel-actions.git", + "reference": "5a168bfdd3b75dd6ff259019d4aeef784bbd5403" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lorisleiva/laravel-actions/zipball/5a168bfdd3b75dd6ff259019d4aeef784bbd5403", + "reference": "5a168bfdd3b75dd6ff259019d4aeef784bbd5403", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^10.0|^11.0", + "lorisleiva/lody": "^0.5", + "php": "^8.1" + }, + "require-dev": { + "orchestra/testbench": "^8.0|^9.0", + "pestphp/pest": "^1.23|^2.34", + "phpunit/phpunit": "^9.6|^10.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Lorisleiva\\Actions\\ActionServiceProvider" + ], + "aliases": { + "Action": "Lorisleiva\\Actions\\Facades\\Actions" + } + } + }, + "autoload": { + "psr-4": { + "Lorisleiva\\Actions\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Loris Leiva", + "email": "loris.leiva@gmail.com", + "homepage": "https://lorisleiva.com", + "role": "Developer" + } + ], + "description": "Laravel components that take care of one specific task", + "homepage": "https://github.com/lorisleiva/laravel-actions", + "keywords": [ + "action", + "command", + "component", + "controller", + "job", + "laravel", + "listener", + "object" + ], + "support": { + "issues": "https://github.com/lorisleiva/laravel-actions/issues", + "source": "https://github.com/lorisleiva/laravel-actions/tree/v2.8.4" + }, + "funding": [ + { + "url": "https://github.com/sponsors/lorisleiva", + "type": "github" + } + ], + "time": "2024-09-10T09:57:29+00:00" + }, + { + "name": "lorisleiva/lody", + "version": "v0.5.0", + "source": { + "type": "git", + "url": "https://github.com/lorisleiva/lody.git", + "reference": "c2f51b070e99f3a240d66cf68ef1f232036917fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lorisleiva/lody/zipball/c2f51b070e99f3a240d66cf68ef1f232036917fe", + "reference": "c2f51b070e99f3a240d66cf68ef1f232036917fe", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.0|^10.0|^11.0", + "php": "^8.0" + }, + "require-dev": { + "orchestra/testbench": "^9.0", + "pestphp/pest": "^1.20|^2.34", + "phpunit/phpunit": "^9.5.10|^10.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Lorisleiva\\Lody\\LodyServiceProvider" + ], + "aliases": { + "Lody": "Lorisleiva\\Lody\\Lody" + } + } + }, + "autoload": { + "psr-4": { + "Lorisleiva\\Lody\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Loris Leiva", + "email": "loris.leiva@gmail.com", + "homepage": "https://lorisleiva.com", + "role": "Developer" + } + ], + "description": "Load files and classes as lazy collections in Laravel.", + "homepage": "https://github.com/lorisleiva/lody", + "keywords": [ + "classes", + "collection", + "files", + "laravel", + "load" + ], + "support": { + "issues": "https://github.com/lorisleiva/lody/issues", + "source": "https://github.com/lorisleiva/lody/tree/v0.5.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/lorisleiva", + "type": "github" + } + ], + "time": "2024-03-13T12:08:59+00:00" + }, { "name": "masterminds/html5", "version": "2.9.0", @@ -13775,6 +13923,73 @@ }, "time": "2024-03-27T12:14:49+00:00" }, + { + "name": "phpdocumentor/reflection", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/Reflection.git", + "reference": "61e2f1fe7683e9647b9ed8d9e53d08699385267d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/61e2f1fe7683e9647b9ed8d9e53d08699385267d", + "reference": "61e2f1fe7683e9647b9ed8d9e53d08699385267d", + "shasum": "" + }, + "require": { + "nikic/php-parser": "~4.18 || ^5.0", + "php": "8.1.*|8.2.*|8.3.*", + "phpdocumentor/reflection-common": "^2.1", + "phpdocumentor/reflection-docblock": "^5", + "phpdocumentor/type-resolver": "^1.2", + "symfony/polyfill-php80": "^1.28", + "webmozart/assert": "^1.7" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "doctrine/coding-standard": "^12.0", + "mikey179/vfsstream": "~1.2", + "mockery/mockery": "~1.6.0", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^10.0", + "psalm/phar": "^5.24", + "rector/rector": "^1.0.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-5.x": "5.3.x-dev", + "dev-6.x": "6.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\": "src/phpDocumentor" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Reflection library to do Static Analysis for PHP Projects", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/Reflection/issues", + "source": "https://github.com/phpDocumentor/Reflection/tree/6.0.0" + }, + "time": "2024-05-23T19:28:12+00:00" + }, { "name": "phpmyadmin/sql-parser", "version": "5.10.0", @@ -14872,6 +15087,60 @@ ], "time": "2024-06-11T12:45:25+00:00" }, + { + "name": "riimu/kit-pathjoin", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/Riimu/Kit-PathJoin.git", + "reference": "8ad2656c79527dba9f7f20e1229dcd38abfe8cee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Riimu/Kit-PathJoin/zipball/8ad2656c79527dba9f7f20e1229dcd38abfe8cee", + "reference": "8ad2656c79527dba9f7f20e1229dcd38abfe8cee", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.3", + "phpunit/phpunit": "^5.7 || ^6.2", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Riimu\\Kit\\PathJoin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Riikka Kalliomäki", + "email": "riikka.kalliomaki@gmail.com", + "homepage": "http://riimu.net" + } + ], + "description": "Cross-platform library for normalizing and joining file system paths", + "homepage": "http://kit.riimu.net", + "keywords": [ + "file", + "join", + "normalize", + "path", + "system" + ], + "support": { + "issues": "https://github.com/Riimu/Kit-PathJoin/issues", + "source": "https://github.com/Riimu/Kit-PathJoin/tree/master" + }, + "time": "2017-07-09T14:41:04+00:00" + }, { "name": "sebastian/cli-parser", "version": "2.0.1", @@ -16640,6 +16909,81 @@ "source": "https://github.com/vimeo/psalm" }, "time": "2024-09-08T18:53:08+00:00" + }, + { + "name": "wulfheart/laravel-actions-ide-helper", + "version": "v0.8.0", + "source": { + "type": "git", + "url": "https://github.com/Wulfheart/laravel-actions-ide-helper.git", + "reference": "48e9d7c89804fcd459a73cc22adee533e7a88deb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Wulfheart/laravel-actions-ide-helper/zipball/48e9d7c89804fcd459a73cc22adee533e7a88deb", + "reference": "48e9d7c89804fcd459a73cc22adee533e7a88deb", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^10.0|^11.0", + "lorisleiva/laravel-actions": "^2.3", + "lorisleiva/lody": "^0.5.0", + "php": "^8.1|^8.2", + "phpdocumentor/reflection": "^5.1|^6.0", + "riimu/kit-pathjoin": "^1.2", + "spatie/laravel-package-tools": "^1.14" + }, + "require-dev": { + "brianium/paratest": "^6.8|^7.4", + "nunomaduro/collision": "^6.1|^8.0", + "orchestra/testbench": "^8.0|^9.0", + "pestphp/pest": "^1.22|^2.34", + "phpunit/phpunit": "^9.5.10|^10.5", + "spatie/invade": "^1.1|^2.0", + "spatie/laravel-ray": "^1.32", + "spatie/pest-plugin-snapshots": "^1.1|^2.1", + "vimeo/psalm": "^5.6" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Wulfheart\\LaravelActionsIdeHelper\\LaravelActionsIdeHelperServiceProvider" + ], + "aliases": { + "LaravelActionsIdeHelper": "Wulfheart\\LaravelActionsIdeHelper\\LaravelActionsIdeHelperFacade" + } + } + }, + "autoload": { + "psr-4": { + "Wulfheart\\LaravelActionsIdeHelper\\": "src", + "Wulfheart\\LaravelActionsIdeHelper\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexander Wulf", + "email": "dev@alexfwulf.de", + "role": "Developer" + } + ], + "description": "Generate a new IDE Helper file for Laravel Actions.", + "homepage": "https://github.com/wulfheart/laravel-actions-ide-helper", + "keywords": [ + "laravel", + "laravel-actions-ide-helper", + "wulfheart" + ], + "support": { + "issues": "https://github.com/Wulfheart/laravel-actions-ide-helper/issues", + "source": "https://github.com/Wulfheart/laravel-actions-ide-helper/tree/v0.8.0" + }, + "time": "2024-08-30T14:32:22+00:00" } ], "aliases": [], From a524c0b418de29f2393aa49a9dc82f7de00ce2e7 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Wed, 16 Oct 2024 18:48:15 -0500 Subject: [PATCH 002/105] more work --- _ide_helper_actions.php | 44 ------------------- .../CreateCheckoutRequest.php | 41 +++++++++++++---- app/Http/Controllers/ViewAssetsController.php | 6 ++- 3 files changed, 37 insertions(+), 54 deletions(-) delete mode 100644 _ide_helper_actions.php diff --git a/_ide_helper_actions.php b/_ide_helper_actions.php deleted file mode 100644 index a420493954..0000000000 --- a/_ide_helper_actions.php +++ /dev/null @@ -1,44 +0,0 @@ -user(); // Check if the asset exists and is requestable if (is_null($asset = Asset::RequestableAssets()->find($assetId))) { - return redirect()->route('requestable-assets') - ->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')); + $this->status = 'doesNotExist'; + return false; } if (!Company::isCurrentUserHasAccess($asset)) { - return redirect()->route('requestable-assets') - ->with('error', trans('general.insufficient_permissions')); + $this->status = 'accessDenied'; + return false; } $data['item'] = $asset; @@ -57,8 +61,8 @@ class CreateCheckoutRequest } catch (\Exception $e) { Log::warning($e); } - return redirect()->route('requestable-assets') - ->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); + $this->status = 'cancelled'; + return true; } $logaction->logaction('requested'); @@ -67,9 +71,28 @@ class CreateCheckoutRequest try { $settings->notify(new RequestAssetNotification($data)); } catch (\Exception $e) { - Log::warning($e); + \Log::warning($e); } - return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); + return true; + } + + public function jsonResponse(): JsonResponse + { + return match ($this->status) { + 'doesNotExist' => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist_or_not_requestable'))), + 'accessDenied' => response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))), + default => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.request_successfully_created'))), + }; + } + + public function htmlResponse(): RedirectResponse + { + return match ($this->status) { + 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), + 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), + 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), + default => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), + }; } } diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index d9c1f0facf..722974713f 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -145,7 +145,11 @@ class ViewAssetsController extends Controller */ public function getRequestAsset($assetId = null): void { - CreateCheckoutRequest::run($assetId); + $request = CreateCheckoutRequest::run($assetId); + + if (!$request) { + \Log::debug('problem'); + } } public function getRequestedAssets() : View From b2ff34260a0d33bc4daea43554cb5d992a1cfc38 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Thu, 17 Oct 2024 00:02:15 -0500 Subject: [PATCH 003/105] hm, lots of thinking to do --- .../CreateCheckoutRequest.php | 19 ++++++++++++++++--- app/Http/Controllers/ViewAssetsController.php | 6 +----- routes/web.php | 1 + 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php index a8c6d41368..5cfcecc785 100644 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php @@ -13,15 +13,19 @@ use App\Notifications\RequestAssetNotification; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Lorisleiva\Actions\Concerns\AsAction; +use Lorisleiva\Actions\Concerns\AsController; +use Lorisleiva\Actions\Concerns\WithAttributes; class CreateCheckoutRequest { use AsAction; + use WithAttributes; - public string $status; + public string $status = ''; public function handle($assetId) { + dump($assetId); $user = auth()->user(); // Check if the asset exists and is requestable @@ -59,7 +63,7 @@ class CreateCheckoutRequest try { $settings->notify(new RequestAssetCancelation($data)); } catch (\Exception $e) { - Log::warning($e); + \Log::warning($e); } $this->status = 'cancelled'; return true; @@ -73,12 +77,19 @@ class CreateCheckoutRequest } catch (\Exception $e) { \Log::warning($e); } + dump('handle end'); + } - return true; + public function asController($assetId) + { + dump('asController'); + $asset = $this->handle($assetId); + //return $asset; } public function jsonResponse(): JsonResponse { + dump('json'); return match ($this->status) { 'doesNotExist' => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist_or_not_requestable'))), 'accessDenied' => response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))), @@ -88,7 +99,9 @@ class CreateCheckoutRequest public function htmlResponse(): RedirectResponse { + dump('redirects'); return match ($this->status) { + dump('redirects'), 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 722974713f..d9c1f0facf 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -145,11 +145,7 @@ class ViewAssetsController extends Controller */ public function getRequestAsset($assetId = null): void { - $request = CreateCheckoutRequest::run($assetId); - - if (!$request) { - \Log::debug('problem'); - } + CreateCheckoutRequest::run($assetId); } public function getRequestedAssets() : View diff --git a/routes/web.php b/routes/web.php index 60b74476c7..76df83e116 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,6 @@ Date: Thu, 17 Oct 2024 12:45:49 -0500 Subject: [PATCH 004/105] this works --- .../CreateCheckoutRequest.php | 51 +++++++++---------- app/Http/Controllers/Api/CheckoutRequest.php | 23 +++++++++ app/Http/Controllers/ViewAssetsController.php | 12 ++++- 3 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 app/Http/Controllers/Api/CheckoutRequest.php diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php index 5cfcecc785..e0caafa278 100644 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php @@ -30,12 +30,10 @@ class CreateCheckoutRequest // Check if the asset exists and is requestable if (is_null($asset = Asset::RequestableAssets()->find($assetId))) { - $this->status = 'doesNotExist'; - return false; + return $this->status = 'doesNotExist'; } if (!Company::isCurrentUserHasAccess($asset)) { - $this->status = 'accessDenied'; - return false; + return $this->status = 'accessDenied'; } $data['item'] = $asset; @@ -65,8 +63,7 @@ class CreateCheckoutRequest } catch (\Exception $e) { \Log::warning($e); } - $this->status = 'cancelled'; - return true; + return $this->status = 'cancelled'; } $logaction->logaction('requested'); @@ -78,6 +75,8 @@ class CreateCheckoutRequest \Log::warning($e); } dump('handle end'); + + return $this->status = 'success'; } public function asController($assetId) @@ -87,25 +86,25 @@ class CreateCheckoutRequest //return $asset; } - public function jsonResponse(): JsonResponse - { - dump('json'); - return match ($this->status) { - 'doesNotExist' => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist_or_not_requestable'))), - 'accessDenied' => response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))), - default => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.request_successfully_created'))), - }; - } + //public function jsonResponse(): JsonResponse + //{ + // dump('json'); + // return match ($this->status) { + // 'doesNotExist' => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist_or_not_requestable'))), + // 'accessDenied' => response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))), + // default => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.request_successfully_created'))), + // }; + //} - public function htmlResponse(): RedirectResponse - { - dump('redirects'); - return match ($this->status) { - dump('redirects'), - 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), - 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), - 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), - default => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), - }; - } + //public function htmlResponse(): RedirectResponse + //{ + // dump('redirects'); + // return match ($this->status) { + // dump('redirects'), + // 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), + // 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), + // 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), + // default => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), + // }; + //} } diff --git a/app/Http/Controllers/Api/CheckoutRequest.php b/app/Http/Controllers/Api/CheckoutRequest.php new file mode 100644 index 0000000000..bbf7cd3587 --- /dev/null +++ b/app/Http/Controllers/Api/CheckoutRequest.php @@ -0,0 +1,23 @@ + response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist_or_not_requestable'))), + 'accessDenied' => response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))), + default => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.request_successfully_created'))), + }; + } +} diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index d9c1f0facf..6772a57620 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -143,9 +143,17 @@ class ViewAssetsController extends Controller * Process a specific requested asset * @param null $assetId */ - public function getRequestAsset($assetId = null): void + public function getRequestAsset($assetId = null): RedirectResponse { - CreateCheckoutRequest::run($assetId); + $status = CreateCheckoutRequest::run($assetId); + + return match ($status) { + 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), + 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), + 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), + 'success' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), + default => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), + }; } public function getRequestedAssets() : View From 95a32864cf7c10866d65207156b3c919e5727a2f Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Thu, 17 Oct 2024 18:11:18 -0500 Subject: [PATCH 005/105] =?UTF-8?q?hm,=20lots=20to=20think=20about=20?= =?UTF-8?q?=F0=9F=A4=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Actions/BaseAction.php | 19 +++++ .../CreateCheckoutRequestNew.php | 75 +++++++++++++++++++ app/Http/Controllers/ViewAssetsController.php | 6 +- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 app/Actions/BaseAction.php create mode 100644 app/Actions/CheckoutRequests/CreateCheckoutRequestNew.php diff --git a/app/Actions/BaseAction.php b/app/Actions/BaseAction.php new file mode 100644 index 0000000000..bb48b336d0 --- /dev/null +++ b/app/Actions/BaseAction.php @@ -0,0 +1,19 @@ +$method(...$args); + } + + //abstract static function run($parameters = null) + //{ + // return call_user_func($this->run(...$parameters)); + //} + +} diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequestNew.php b/app/Actions/CheckoutRequests/CreateCheckoutRequestNew.php new file mode 100644 index 0000000000..6b5a7565c7 --- /dev/null +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequestNew.php @@ -0,0 +1,75 @@ +user(); + + // Check if the asset exists and is requestable + if (is_null($asset = Asset::RequestableAssets()->find($assetId))) { + return $status = 'doesNotExist'; + } + if (!Company::isCurrentUserHasAccess($asset)) { + return $status = 'accessDenied'; + } + + $data['item'] = $asset; + $data['target'] = auth()->user(); + $data['item_quantity'] = 1; + $settings = Setting::getSettings(); + + $logaction = new Actionlog(); + $logaction->item_id = $data['asset_id'] = $asset->id; + $logaction->item_type = $data['item_type'] = Asset::class; + $logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s'); + + if ($user->location_id) { + $logaction->location_id = $user->location_id; + } + $logaction->target_id = $data['user_id'] = auth()->id(); + $logaction->target_type = User::class; + + // If it's already requested, cancel the request. + if ($asset->isRequestedBy(auth()->user())) { + $asset->cancelRequest(); + $asset->decrement('requests_counter', 1); + + $logaction->logaction('request canceled'); + try { + $settings->notify(new RequestAssetCancelation($data)); + } catch (\Exception $e) { + \Log::warning($e); + } + return $status = 'cancelled'; + } + + $logaction->logaction('requested'); + $asset->request(); + $asset->increment('requests_counter', 1); + try { + $settings->notify(new RequestAssetNotification($data)); + } catch (\Exception $e) { + \Log::warning($e); + } + dump('handle end'); + + return $status = 'success'; + } + + +} \ No newline at end of file diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 6772a57620..67c3ee1c8e 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Actions\CheckoutRequests\CreateCheckoutRequest; +use App\Actions\CheckoutRequests\CreateCheckoutRequestNew; use App\Models\Actionlog; use App\Models\Asset; use App\Models\AssetModel; @@ -143,9 +144,10 @@ class ViewAssetsController extends Controller * Process a specific requested asset * @param null $assetId */ - public function getRequestAsset($assetId = null): RedirectResponse + public function getRequestAsset(CreateCheckoutRequestNew $checkoutRequestNew, $assetId = null): RedirectResponse { - $status = CreateCheckoutRequest::run($assetId); + $status = CreateCheckoutRequestNew::run($assetId); + //$status = $checkoutRequestNew->run($assetId); return match ($status) { 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), From e40849c910bb89aa4a7fa2137ee651f610248bb1 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Mon, 21 Oct 2024 17:31:26 -0500 Subject: [PATCH 006/105] this stuff works --- app/Actions/BaseAction.php | 19 - .../CreateCheckoutRequest.php | 67 +--- .../CreateCheckoutRequestNew.php | 75 ---- app/Exceptions/AssetNotRequestable.php | 27 ++ app/Exceptions/UserDoestExistException.php | 10 + app/Http/Controllers/Api/CheckoutRequest.php | 9 +- app/Http/Controllers/ViewAssetsController.php | 43 ++- app/Models/Asset.php | 2 +- composer.json | 4 +- composer.lock | 346 +----------------- routes/web.php | 2 +- 11 files changed, 90 insertions(+), 514 deletions(-) delete mode 100644 app/Actions/BaseAction.php delete mode 100644 app/Actions/CheckoutRequests/CreateCheckoutRequestNew.php create mode 100644 app/Exceptions/AssetNotRequestable.php create mode 100644 app/Exceptions/UserDoestExistException.php diff --git a/app/Actions/BaseAction.php b/app/Actions/BaseAction.php deleted file mode 100644 index bb48b336d0..0000000000 --- a/app/Actions/BaseAction.php +++ /dev/null @@ -1,19 +0,0 @@ -$method(...$args); - } - - //abstract static function run($parameters = null) - //{ - // return call_user_func($this->run(...$parameters)); - //} - -} diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php index e0caafa278..f0a36d46ab 100644 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php @@ -2,7 +2,7 @@ namespace App\Actions\CheckoutRequests; -use App\Helpers\Helper; +use App\Exceptions\AssetNotRequestable; use App\Models\Actionlog; use App\Models\Asset; use App\Models\Company; @@ -10,34 +10,27 @@ use App\Models\Setting; use App\Models\User; use App\Notifications\RequestAssetCancelation; use App\Notifications\RequestAssetNotification; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\RedirectResponse; -use Lorisleiva\Actions\Concerns\AsAction; -use Lorisleiva\Actions\Concerns\AsController; -use Lorisleiva\Actions\Concerns\WithAttributes; +use Illuminate\Auth\Access\AuthorizationException; +use Illuminate\Database\Eloquent\ModelNotFoundException; class CreateCheckoutRequest { - use AsAction; - use WithAttributes; - - public string $status = ''; - - public function handle($assetId) + /** + * @throws AssetNotRequestable + * @throws AuthorizationException + */ + public static function run(Asset $asset, User $user): string { - dump($assetId); - $user = auth()->user(); - - // Check if the asset exists and is requestable - if (is_null($asset = Asset::RequestableAssets()->find($assetId))) { - return $this->status = 'doesNotExist'; + // Check if asset is requestable + if (is_null(Asset::RequestableAssets()->find($asset->id))) { + throw new AssetNotRequestable($asset); } if (!Company::isCurrentUserHasAccess($asset)) { - return $this->status = 'accessDenied'; + throw new AuthorizationException(); } $data['item'] = $asset; - $data['target'] = auth()->user(); + $data['target'] = $user; $data['item_quantity'] = 1; $settings = Setting::getSettings(); @@ -63,7 +56,7 @@ class CreateCheckoutRequest } catch (\Exception $e) { \Log::warning($e); } - return $this->status = 'cancelled'; + return $status = 'cancelled'; } $logaction->logaction('requested'); @@ -74,37 +67,9 @@ class CreateCheckoutRequest } catch (\Exception $e) { \Log::warning($e); } - dump('handle end'); - return $this->status = 'success'; + return $asset; } - public function asController($assetId) - { - dump('asController'); - $asset = $this->handle($assetId); - //return $asset; - } - //public function jsonResponse(): JsonResponse - //{ - // dump('json'); - // return match ($this->status) { - // 'doesNotExist' => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist_or_not_requestable'))), - // 'accessDenied' => response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))), - // default => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.request_successfully_created'))), - // }; - //} - - //public function htmlResponse(): RedirectResponse - //{ - // dump('redirects'); - // return match ($this->status) { - // dump('redirects'), - // 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), - // 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), - // 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), - // default => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), - // }; - //} -} +} \ No newline at end of file diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequestNew.php b/app/Actions/CheckoutRequests/CreateCheckoutRequestNew.php deleted file mode 100644 index 6b5a7565c7..0000000000 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequestNew.php +++ /dev/null @@ -1,75 +0,0 @@ -user(); - - // Check if the asset exists and is requestable - if (is_null($asset = Asset::RequestableAssets()->find($assetId))) { - return $status = 'doesNotExist'; - } - if (!Company::isCurrentUserHasAccess($asset)) { - return $status = 'accessDenied'; - } - - $data['item'] = $asset; - $data['target'] = auth()->user(); - $data['item_quantity'] = 1; - $settings = Setting::getSettings(); - - $logaction = new Actionlog(); - $logaction->item_id = $data['asset_id'] = $asset->id; - $logaction->item_type = $data['item_type'] = Asset::class; - $logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s'); - - if ($user->location_id) { - $logaction->location_id = $user->location_id; - } - $logaction->target_id = $data['user_id'] = auth()->id(); - $logaction->target_type = User::class; - - // If it's already requested, cancel the request. - if ($asset->isRequestedBy(auth()->user())) { - $asset->cancelRequest(); - $asset->decrement('requests_counter', 1); - - $logaction->logaction('request canceled'); - try { - $settings->notify(new RequestAssetCancelation($data)); - } catch (\Exception $e) { - \Log::warning($e); - } - return $status = 'cancelled'; - } - - $logaction->logaction('requested'); - $asset->request(); - $asset->increment('requests_counter', 1); - try { - $settings->notify(new RequestAssetNotification($data)); - } catch (\Exception $e) { - \Log::warning($e); - } - dump('handle end'); - - return $status = 'success'; - } - - -} \ No newline at end of file diff --git a/app/Exceptions/AssetNotRequestable.php b/app/Exceptions/AssetNotRequestable.php new file mode 100644 index 0000000000..8c3daa7cf8 --- /dev/null +++ b/app/Exceptions/AssetNotRequestable.php @@ -0,0 +1,27 @@ +errorMessage = $errorMessage; + + parent::__construct($errorMessage); + } + + public function __toString() + { + return is_null($this->errorMessage) ? 'This asset is not requestable.' : $this->errorMessage; + } + + public function getTranslatedMessage() + { + return trans($this->errorMessage); + } +} diff --git a/app/Exceptions/UserDoestExistException.php b/app/Exceptions/UserDoestExistException.php new file mode 100644 index 0000000000..e14069dca4 --- /dev/null +++ b/app/Exceptions/UserDoestExistException.php @@ -0,0 +1,10 @@ + response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist_or_not_requestable'))), - 'accessDenied' => response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))), - default => response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.request_successfully_created'))), - }; + CreateCheckoutRequest::run($assetId); } } diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 67c3ee1c8e..8c8b1e993f 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers; use App\Actions\CheckoutRequests\CreateCheckoutRequest; -use App\Actions\CheckoutRequests\CreateCheckoutRequestNew; +use App\Exceptions\AssetNotRequestable; use App\Models\Actionlog; use App\Models\Asset; use App\Models\AssetModel; @@ -11,9 +11,11 @@ use App\Models\Setting; use App\Models\User; use App\Notifications\RequestAssetCancelation; use App\Notifications\RequestAssetNotification; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use \Illuminate\Contracts\View\View; +use Illuminate\Support\Facades\Log; /** * This controller handles all actions related to the ability for users @@ -144,20 +146,39 @@ class ViewAssetsController extends Controller * Process a specific requested asset * @param null $assetId */ - public function getRequestAsset(CreateCheckoutRequestNew $checkoutRequestNew, $assetId = null): RedirectResponse + public function getRequestAsset(Asset $asset): RedirectResponse { - $status = CreateCheckoutRequestNew::run($assetId); - //$status = $checkoutRequestNew->run($assetId); + try { + CreateCheckoutRequest::run($asset, auth()->user()); + return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); + } catch (AssetNotRequestable $e) { + return redirect()->back()->with('error', 'poop'); + } catch (AuthorizationException $e) { + return redirect()->back()->with('error', trans('admin/hardware/message.requests.error')); + } catch (\Exception $e) { + report($e); + return redirect()->back()->with('error', 'generic error message'); + } - return match ($status) { - 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), - 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), - 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), - 'success' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), - default => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), - }; + + //$status = CreateCheckoutRequest::run($asset, auth()->user()); + // + //return match ($status) { + // 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), + // 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), + // 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), + // default => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), + //}; } + //public function destroy(Asset $asset): RedirectResponse + //{ + // try { + // CancelCheckoutRequest($asset, auth()->user()); + // } + //} + + public function getRequestedAssets() : View { return view('account/requested'); diff --git a/app/Models/Asset.php b/app/Models/Asset.php index ce8b870eb2..5bf49186f0 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -1433,7 +1433,7 @@ class Asset extends Depreciable * @return \Illuminate\Database\Query\Builder Modified query builder */ - public function scopeRequestableAssets($query) + public function scopeRequestableAssets($query): Builder { $table = $query->getModel()->getTable(); diff --git a/composer.json b/composer.json index c96b11e1bc..6d89312578 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,6 @@ "league/csv": "^9.7", "league/flysystem-aws-s3-v3": "^3.0", "livewire/livewire": "^3.5", - "lorisleiva/laravel-actions": "^2.8", "neitanod/forceutf8": "^2.0", "nesbot/carbon": "^2.32", "nunomaduro/collision": "^7.0", @@ -85,8 +84,7 @@ "squizlabs/php_codesniffer": "^3.5", "symfony/css-selector": "^4.4", "symfony/dom-crawler": "^4.4", - "vimeo/psalm": "^5.13", - "wulfheart/laravel-actions-ide-helper": "^0.8.0" + "vimeo/psalm": "^5.13" }, "extra": { "laravel": { diff --git a/composer.lock b/composer.lock index 09504209f0..3f79921b26 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9bbdc0f3c60b21a77208b4c65c2f5bdb", + "content-hash": "3819ab4ef72eb77fabe494c0e746b83b", "packages": [ { "name": "alek13/slack", @@ -4535,154 +4535,6 @@ ], "time": "2024-10-01T12:40:06+00:00" }, - { - "name": "lorisleiva/laravel-actions", - "version": "v2.8.4", - "source": { - "type": "git", - "url": "https://github.com/lorisleiva/laravel-actions.git", - "reference": "5a168bfdd3b75dd6ff259019d4aeef784bbd5403" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/lorisleiva/laravel-actions/zipball/5a168bfdd3b75dd6ff259019d4aeef784bbd5403", - "reference": "5a168bfdd3b75dd6ff259019d4aeef784bbd5403", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^10.0|^11.0", - "lorisleiva/lody": "^0.5", - "php": "^8.1" - }, - "require-dev": { - "orchestra/testbench": "^8.0|^9.0", - "pestphp/pest": "^1.23|^2.34", - "phpunit/phpunit": "^9.6|^10.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Lorisleiva\\Actions\\ActionServiceProvider" - ], - "aliases": { - "Action": "Lorisleiva\\Actions\\Facades\\Actions" - } - } - }, - "autoload": { - "psr-4": { - "Lorisleiva\\Actions\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Loris Leiva", - "email": "loris.leiva@gmail.com", - "homepage": "https://lorisleiva.com", - "role": "Developer" - } - ], - "description": "Laravel components that take care of one specific task", - "homepage": "https://github.com/lorisleiva/laravel-actions", - "keywords": [ - "action", - "command", - "component", - "controller", - "job", - "laravel", - "listener", - "object" - ], - "support": { - "issues": "https://github.com/lorisleiva/laravel-actions/issues", - "source": "https://github.com/lorisleiva/laravel-actions/tree/v2.8.4" - }, - "funding": [ - { - "url": "https://github.com/sponsors/lorisleiva", - "type": "github" - } - ], - "time": "2024-09-10T09:57:29+00:00" - }, - { - "name": "lorisleiva/lody", - "version": "v0.5.0", - "source": { - "type": "git", - "url": "https://github.com/lorisleiva/lody.git", - "reference": "c2f51b070e99f3a240d66cf68ef1f232036917fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/lorisleiva/lody/zipball/c2f51b070e99f3a240d66cf68ef1f232036917fe", - "reference": "c2f51b070e99f3a240d66cf68ef1f232036917fe", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^9.0|^10.0|^11.0", - "php": "^8.0" - }, - "require-dev": { - "orchestra/testbench": "^9.0", - "pestphp/pest": "^1.20|^2.34", - "phpunit/phpunit": "^9.5.10|^10.5" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Lorisleiva\\Lody\\LodyServiceProvider" - ], - "aliases": { - "Lody": "Lorisleiva\\Lody\\Lody" - } - } - }, - "autoload": { - "psr-4": { - "Lorisleiva\\Lody\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Loris Leiva", - "email": "loris.leiva@gmail.com", - "homepage": "https://lorisleiva.com", - "role": "Developer" - } - ], - "description": "Load files and classes as lazy collections in Laravel.", - "homepage": "https://github.com/lorisleiva/lody", - "keywords": [ - "classes", - "collection", - "files", - "laravel", - "load" - ], - "support": { - "issues": "https://github.com/lorisleiva/lody/issues", - "source": "https://github.com/lorisleiva/lody/tree/v0.5.0" - }, - "funding": [ - { - "url": "https://github.com/sponsors/lorisleiva", - "type": "github" - } - ], - "time": "2024-03-13T12:08:59+00:00" - }, { "name": "masterminds/html5", "version": "2.9.0", @@ -13923,73 +13775,6 @@ }, "time": "2024-03-27T12:14:49+00:00" }, - { - "name": "phpdocumentor/reflection", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/Reflection.git", - "reference": "61e2f1fe7683e9647b9ed8d9e53d08699385267d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/61e2f1fe7683e9647b9ed8d9e53d08699385267d", - "reference": "61e2f1fe7683e9647b9ed8d9e53d08699385267d", - "shasum": "" - }, - "require": { - "nikic/php-parser": "~4.18 || ^5.0", - "php": "8.1.*|8.2.*|8.3.*", - "phpdocumentor/reflection-common": "^2.1", - "phpdocumentor/reflection-docblock": "^5", - "phpdocumentor/type-resolver": "^1.2", - "symfony/polyfill-php80": "^1.28", - "webmozart/assert": "^1.7" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "doctrine/coding-standard": "^12.0", - "mikey179/vfsstream": "~1.2", - "mockery/mockery": "~1.6.0", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^10.0", - "psalm/phar": "^5.24", - "rector/rector": "^1.0.0", - "squizlabs/php_codesniffer": "^3.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-5.x": "5.3.x-dev", - "dev-6.x": "6.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\": "src/phpDocumentor" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Reflection library to do Static Analysis for PHP Projects", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/Reflection/issues", - "source": "https://github.com/phpDocumentor/Reflection/tree/6.0.0" - }, - "time": "2024-05-23T19:28:12+00:00" - }, { "name": "phpmyadmin/sql-parser", "version": "5.10.0", @@ -15087,60 +14872,6 @@ ], "time": "2024-06-11T12:45:25+00:00" }, - { - "name": "riimu/kit-pathjoin", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/Riimu/Kit-PathJoin.git", - "reference": "8ad2656c79527dba9f7f20e1229dcd38abfe8cee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Riimu/Kit-PathJoin/zipball/8ad2656c79527dba9f7f20e1229dcd38abfe8cee", - "reference": "8ad2656c79527dba9f7f20e1229dcd38abfe8cee", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.3", - "phpunit/phpunit": "^5.7 || ^6.2", - "squizlabs/php_codesniffer": "^3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Riimu\\Kit\\PathJoin\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Riikka Kalliomäki", - "email": "riikka.kalliomaki@gmail.com", - "homepage": "http://riimu.net" - } - ], - "description": "Cross-platform library for normalizing and joining file system paths", - "homepage": "http://kit.riimu.net", - "keywords": [ - "file", - "join", - "normalize", - "path", - "system" - ], - "support": { - "issues": "https://github.com/Riimu/Kit-PathJoin/issues", - "source": "https://github.com/Riimu/Kit-PathJoin/tree/master" - }, - "time": "2017-07-09T14:41:04+00:00" - }, { "name": "sebastian/cli-parser", "version": "2.0.1", @@ -16909,81 +16640,6 @@ "source": "https://github.com/vimeo/psalm" }, "time": "2024-09-08T18:53:08+00:00" - }, - { - "name": "wulfheart/laravel-actions-ide-helper", - "version": "v0.8.0", - "source": { - "type": "git", - "url": "https://github.com/Wulfheart/laravel-actions-ide-helper.git", - "reference": "48e9d7c89804fcd459a73cc22adee533e7a88deb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Wulfheart/laravel-actions-ide-helper/zipball/48e9d7c89804fcd459a73cc22adee533e7a88deb", - "reference": "48e9d7c89804fcd459a73cc22adee533e7a88deb", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^10.0|^11.0", - "lorisleiva/laravel-actions": "^2.3", - "lorisleiva/lody": "^0.5.0", - "php": "^8.1|^8.2", - "phpdocumentor/reflection": "^5.1|^6.0", - "riimu/kit-pathjoin": "^1.2", - "spatie/laravel-package-tools": "^1.14" - }, - "require-dev": { - "brianium/paratest": "^6.8|^7.4", - "nunomaduro/collision": "^6.1|^8.0", - "orchestra/testbench": "^8.0|^9.0", - "pestphp/pest": "^1.22|^2.34", - "phpunit/phpunit": "^9.5.10|^10.5", - "spatie/invade": "^1.1|^2.0", - "spatie/laravel-ray": "^1.32", - "spatie/pest-plugin-snapshots": "^1.1|^2.1", - "vimeo/psalm": "^5.6" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Wulfheart\\LaravelActionsIdeHelper\\LaravelActionsIdeHelperServiceProvider" - ], - "aliases": { - "LaravelActionsIdeHelper": "Wulfheart\\LaravelActionsIdeHelper\\LaravelActionsIdeHelperFacade" - } - } - }, - "autoload": { - "psr-4": { - "Wulfheart\\LaravelActionsIdeHelper\\": "src", - "Wulfheart\\LaravelActionsIdeHelper\\Database\\Factories\\": "database/factories" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alexander Wulf", - "email": "dev@alexfwulf.de", - "role": "Developer" - } - ], - "description": "Generate a new IDE Helper file for Laravel Actions.", - "homepage": "https://github.com/wulfheart/laravel-actions-ide-helper", - "keywords": [ - "laravel", - "laravel-actions-ide-helper", - "wulfheart" - ], - "support": { - "issues": "https://github.com/Wulfheart/laravel-actions-ide-helper/issues", - "source": "https://github.com/Wulfheart/laravel-actions-ide-helper/tree/v0.8.0" - }, - "time": "2024-08-30T14:32:22+00:00" } ], "aliases": [], diff --git a/routes/web.php b/routes/web.php index 76df83e116..137ed83b45 100644 --- a/routes/web.php +++ b/routes/web.php @@ -304,7 +304,7 @@ Route::group(['prefix' => 'account', 'middleware' => ['auth']], function () { [ViewAssetsController::class, 'getRequestableIndex'] )->name('requestable-assets'); Route::post( - 'request-asset/{assetId}', + 'request-asset/{asset}', [ViewAssetsController::class, 'getRequestAsset'] )->name('account/request-asset'); From b59bf495e148af73eef047b7de84f3b2ba3e9db2 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Tue, 22 Oct 2024 15:09:35 -0500 Subject: [PATCH 007/105] stuff --- .../CheckoutRequests/CreateCheckoutRequest.php | 12 +++++++++--- app/Http/Controllers/Api/CheckoutRequest.php | 17 +++++++++++++++-- app/Http/Controllers/ViewAssetsController.php | 16 +++------------- routes/api.php | 2 ++ routes/web.php | 2 +- .../Feature/Checkouts/Api/AssetCheckoutTest.php | 9 +++++++++ 6 files changed, 39 insertions(+), 19 deletions(-) diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php index f0a36d46ab..c9fd4c9387 100644 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php @@ -3,6 +3,7 @@ namespace App\Actions\CheckoutRequests; use App\Exceptions\AssetNotRequestable; +use App\Exceptions\ThereIsNoUser; use App\Models\Actionlog; use App\Models\Asset; use App\Models\Company; @@ -11,7 +12,6 @@ use App\Models\User; use App\Notifications\RequestAssetCancelation; use App\Notifications\RequestAssetNotification; use Illuminate\Auth\Access\AuthorizationException; -use Illuminate\Database\Eloquent\ModelNotFoundException; class CreateCheckoutRequest { @@ -21,7 +21,7 @@ class CreateCheckoutRequest */ public static function run(Asset $asset, User $user): string { - // Check if asset is requestable + //throw new \Exception(); if (is_null(Asset::RequestableAssets()->find($asset->id))) { throw new AssetNotRequestable($asset); } @@ -46,6 +46,7 @@ class CreateCheckoutRequest $logaction->target_type = User::class; // If it's already requested, cancel the request. + // this is going into another action class if ($asset->isRequestedBy(auth()->user())) { $asset->cancelRequest(); $asset->decrement('requests_counter', 1); @@ -68,7 +69,12 @@ class CreateCheckoutRequest \Log::warning($e); } - return $asset; + return true; // or $asset, or whatever + } + + public function doSomethingElse() + { + } diff --git a/app/Http/Controllers/Api/CheckoutRequest.php b/app/Http/Controllers/Api/CheckoutRequest.php index f7a6023cd3..b1c976cf24 100644 --- a/app/Http/Controllers/Api/CheckoutRequest.php +++ b/app/Http/Controllers/Api/CheckoutRequest.php @@ -3,14 +3,27 @@ namespace App\Http\Controllers\Api; use App\Actions\CheckoutRequests\CreateCheckoutRequest; +use App\Exceptions\AssetNotRequestable; use App\Helpers\Helper; use App\Http\Controllers\Controller; +use App\Models\Asset; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\JsonResponse; class CheckoutRequest extends Controller { - public function store($assetId): JsonResponse + public function store(CheckoutRequestRequest $request, Asset $asset): JsonResponse { - CreateCheckoutRequest::run($assetId); + try { + CreateCheckoutRequest::run($asset, $request->validated()['user_id']); + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.success'))); + } catch (AssetNotRequestable $e) { + return response()->json(Helper::formatStandardApiResponse('error', 'Asset is not requestable')); + } catch (AuthorizationException $e) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))); + } catch (\Exception $e) { + report($e); + return response()->json(Helper::formatStandardApiResponse('error', null, 'Something terrible has gone wrong and we\'re not sure if we can help - may god have mercy on your soul. Contact your admin :)')); + } } } diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 8c8b1e993f..605bbb8b99 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -146,29 +146,19 @@ class ViewAssetsController extends Controller * Process a specific requested asset * @param null $assetId */ - public function getRequestAsset(Asset $asset): RedirectResponse + public function store(Asset $asset): RedirectResponse { try { CreateCheckoutRequest::run($asset, auth()->user()); return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); } catch (AssetNotRequestable $e) { - return redirect()->back()->with('error', 'poop'); + return redirect()->back()->with('error', 'Asset is not requestable'); } catch (AuthorizationException $e) { return redirect()->back()->with('error', trans('admin/hardware/message.requests.error')); } catch (\Exception $e) { report($e); - return redirect()->back()->with('error', 'generic error message'); + return redirect()->back()->with('error', 'Something terrible has gone wrong and we\'re not sure if we can help - may god have mercy on your soul. Contact your admin :)'); } - - - //$status = CreateCheckoutRequest::run($asset, auth()->user()); - // - //return match ($status) { - // 'doesNotExist' => redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable')), - // 'accessDenied' => redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')), - // 'cancelled' => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')), - // default => redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')), - //}; } //public function destroy(Asset $asset): RedirectResponse diff --git a/routes/api.php b/routes/api.php index 0581a04682..129e4d6ea1 100644 --- a/routes/api.php +++ b/routes/api.php @@ -40,6 +40,8 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->name('api.assets.requested'); + Route::post('request/{asset}', [Api\CheckoutRequest::class, 'store'])->name('api.assets.requests.store'); + Route::get('requestable/hardware', [ Api\AssetsController::class, diff --git a/routes/web.php b/routes/web.php index 137ed83b45..f719231c51 100644 --- a/routes/web.php +++ b/routes/web.php @@ -305,7 +305,7 @@ Route::group(['prefix' => 'account', 'middleware' => ['auth']], function () { )->name('requestable-assets'); Route::post( 'request-asset/{asset}', - [ViewAssetsController::class, 'getRequestAsset'] + [ViewAssetsController::class, 'store'] )->name('account/request-asset'); Route::post( diff --git a/tests/Feature/Checkouts/Api/AssetCheckoutTest.php b/tests/Feature/Checkouts/Api/AssetCheckoutTest.php index ded3889642..0763478a98 100644 --- a/tests/Feature/Checkouts/Api/AssetCheckoutTest.php +++ b/tests/Feature/Checkouts/Api/AssetCheckoutTest.php @@ -21,6 +21,15 @@ class AssetCheckoutTest extends TestCase Event::fake([CheckoutableCheckedOut::class]); } + public function testCheckoutRequest() + { + $asset = Asset::factory()->create(); + + $this->actingAsForApi(User::factory()->create()) + ->post(route('api.assets.requests.store', $asset->id)) + ->assertOk(); + } + public function testCheckingOutAssetRequiresCorrectPermission() { $this->actingAsForApi(User::factory()->create()) From 8a99cc1391ef7c98a9bdf22060cd5709e67350cc Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Tue, 22 Oct 2024 17:25:58 -0500 Subject: [PATCH 008/105] test working --- app/Http/Controllers/Api/CheckoutRequest.php | 4 ++-- database/factories/AssetFactory.php | 16 ++++++++++++++-- .../Feature/Checkouts/Api/AssetCheckoutTest.php | 15 ++++++++++++--- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Api/CheckoutRequest.php b/app/Http/Controllers/Api/CheckoutRequest.php index b1c976cf24..cf179fb813 100644 --- a/app/Http/Controllers/Api/CheckoutRequest.php +++ b/app/Http/Controllers/Api/CheckoutRequest.php @@ -12,10 +12,10 @@ use Illuminate\Http\JsonResponse; class CheckoutRequest extends Controller { - public function store(CheckoutRequestRequest $request, Asset $asset): JsonResponse + public function store(Asset $asset): JsonResponse { try { - CreateCheckoutRequest::run($asset, $request->validated()['user_id']); + CreateCheckoutRequest::run($asset, auth()->user()); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.success'))); } catch (AssetNotRequestable $e) { return response()->json(Helper::formatStandardApiResponse('error', 'Asset is not requestable')); diff --git a/database/factories/AssetFactory.php b/database/factories/AssetFactory.php index 4d6d20651c..1923b15150 100644 --- a/database/factories/AssetFactory.php +++ b/database/factories/AssetFactory.php @@ -347,12 +347,24 @@ class AssetFactory extends Factory public function requestable() { - return $this->state(['requestable' => true]); + $id = Statuslabel::factory()->create([ + 'archived' => false, + 'deployable' => true, + 'pending' => true, + ])->id; + //return $this->state(['requestable' => true]); + return $this->state(['status_id' => $id]); } public function nonrequestable() { - return $this->state(['requestable' => false]); + $id = Statuslabel::factory()->create([ + 'archived' => true, + 'deployable' => false, + 'pending' => false, + ])->id; + //return $this->state(['requestable' => false]); + return $this->state(['status_id' => $id]); } public function noPurchaseOrEolDate() diff --git a/tests/Feature/Checkouts/Api/AssetCheckoutTest.php b/tests/Feature/Checkouts/Api/AssetCheckoutTest.php index 0763478a98..efbe745231 100644 --- a/tests/Feature/Checkouts/Api/AssetCheckoutTest.php +++ b/tests/Feature/Checkouts/Api/AssetCheckoutTest.php @@ -2,6 +2,8 @@ namespace Tests\Feature\Checkouts\Api; +use Illuminate\Support\Facades\Mail; +use Notification; use PHPUnit\Framework\Attributes\DataProvider; use App\Events\CheckoutableCheckedOut; use App\Models\Asset; @@ -23,11 +25,18 @@ class AssetCheckoutTest extends TestCase public function testCheckoutRequest() { - $asset = Asset::factory()->create(); + Notification::fake(); + $requestable = Asset::factory()->requestable()->create(); + $nonRequestable = Asset::factory()->nonrequestable()->create(); $this->actingAsForApi(User::factory()->create()) - ->post(route('api.assets.requests.store', $asset->id)) - ->assertOk(); + ->post(route('api.assets.requests.store', $requestable->id)) + ->assertStatusMessageIs('success'); + + $this->actingAsForApi(User::factory()->create()) + ->post(route('api.assets.requests.store', $nonRequestable->id)) + ->assertStatusMessageIs('error'); + } public function testCheckingOutAssetRequiresCorrectPermission() From 48c812d34564c08b5e177545714efd25ddb433c3 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Tue, 22 Oct 2024 20:48:27 -0500 Subject: [PATCH 009/105] bunch of cleanup, split cancel out --- .../CancelCheckoutRequest.php | 43 +++++++++++++++++++ .../CreateCheckoutRequest.php | 36 ++++------------ app/Http/Controllers/ViewAssetsController.php | 2 +- database/factories/AssetFactory.php | 6 +-- routes/web.php | 20 +++------ 5 files changed, 62 insertions(+), 45 deletions(-) create mode 100644 app/Actions/CheckoutRequests/CancelCheckoutRequest.php diff --git a/app/Actions/CheckoutRequests/CancelCheckoutRequest.php b/app/Actions/CheckoutRequests/CancelCheckoutRequest.php new file mode 100644 index 0000000000..235728be14 --- /dev/null +++ b/app/Actions/CheckoutRequests/CancelCheckoutRequest.php @@ -0,0 +1,43 @@ +cancelRequest(); + + $asset->decrement('requests_counter', 1); + + $data['item'] = $asset; + $data['target'] = $user; + $data['item_quantity'] = 1; + $settings = Setting::getSettings(); + + $logaction = Actionlog::create([ + 'item_id' => $data['asset_id'] = $asset->id, + 'item_type' => $data['item_type'] = Asset::class, + 'created_at' => $data['requested_date'] = date('Y-m-d H:i:s'), + 'target_id' => $data['user_id'] = auth()->id(), + 'target_type' => User::class, + 'location_id' => $user->location_id ?? null, + ]); + $logaction->logaction('request canceled'); + + try { + $settings->notify(new RequestAssetCancelation($data)); + } catch (\Exception $e) { + \Log::warning($e); + } + + return true; + } + +} \ No newline at end of file diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php index c9fd4c9387..825c1ddeb4 100644 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php @@ -21,7 +21,6 @@ class CreateCheckoutRequest */ public static function run(Asset $asset, User $user): string { - //throw new \Exception(); if (is_null(Asset::RequestableAssets()->find($asset->id))) { throw new AssetNotRequestable($asset); } @@ -34,33 +33,16 @@ class CreateCheckoutRequest $data['item_quantity'] = 1; $settings = Setting::getSettings(); - $logaction = new Actionlog(); - $logaction->item_id = $data['asset_id'] = $asset->id; - $logaction->item_type = $data['item_type'] = Asset::class; - $logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s'); - - if ($user->location_id) { - $logaction->location_id = $user->location_id; - } - $logaction->target_id = $data['user_id'] = auth()->id(); - $logaction->target_type = User::class; - - // If it's already requested, cancel the request. - // this is going into another action class - if ($asset->isRequestedBy(auth()->user())) { - $asset->cancelRequest(); - $asset->decrement('requests_counter', 1); - - $logaction->logaction('request canceled'); - try { - $settings->notify(new RequestAssetCancelation($data)); - } catch (\Exception $e) { - \Log::warning($e); - } - return $status = 'cancelled'; - } - + $logaction = Actionlog::create([ + 'target_id' => $data['asset_id'] = $asset->id, + 'item_type' => $data['item_type'] = Asset::class, + 'created_at' => $data['requested_date'] = date('Y-m-d H:i:s'), + 'user_id' => $data['user_id'] = auth()->id(), + 'target_type' => User::class, + 'location_id' => $user->location_id ?? null, + ]); $logaction->logaction('requested'); + $asset->request(); $asset->increment('requests_counter', 1); try { diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 605bbb8b99..1f4a54b4dd 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -83,7 +83,7 @@ class ViewAssetsController extends Controller return view('account/requestable-assets', compact('assets', 'models')); } - public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null) : RedirectResponse + public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null): RedirectResponse { $item = null; $fullItemType = 'App\\Models\\'.studly_case($itemType); diff --git a/database/factories/AssetFactory.php b/database/factories/AssetFactory.php index 1923b15150..00105205c1 100644 --- a/database/factories/AssetFactory.php +++ b/database/factories/AssetFactory.php @@ -352,8 +352,7 @@ class AssetFactory extends Factory 'deployable' => true, 'pending' => true, ])->id; - //return $this->state(['requestable' => true]); - return $this->state(['status_id' => $id]); + return $this->state(['status_id' => $id, 'requestable' => true]); } public function nonrequestable() @@ -363,8 +362,7 @@ class AssetFactory extends Factory 'deployable' => false, 'pending' => false, ])->id; - //return $this->state(['requestable' => false]); - return $this->state(['status_id' => $id]); + return $this->state(['status_id' => $id, 'requestable' => false]); } public function noPurchaseOrEolDate() diff --git a/routes/web.php b/routes/web.php index f719231c51..51451e5b8a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -294,24 +294,18 @@ Route::group(['prefix' => 'account', 'middleware' => ['auth']], function () { Route::get('api', [ProfileController::class, 'api'])->name('user.api'); // View Assets - Route::get('view-assets', [ViewAssetsController::class, 'getIndex'])->name('view-assets'); + Route::get('view-assets', [ViewAssetsController::class, 'index'])->name('view-assets'); Route::get('requested', [ViewAssetsController::class, 'getRequestedAssets'])->name('account.requested'); // Profile - Route::get( - 'requestable-assets', - [ViewAssetsController::class, 'getRequestableIndex'] - )->name('requestable-assets'); - Route::post( - 'request-asset/{asset}', - [ViewAssetsController::class, 'store'] - )->name('account/request-asset'); + Route::get('requestable-assets', [ViewAssetsController::class, 'getRequestableIndex'])->name('requestable-assets'); - Route::post( - 'request/{itemType}/{itemId}/{cancel_by_admin?}/{requestingUser?}', - [ViewAssetsController::class, 'getRequestItem'] - )->name('account/request-item'); + Route::post('request-asset/{asset}', [ViewAssetsController::class, 'store']) + ->name('account/request-asset'); + + Route::post('request/{itemType}/{itemId}/{cancel_by_admin?}/{requestingUser?}', [ViewAssetsController::class, 'getRequestItem']) + ->name('account/request-item'); // Account Dashboard Route::get('/', [ViewAssetsController::class, 'getIndex'])->name('account'); From 3ee008e871049b9405fc051a26ac987d4fff3734 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Tue, 22 Oct 2024 20:54:20 -0500 Subject: [PATCH 010/105] revert --- .../CheckoutRequests/CancelCheckoutRequest.php | 15 +++++++-------- .../CheckoutRequests/CreateCheckoutRequest.php | 15 +++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/app/Actions/CheckoutRequests/CancelCheckoutRequest.php b/app/Actions/CheckoutRequests/CancelCheckoutRequest.php index 235728be14..ccad6b2e64 100644 --- a/app/Actions/CheckoutRequests/CancelCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CancelCheckoutRequest.php @@ -21,14 +21,13 @@ class CancelCheckoutRequest $data['item_quantity'] = 1; $settings = Setting::getSettings(); - $logaction = Actionlog::create([ - 'item_id' => $data['asset_id'] = $asset->id, - 'item_type' => $data['item_type'] = Asset::class, - 'created_at' => $data['requested_date'] = date('Y-m-d H:i:s'), - 'target_id' => $data['user_id'] = auth()->id(), - 'target_type' => User::class, - 'location_id' => $user->location_id ?? null, - ]); + $logaction = new Actionlog(); + $logaction->item_id = $data['asset_id'] = $asset->id; + $logaction->item_type = $data['item_type'] = Asset::class; + $logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s'); + $logaction->target_id = $data['user_id'] = auth()->id(); + $logaction->target_type = User::class; + $logaction->location_id = $user->location_id ?? null; $logaction->logaction('request canceled'); try { diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php index 825c1ddeb4..c90ca4f23c 100644 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php @@ -33,14 +33,13 @@ class CreateCheckoutRequest $data['item_quantity'] = 1; $settings = Setting::getSettings(); - $logaction = Actionlog::create([ - 'target_id' => $data['asset_id'] = $asset->id, - 'item_type' => $data['item_type'] = Asset::class, - 'created_at' => $data['requested_date'] = date('Y-m-d H:i:s'), - 'user_id' => $data['user_id'] = auth()->id(), - 'target_type' => User::class, - 'location_id' => $user->location_id ?? null, - ]); + $logaction = new Actionlog(); + $logaction->item_id = $data['asset_id'] = $asset->id; + $logaction->item_type = $data['item_type'] = Asset::class; + $logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s'); + $logaction->target_id = $data['user_id'] = auth()->id(); + $logaction->target_type = User::class; + $logaction->location_id = $user->location_id ?? null; $logaction->logaction('requested'); $asset->request(); From 79e6eafafa7d0295827f8b88b96d3903396664ba Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Tue, 22 Oct 2024 22:33:29 -0500 Subject: [PATCH 011/105] cancelled scaffolded out --- app/Http/Controllers/Api/CheckoutRequest.php | 7 +++++++ app/Http/Controllers/ViewAssetsController.php | 12 ++++++------ routes/api.php | 1 + routes/web.php | 5 ++++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Api/CheckoutRequest.php b/app/Http/Controllers/Api/CheckoutRequest.php index cf179fb813..3b31451376 100644 --- a/app/Http/Controllers/Api/CheckoutRequest.php +++ b/app/Http/Controllers/Api/CheckoutRequest.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Api; +use App\Actions\CheckoutRequests\CancelCheckoutRequest; use App\Actions\CheckoutRequests\CreateCheckoutRequest; use App\Exceptions\AssetNotRequestable; use App\Helpers\Helper; @@ -26,4 +27,10 @@ class CheckoutRequest extends Controller return response()->json(Helper::formatStandardApiResponse('error', null, 'Something terrible has gone wrong and we\'re not sure if we can help - may god have mercy on your soul. Contact your admin :)')); } } + + public function destroy(Asset $asset): JsonResponse + { + CancelCheckoutRequest::run($asset, auth()->user()); + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.canceled'))); + } } diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 1f4a54b4dd..3f723559f0 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Actions\CheckoutRequests\CancelCheckoutRequest; use App\Actions\CheckoutRequests\CreateCheckoutRequest; use App\Exceptions\AssetNotRequestable; use App\Models\Actionlog; @@ -161,12 +162,11 @@ class ViewAssetsController extends Controller } } - //public function destroy(Asset $asset): RedirectResponse - //{ - // try { - // CancelCheckoutRequest($asset, auth()->user()); - // } - //} + public function destroy(Asset $asset): RedirectResponse + { + CancelCheckoutRequest::run($asset, auth()->user()); + return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); + } public function getRequestedAssets() : View diff --git a/routes/api.php b/routes/api.php index 129e4d6ea1..64c55e51da 100644 --- a/routes/api.php +++ b/routes/api.php @@ -41,6 +41,7 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi )->name('api.assets.requested'); Route::post('request/{asset}', [Api\CheckoutRequest::class, 'store'])->name('api.assets.requests.store'); + Route::post('request/{asset}/cancel', [Api\CheckoutRequest::class, 'destroy'])->name('api.assets.requests.destroy'); Route::get('requestable/hardware', [ diff --git a/routes/web.php b/routes/web.php index 51451e5b8a..728c9c51e0 100644 --- a/routes/web.php +++ b/routes/web.php @@ -302,7 +302,10 @@ Route::group(['prefix' => 'account', 'middleware' => ['auth']], function () { Route::get('requestable-assets', [ViewAssetsController::class, 'getRequestableIndex'])->name('requestable-assets'); Route::post('request-asset/{asset}', [ViewAssetsController::class, 'store']) - ->name('account/request-asset'); + ->name('account.request-asset'); + + Route::post('request-asset/{asset}/cancel', [ViewAssetsController::class, 'destroy']) + ->name('account.request-asset.cancel'); Route::post('request/{itemType}/{itemId}/{cancel_by_admin?}/{requestingUser?}', [ViewAssetsController::class, 'getRequestItem']) ->name('account/request-item'); From b0d7eb216868a8f9e73cf64ceeeb270957aaec07 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Tue, 22 Oct 2024 22:44:28 -0500 Subject: [PATCH 012/105] cancel action finished --- app/Http/Controllers/ViewAssetsController.php | 2 +- resources/views/partials/bootstrap-table.blade.php | 2 +- routes/web.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 3f723559f0..83a2168311 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -165,7 +165,7 @@ class ViewAssetsController extends Controller public function destroy(Asset $asset): RedirectResponse { CancelCheckoutRequest::run($asset, auth()->user()); - return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); + return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); } diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index f24552d753..5a1b8f5c22 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -480,7 +480,7 @@ if (value.assigned_to_self == true){ return ''; } else if (value.available_actions.cancel == true) { - return '
@csrf
'; + return '
@csrf
'; } else if (value.available_actions.request == true) { return '
@csrf
'; } diff --git a/routes/web.php b/routes/web.php index 728c9c51e0..6e32ac0135 100644 --- a/routes/web.php +++ b/routes/web.php @@ -294,7 +294,7 @@ Route::group(['prefix' => 'account', 'middleware' => ['auth']], function () { Route::get('api', [ProfileController::class, 'api'])->name('user.api'); // View Assets - Route::get('view-assets', [ViewAssetsController::class, 'index'])->name('view-assets'); + Route::get('view-assets', [ViewAssetsController::class, 'getIndex'])->name('view-assets'); Route::get('requested', [ViewAssetsController::class, 'getRequestedAssets'])->name('account.requested'); From fdb6970f36456c3b85ad3e7dcdeb824a8c935280 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Wed, 23 Oct 2024 01:41:27 -0500 Subject: [PATCH 013/105] this is pretty much done --- .../CheckoutRequests/CreateCheckoutRequest.php | 12 +++--------- app/Http/Controllers/Api/CheckoutRequest.php | 9 +++++++-- app/Http/Controllers/ViewAssetsController.php | 9 +++++++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php index c90ca4f23c..9d1ba332ff 100644 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequest.php @@ -12,6 +12,7 @@ use App\Models\User; use App\Notifications\RequestAssetCancelation; use App\Notifications\RequestAssetNotification; use Illuminate\Auth\Access\AuthorizationException; +use Log; class CreateCheckoutRequest { @@ -47,16 +48,9 @@ class CreateCheckoutRequest try { $settings->notify(new RequestAssetNotification($data)); } catch (\Exception $e) { - \Log::warning($e); + Log::warning($e); } - return true; // or $asset, or whatever + return true; } - - public function doSomethingElse() - { - - } - - } \ No newline at end of file diff --git a/app/Http/Controllers/Api/CheckoutRequest.php b/app/Http/Controllers/Api/CheckoutRequest.php index 3b31451376..1ee9c4c025 100644 --- a/app/Http/Controllers/Api/CheckoutRequest.php +++ b/app/Http/Controllers/Api/CheckoutRequest.php @@ -30,7 +30,12 @@ class CheckoutRequest extends Controller public function destroy(Asset $asset): JsonResponse { - CancelCheckoutRequest::run($asset, auth()->user()); - return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.canceled'))); + try { + CancelCheckoutRequest::run($asset, auth()->user()); + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.canceled'))); + } catch (\Exception $e) { + report($e); + return response()->json(Helper::formatStandardApiResponse('error', null, $e->getMessage())); + } } } diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 83a2168311..56d1f8b390 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -164,8 +164,13 @@ class ViewAssetsController extends Controller public function destroy(Asset $asset): RedirectResponse { - CancelCheckoutRequest::run($asset, auth()->user()); - return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); + try { + CancelCheckoutRequest::run($asset, auth()->user()); + return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); + } catch (\Exception $e) { + report($e); + return redirect()->back()->with('error', 'something bad happened'); + } } From c76cccbb68dc55300127204653e269f8f460e606 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Wed, 23 Oct 2024 12:56:36 -0500 Subject: [PATCH 014/105] rm extraneous methods in exception --- app/Exceptions/AssetNotRequestable.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/Exceptions/AssetNotRequestable.php b/app/Exceptions/AssetNotRequestable.php index 8c3daa7cf8..4067dda385 100644 --- a/app/Exceptions/AssetNotRequestable.php +++ b/app/Exceptions/AssetNotRequestable.php @@ -6,22 +6,4 @@ use Exception; class AssetNotRequestable extends Exception { - private $errorMessage = 'Asset not requestable'; - - public function __construct($errorMessage = null) - { - $this->errorMessage = $errorMessage; - - parent::__construct($errorMessage); - } - - public function __toString() - { - return is_null($this->errorMessage) ? 'This asset is not requestable.' : $this->errorMessage; - } - - public function getTranslatedMessage() - { - return trans($this->errorMessage); - } } From 513c78a09f36817d8454304d03b7750928499d09 Mon Sep 17 00:00:00 2001 From: spencerrlongg Date: Tue, 3 Dec 2024 17:51:12 -0600 Subject: [PATCH 015/105] Refactor CheckoutRequest actions for consistency Renamed CheckoutRequest action classes to include "Action" in their names for consistency and clarity. Enhanced error handling in controllers to standardize error responses with translations. Updated usage of the renamed action classes throughout the code to ensure proper integration. --- ...st.php => CancelCheckoutRequestAction.php} | 8 +++++++- ...st.php => CreateCheckoutRequestAction.php} | 4 +--- app/Http/Controllers/Api/CheckoutRequest.php | 19 +++++++++++-------- app/Http/Controllers/ViewAssetsController.php | 18 +++++++++--------- routes/web.php | 1 - 5 files changed, 28 insertions(+), 22 deletions(-) rename app/Actions/CheckoutRequests/{CancelCheckoutRequest.php => CancelCheckoutRequestAction.php} (83%) rename app/Actions/CheckoutRequests/{CreateCheckoutRequest.php => CreateCheckoutRequestAction.php} (93%) diff --git a/app/Actions/CheckoutRequests/CancelCheckoutRequest.php b/app/Actions/CheckoutRequests/CancelCheckoutRequestAction.php similarity index 83% rename from app/Actions/CheckoutRequests/CancelCheckoutRequest.php rename to app/Actions/CheckoutRequests/CancelCheckoutRequestAction.php index ccad6b2e64..2d6dd68a08 100644 --- a/app/Actions/CheckoutRequests/CancelCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CancelCheckoutRequestAction.php @@ -4,14 +4,20 @@ namespace App\Actions\CheckoutRequests; use App\Models\Actionlog; use App\Models\Asset; +use App\Models\Company; use App\Models\Setting; use App\Models\User; use App\Notifications\RequestAssetCancelation; +use Illuminate\Auth\Access\AuthorizationException; -class CancelCheckoutRequest +class CancelCheckoutRequestAction { public static function run(Asset $asset, User $user) { + if (!Company::isCurrentUserHasAccess($asset)) { + throw new AuthorizationException(); + } + $asset->cancelRequest(); $asset->decrement('requests_counter', 1); diff --git a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php b/app/Actions/CheckoutRequests/CreateCheckoutRequestAction.php similarity index 93% rename from app/Actions/CheckoutRequests/CreateCheckoutRequest.php rename to app/Actions/CheckoutRequests/CreateCheckoutRequestAction.php index 9d1ba332ff..6870cfba2d 100644 --- a/app/Actions/CheckoutRequests/CreateCheckoutRequest.php +++ b/app/Actions/CheckoutRequests/CreateCheckoutRequestAction.php @@ -3,18 +3,16 @@ namespace App\Actions\CheckoutRequests; use App\Exceptions\AssetNotRequestable; -use App\Exceptions\ThereIsNoUser; use App\Models\Actionlog; use App\Models\Asset; use App\Models\Company; use App\Models\Setting; use App\Models\User; -use App\Notifications\RequestAssetCancelation; use App\Notifications\RequestAssetNotification; use Illuminate\Auth\Access\AuthorizationException; use Log; -class CreateCheckoutRequest +class CreateCheckoutRequestAction { /** * @throws AssetNotRequestable diff --git a/app/Http/Controllers/Api/CheckoutRequest.php b/app/Http/Controllers/Api/CheckoutRequest.php index 1ee9c4c025..9b66531ae4 100644 --- a/app/Http/Controllers/Api/CheckoutRequest.php +++ b/app/Http/Controllers/Api/CheckoutRequest.php @@ -2,40 +2,43 @@ namespace App\Http\Controllers\Api; -use App\Actions\CheckoutRequests\CancelCheckoutRequest; -use App\Actions\CheckoutRequests\CreateCheckoutRequest; +use App\Actions\CheckoutRequests\CancelCheckoutRequestAction; +use App\Actions\CheckoutRequests\CreateCheckoutRequestAction; use App\Exceptions\AssetNotRequestable; use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Models\Asset; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\JsonResponse; +use Exception; class CheckoutRequest extends Controller { public function store(Asset $asset): JsonResponse { try { - CreateCheckoutRequest::run($asset, auth()->user()); + CreateCheckoutRequestAction::run($asset, auth()->user()); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.success'))); } catch (AssetNotRequestable $e) { return response()->json(Helper::formatStandardApiResponse('error', 'Asset is not requestable')); } catch (AuthorizationException $e) { return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))); - } catch (\Exception $e) { + } catch (Exception $e) { report($e); - return response()->json(Helper::formatStandardApiResponse('error', null, 'Something terrible has gone wrong and we\'re not sure if we can help - may god have mercy on your soul. Contact your admin :)')); + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong'))); } } public function destroy(Asset $asset): JsonResponse { try { - CancelCheckoutRequest::run($asset, auth()->user()); + CancelCheckoutRequestAction::run($asset, auth()->user()); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.canceled'))); - } catch (\Exception $e) { + } catch (AuthorizationException $e) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions'))); + } catch (Exception $e) { report($e); - return response()->json(Helper::formatStandardApiResponse('error', null, $e->getMessage())); + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong'))); } } } diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 56d1f8b390..bbff6ba4f7 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -2,8 +2,8 @@ namespace App\Http\Controllers; -use App\Actions\CheckoutRequests\CancelCheckoutRequest; -use App\Actions\CheckoutRequests\CreateCheckoutRequest; +use App\Actions\CheckoutRequests\CancelCheckoutRequestAction; +use App\Actions\CheckoutRequests\CreateCheckoutRequestAction; use App\Exceptions\AssetNotRequestable; use App\Models\Actionlog; use App\Models\Asset; @@ -16,7 +16,7 @@ use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use \Illuminate\Contracts\View\View; -use Illuminate\Support\Facades\Log; +use Exception; /** * This controller handles all actions related to the ability for users @@ -150,26 +150,26 @@ class ViewAssetsController extends Controller public function store(Asset $asset): RedirectResponse { try { - CreateCheckoutRequest::run($asset, auth()->user()); + CreateCheckoutRequestAction::run($asset, auth()->user()); return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); } catch (AssetNotRequestable $e) { return redirect()->back()->with('error', 'Asset is not requestable'); } catch (AuthorizationException $e) { return redirect()->back()->with('error', trans('admin/hardware/message.requests.error')); - } catch (\Exception $e) { + } catch (Exception $e) { report($e); - return redirect()->back()->with('error', 'Something terrible has gone wrong and we\'re not sure if we can help - may god have mercy on your soul. Contact your admin :)'); + return redirect()->back()->with('error', trans('general.something_went_wrong')); } } public function destroy(Asset $asset): RedirectResponse { try { - CancelCheckoutRequest::run($asset, auth()->user()); + CancelCheckoutRequestAction::run($asset, auth()->user()); return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); - } catch (\Exception $e) { + } catch (Exception $e) { report($e); - return redirect()->back()->with('error', 'something bad happened'); + return redirect()->back()->with('error', trans('general.something_went_wrong')); } } diff --git a/routes/web.php b/routes/web.php index 5482de9cac..473ac9a2b4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,5 @@ Date: Mon, 13 Jan 2025 12:17:01 -0800 Subject: [PATCH 016/105] Introduce select component --- resources/views/blade/input/select.blade.php | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 resources/views/blade/input/select.blade.php diff --git a/resources/views/blade/input/select.blade.php b/resources/views/blade/input/select.blade.php new file mode 100644 index 0000000000..c96b5e1772 --- /dev/null +++ b/resources/views/blade/input/select.blade.php @@ -0,0 +1,25 @@ +@props([ + // can either be provided as key => value pairs + // or passed in via the default $slot + 'options', + 'selected' => null, + 'includeEmpty' => false, + 'forLivewire' => false, +]) + + From d74454620a728f9e8f9113c85ca7992ce4389904 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 12:45:49 -0800 Subject: [PATCH 017/105] Convert Form::select to blade component --- resources/views/categories/edit.blade.php | 9 ++++++++- resources/views/custom_fields/fields/edit.blade.php | 9 ++++++++- resources/views/custom_fields/fieldsets/view.blade.php | 8 ++++++-- resources/views/hardware/bulk.blade.php | 8 +++++++- resources/views/hardware/checkin.blade.php | 10 ++++++++-- resources/views/hardware/checkout.blade.php | 8 +++++++- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/resources/views/categories/edit.blade.php b/resources/views/categories/edit.blade.php index 1ef3b7aa6a..d19e44dcd4 100755 --- a/resources/views/categories/edit.blade.php +++ b/resources/views/categories/edit.blade.php @@ -15,7 +15,14 @@
- {{ Form::select('category_type', $category_types , old('category_type', $item->category_type), array('class'=>'select2', 'style'=>'min-width:350px', 'aria-label'=>'category_type', ($item->category_type!='') || ($item->itemCount() > 0) ? 'disabled' : '')) }} + {!! $errors->first('category_type', '') !!}
diff --git a/resources/views/custom_fields/fields/edit.blade.php b/resources/views/custom_fields/fields/edit.blade.php index 12c3a3fb73..463019f884 100644 --- a/resources/views/custom_fields/fields/edit.blade.php +++ b/resources/views/custom_fields/fields/edit.blade.php @@ -88,7 +88,14 @@ } @endphp
- {{ Form::select("format",Helper::predefined_formats(), ($field_format == '') ? $field->format : $field_format, array('class'=>'format select2 form-control', 'aria-label'=>'format', 'style' => 'width:100%;')) }} + {!! $errors->first('format', '') !!}
diff --git a/resources/views/custom_fields/fieldsets/view.blade.php b/resources/views/custom_fields/fieldsets/view.blade.php index b73ae708c6..6868c21634 100644 --- a/resources/views/custom_fields/fieldsets/view.blade.php +++ b/resources/views/custom_fields/fieldsets/view.blade.php @@ -98,8 +98,12 @@ - {{ Form::select("field_id",$custom_fields_list,"",['aria-label'=>'field_id', 'class'=>'select2', 'style' => 'min-width:400px;']) }} - +
@@ -142,4 +148,4 @@ -@stop \ No newline at end of file +@stop diff --git a/resources/views/hardware/checkout.blade.php b/resources/views/hardware/checkout.blade.php index e0dc8ef519..ca297fa9ce 100755 --- a/resources/views/hardware/checkout.blade.php +++ b/resources/views/hardware/checkout.blade.php @@ -82,7 +82,13 @@ {{ trans('admin/hardware/form.status') }}
- {{ Form::select('status_id', $statusLabel_list, $asset->status_id, array('class'=>'select2', 'style'=>'width:100%','', 'aria-label'=>'status_id')) }} + {!! $errors->first('status_id', '') !!}
From 2206b0a69937af9467609e173c1db16ca314fa75 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 12:50:18 -0800 Subject: [PATCH 018/105] Convert additional Form::select to blade component --- resources/views/hardware/quickscan-checkin.blade.php | 8 +++++++- ...ustom-field-set-default-values-for-model.blade.php | 11 ++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/resources/views/hardware/quickscan-checkin.blade.php b/resources/views/hardware/quickscan-checkin.blade.php index de892399cc..c79ca605d8 100644 --- a/resources/views/hardware/quickscan-checkin.blade.php +++ b/resources/views/hardware/quickscan-checkin.blade.php @@ -47,7 +47,13 @@ {{ trans('admin/hardware/form.status') }}
- {{ Form::select('status_id', $statusLabel_list, '', array('class'=>'select2', 'style'=>'width:100%','', 'aria-label'=>'status_id')) }} + {!! $errors->first('status_id', '') !!}
diff --git a/resources/views/livewire/custom-field-set-default-values-for-model.blade.php b/resources/views/livewire/custom-field-set-default-values-for-model.blade.php index 9f09d111b1..cf3eac3f07 100644 --- a/resources/views/livewire/custom-field-set-default-values-for-model.blade.php +++ b/resources/views/livewire/custom-field-set-default-values-for-model.blade.php @@ -4,7 +4,16 @@ {{ trans('admin/models/general.fieldset') }}
- {{ Form::select('fieldset_id', Helper::customFieldsetList(), old('fieldset_id', $fieldset_id), array('class'=>'select2 js-fieldset-field livewire-select2', 'style'=>'width:100%; min-width:350px', 'aria-label'=>'custom_fieldset', 'data-livewire-component' => $this->getId())) }} + {!! $errors->first('custom_fieldset', '') !!}
From 589ec3a564b83fb405f4d4d7e8113bf4edd56e78 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 13:08:59 -0800 Subject: [PATCH 019/105] Convert additional Form::select to blade component --- resources/views/livewire/importer.blade.php | 50 +++++++++++-------- .../livewire/slack-settings-form.blade.php | 18 ++++--- .../modals/partials/fieldset-select.blade.php | 12 ++++- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/resources/views/livewire/importer.blade.php b/resources/views/livewire/importer.blade.php index b0ba7e037b..3511a81bb2 100644 --- a/resources/views/livewire/importer.blade.php +++ b/resources/views/livewire/importer.blade.php @@ -155,15 +155,20 @@
- {{ Form::select('typeOfImport', $importTypes, $typeOfImport, [ - 'id' => 'import_type', - 'class' => 'livewire-select2', - 'style' => 'min-width: 350px', - 'data-placeholder' => trans('general.select_var', ['thing' => trans('general.import_type')]), - 'placeholder' => '', //needed so that the form-helper will put an empty option first - 'data-minimum-results-for-search' => '-1', // Remove this if the list gets long enough that we need to search - 'data-livewire-component' => $this->getId() - ]) }} + @if ($typeOfImport === 'asset' && $snipeSettings->auto_increment_assets == 0)

{{ trans('general.auto_incrementing_asset_tags_disabled_so_tags_required') }} @@ -238,17 +243,22 @@

- - {{ Form::select('field_map.'.$index, $columnOptions[$typeOfImport], @$field_map[$index], - [ - 'class' => 'mappings livewire-select2', - 'placeholder' => trans('general.importer.do_not_import'), - 'style' => 'min-width: 100%', - 'data-livewire-component' => $this->getId() - ],[ - '-' => ['disabled' => true] // this makes the "-----" line unclickable - ]) - }} + + + @foreach($columnOptions[$typeOfImport] as $key => $value) + + @endforeach +
@if (($this->activeFile->first_row) && (array_key_exists($index, $this->activeFile->first_row)))
diff --git a/resources/views/livewire/slack-settings-form.blade.php b/resources/views/livewire/slack-settings-form.blade.php index 76b3987229..09b4b03474 100644 --- a/resources/views/livewire/slack-settings-form.blade.php +++ b/resources/views/livewire/slack-settings-form.blade.php @@ -66,13 +66,17 @@
- - @if (Helper::isDemoMode()) - {{ Form::select('webhook_selected', array('slack' => trans('admin/settings/general.slack'), 'general' => trans('admin/settings/general.general_webhook'),'google' => trans('admin/settings/general.google_workspaces'), 'microsoft' => trans('admin/settings/general.ms_teams')), old('webhook_selected', $webhook_selected), array('class'=>'select2 form-control', 'aria-label' => 'webhook_selected', 'id' => 'select2', 'style'=>'width:100%', 'disabled')) }} - @else - {{ Form::select('webhook_selected', array('slack' => trans('admin/settings/general.slack'), 'general' => trans('admin/settings/general.general_webhook'),'google' => trans('admin/settings/general.google_workspaces'), 'microsoft' => trans('admin/settings/general.ms_teams')), old('webhook_selected', $webhook_selected), array('class'=>'select2 form-control', 'aria-label' => 'webhook_selected', 'id' => 'select2', 'data-minimum-results-for-search' => '-1', 'style'=>'width:100%')) }} - @endif - +
diff --git a/resources/views/modals/partials/fieldset-select.blade.php b/resources/views/modals/partials/fieldset-select.blade.php index 3f4efc24b6..ec1e178f22 100644 --- a/resources/views/modals/partials/fieldset-select.blade.php +++ b/resources/views/modals/partials/fieldset-select.blade.php @@ -1,6 +1,14 @@
-
{{ Form::select('fieldset_id', Helper::customFieldsetList(),old('fieldset_id'), array('class'=>'select2', 'id'=>'modal-fieldset_id', 'style'=>'width:100%;')) }}
+
+ +
- \ No newline at end of file + From 1f4cc0a4b4b01c887f247de53876b7e922d2fc25 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 15:23:43 -0800 Subject: [PATCH 020/105] Convert additional Form::select to blade component --- resources/views/modals/statuslabel.blade.php | 10 +++++++++- resources/views/models/bulk-edit.blade.php | 15 +++++++++++++-- .../views/models/custom_fields_form.blade.php | 9 +++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/resources/views/modals/statuslabel.blade.php b/resources/views/modals/statuslabel.blade.php index e7076f1bbb..a6676c9292 100644 --- a/resources/views/modals/statuslabel.blade.php +++ b/resources/views/modals/statuslabel.blade.php @@ -16,7 +16,15 @@
-
{{ Form::select('type', $statuslabel_types, '', array('class'=>'select2', 'style'=>'width:100%','id' =>'modal-type', 'required',)) }}
+
+ +
diff --git a/resources/views/models/bulk-edit.blade.php b/resources/views/models/bulk-edit.blade.php index 8a053b048f..5539149471 100644 --- a/resources/views/models/bulk-edit.blade.php +++ b/resources/views/models/bulk-edit.blade.php @@ -62,7 +62,13 @@ {{ trans('admin/models/general.fieldset') }}
- {{ Form::select('fieldset_id', $fieldset_list , old('fieldset_id', 'NC'), array('class'=>'select2 js-fieldset-field', 'style'=>'width:350px')) }} + {!! $errors->first('fieldset_id', '') !!}
@@ -74,7 +80,12 @@ {{ trans('general.depreciation') }}
- {{ Form::select('depreciation_id', $depreciation_list , old('depreciation_id', 'NC'), array('class'=>'select2', 'style'=>'width:350px')) }} + {!! $errors->first('depreciation_id', '') !!}
diff --git a/resources/views/models/custom_fields_form.blade.php b/resources/views/models/custom_fields_form.blade.php index 4949fae71c..1da3609c92 100644 --- a/resources/views/models/custom_fields_form.blade.php +++ b/resources/views/models/custom_fields_form.blade.php @@ -9,8 +9,13 @@ @if ($field->element=='listbox') - {{ Form::select($field->db_column_name(), $field->formatFieldValuesAsArray(), - old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))), ['class' => 'format select2 form-control', ($field->pivot->required=='1' ? ' required' : '') ]) }} + @elseif ($field->element=='textarea') From bc3f236b86c827fe3c9abba9cb41d7fabcb2507e Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 15:38:45 -0800 Subject: [PATCH 021/105] Weaken comparsion so string and int keys match --- resources/views/blade/input/select.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/blade/input/select.blade.php b/resources/views/blade/input/select.blade.php index c96b5e1772..9ddbbd6d7c 100644 --- a/resources/views/blade/input/select.blade.php +++ b/resources/views/blade/input/select.blade.php @@ -17,7 +17,7 @@ {{-- map the simple key => value pairs when nothing is passed in via the slot --}} @if($slot->isEmpty()) @foreach($options as $key => $value) - + @endforeach @else {{ $slot }} From 888c96718ca036379b61270738c128f18690a4d0 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 15:38:51 -0800 Subject: [PATCH 022/105] Convert additional Form::select to blade component --- .../views/models/custom_fields_form_bulk_edit.blade.php | 8 ++++++-- .../views/partials/forms/edit/depreciation.blade.php | 9 ++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/resources/views/models/custom_fields_form_bulk_edit.blade.php b/resources/views/models/custom_fields_form_bulk_edit.blade.php index e048b38d09..cea495ccd2 100644 --- a/resources/views/models/custom_fields_form_bulk_edit.blade.php +++ b/resources/views/models/custom_fields_form_bulk_edit.blade.php @@ -24,8 +24,12 @@ @if ($field->element!='text') @if ($field->element=='listbox') - {{ Form::select($field->db_column_name(), $field->formatFieldValuesAsArray(), - old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))), ['class'=>'format select2 form-control']) }} + @elseif ($field->element=='textarea') @if($field->is_unique) diff --git a/resources/views/partials/forms/edit/depreciation.blade.php b/resources/views/partials/forms/edit/depreciation.blade.php index 4478a9c05f..8195ac394e 100644 --- a/resources/views/partials/forms/edit/depreciation.blade.php +++ b/resources/views/partials/forms/edit/depreciation.blade.php @@ -2,7 +2,14 @@
- {{ Form::select('depreciation_id', $depreciation_list , old('depreciation_id', $item->depreciation_id), array('class'=>'select2', 'style'=>'width:350px', 'aria-label'=>'depreciation_id')) }} + {!! $errors->first('depreciation_id', '') !!}
From 3eb307a01900a512d2e44260b157af7d30e035c9 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 15:58:15 -0800 Subject: [PATCH 023/105] Convert additional Form::select to blade component --- .../forms/edit/maintenance_type.blade.php | 9 +++- .../partials/forms/edit/status.blade.php | 11 ++++- resources/views/settings/branding.blade.php | 47 +++++++++++++++++-- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/resources/views/partials/forms/edit/maintenance_type.blade.php b/resources/views/partials/forms/edit/maintenance_type.blade.php index 24d008fa23..b82bead76b 100644 --- a/resources/views/partials/forms/edit/maintenance_type.blade.php +++ b/resources/views/partials/forms/edit/maintenance_type.blade.php @@ -3,7 +3,14 @@
- {{ Form::select('asset_maintenance_type', $assetMaintenanceType , old('asset_maintenance_type', $item->asset_maintenance_type), ['class'=>'select2', 'aria-label'=>'asset_maintenance_type', 'required' => Helper::checkIfRequired($item, 'asset_maintenance_type') ? true : '', 'style'=> 'width:100%;']) }} + {!! $errors->first('asset_maintenance_type', '') !!}
diff --git a/resources/views/partials/forms/edit/status.blade.php b/resources/views/partials/forms/edit/status.blade.php index 8554f9a59c..01dd14612e 100644 --- a/resources/views/partials/forms/edit/status.blade.php +++ b/resources/views/partials/forms/edit/status.blade.php @@ -2,7 +2,16 @@
- {{ Form::select('status_id', $statuslabel_list , old('status_id', $item->status_id), array('class'=>'select2 status_id', 'style'=>'width:100%','id'=>'status_select_id', 'aria-label'=>'status_id', 'required' => 'required')) }} + {!! $errors->first('status_id', '') !!}
diff --git a/resources/views/settings/branding.blade.php b/resources/views/settings/branding.blade.php index c586917ef5..be6120a4f7 100644 --- a/resources/views/settings/branding.blade.php +++ b/resources/views/settings/branding.blade.php @@ -67,7 +67,14 @@ {{ Form::label('brand', trans('admin/settings/general.web_brand')) }}
- {!! Form::select('brand', array('1'=>'Text','2'=>'Logo','3'=>'Logo + Text'), old('brand', $setting->brand), array('class' => 'form-control select2', 'style'=>'width: 150px ;')) !!} + {!! $errors->first('brand', '') !!}
@@ -252,10 +259,25 @@
@if (config('app.lock_passwords')===true) - {!! Form::select('support_footer', array('on'=>'Enabled','off'=>'Disabled','admin'=>'Superadmin Only'), old('support_footer', $setting->support_footer), ['class' => 'form-control select2 disabled', 'style'=>'width: 150px ;', 'disabled' => 'disabled']) !!} +

{{ trans('general.feature_disabled') }}

@else - {!! Form::select('support_footer', array('on'=>'Enabled','off'=>'Disabled','admin'=>'Superadmin Only'), old('support_footer', $setting->support_footer), array('class' => 'form-control select2', 'style'=>'width: 150px ;')) !!} + @endif @@ -271,10 +293,25 @@
@if (config('app.lock_passwords')===true) - {!! Form::select('version_footer', array('on'=>'Enabled','off'=>'Disabled','admin'=>'Superadmin Only'), old('version_footer', $setting->version_footer), ['class' => 'form-control select2 disabled', 'style'=>'width: 150px ;', 'disabled' => 'disabled']) !!} +

{{ trans('general.feature_disabled') }}

@else - {!! Form::select('version_footer', array('on'=>'Enabled','off'=>'Disabled','admin'=>'Superadmin Only'), old('version_footer', $setting->version_footer), array('class' => 'form-control select2', 'style'=>'width: 150px ;')) !!} + @endif

{{ trans('admin/settings/general.version_footer_help') }}

From 9a043da005f2be2e90b5191c3d2baddeaf840b84 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 16:16:49 -0800 Subject: [PATCH 024/105] Convert additional Form::select to blade component --- resources/views/settings/general.blade.php | 21 ++++++++----- resources/views/settings/labels.blade.php | 33 +++++++++++++++------ resources/views/statuslabels/edit.blade.php | 8 ++++- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/resources/views/settings/general.blade.php b/resources/views/settings/general.blade.php index 1de5d4035d..3a549525d3 100644 --- a/resources/views/settings/general.blade.php +++ b/resources/views/settings/general.blade.php @@ -359,9 +359,12 @@ trans('general.pie_chart_type')) }}
- {{ Form::select('dash_chart_type', array( - 'name' => 'Status Label Name', - 'type' => 'Status Label Type'), old('dash_chart_type', $setting->dash_chart_type), ['class' =>'select2', 'style' => 'width: 80%']) }} +
@@ -372,11 +375,13 @@ {{ Form::label('depreciation_method', trans('Depreciation method')) }}
- {{ Form::select('depreciation_method', array( - 'default' => 'Linear (default)', - 'half_1' => 'Half-year convention, always applied', - 'half_2' => 'Half-year convention, applied with condition', - ), old('username_format', $setting->depreciation_method), ['class' =>'select2', 'style' => 'width: 80%']) }} +
diff --git a/resources/views/settings/labels.blade.php b/resources/views/settings/labels.blade.php index fa8c5dc858..8666ab8727 100644 --- a/resources/views/settings/labels.blade.php +++ b/resources/views/settings/labels.blade.php @@ -170,7 +170,14 @@ 'none' => trans('admin/settings/general.none'), ]; @endphp - {{ Form::select('label2_1d_type', $select1DValues, old('label2_1d_type', $setting->label2_1d_type), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_1d_type' ]) }} + {!! $errors->first('label2_1d_type', '') !!}

{{ trans('admin/settings/general.label2_1d_type_help') }}. @@ -214,7 +221,14 @@ 'none' => trans('admin/settings/general.none'), ]); @endphp - {{ Form::select('label2_2d_type', $select2DValues, old('label2_2d_type', $setting->label2_2d_type), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_2d_type' ]) }} + {!! $errors->first('label2_2d_type', '') !!}

{{ trans('admin/settings/general.label2_2d_type_help', ['current' => $setting->barcode_type]) }}. @@ -277,13 +291,14 @@ {{ Form::label('label2_2d_target', trans('admin/settings/general.label2_2d_target'), ['class'=>'control-label']) }}

- {{ Form::select('label2_2d_target', [ - 'hardware_id' => trans('general.url') .': /hardware/{id} ('.trans('admin/settings/general.default').')', - 'ht_tag' => trans('general.url') .': /ht/{asset_tag}', - 'plain_asset_id' => trans('admin/settings/general.data') .': '. trans('admin/settings/general.asset_id') .' {id}', - 'plain_asset_tag' => trans('admin/settings/general.data') .': '. trans('general.asset_tag') .' {asset_tag}', - 'plain_serial_number' => trans('admin/settings/general.data') .': '. trans('general.serial_number') .' {serial}', - ], old('label2_2d_target', $setting->label2_2d_target), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_2d_target' ]) }} + {!! $errors->first('label2_2d_target', '') !!}

{{ trans('admin/settings/general.label2_2d_target_help') }}

diff --git a/resources/views/statuslabels/edit.blade.php b/resources/views/statuslabels/edit.blade.php index af69f35de8..27fb91148b 100755 --- a/resources/views/statuslabels/edit.blade.php +++ b/resources/views/statuslabels/edit.blade.php @@ -27,7 +27,13 @@ {{ trans('admin/statuslabels/table.status_type') }}
- {{ Form::select('statuslabel_types', $statuslabel_types, $item->getStatuslabelType(), array('class'=>'select2', 'style'=>'width: 100%; min-width:400px', 'aria-label'=>'statuslabel_types')) }} + {!! $errors->first('statuslabel_types', '') !!}
From 3fadeebd8240638951db7235911d4390c5aefa96 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 16:20:02 -0800 Subject: [PATCH 025/105] Convert additional Form::select to blade component --- resources/views/users/confirm-bulk-delete.blade.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/views/users/confirm-bulk-delete.blade.php b/resources/views/users/confirm-bulk-delete.blade.php index 8e4ba33071..0234ce576b 100644 --- a/resources/views/users/confirm-bulk-delete.blade.php +++ b/resources/views/users/confirm-bulk-delete.blade.php @@ -109,7 +109,15 @@ - {{ Form::select('status_id', $statuslabel_list , old('status_id'), array('class'=>'select2', 'style'=>'width:250px', 'required' => true)) }} + @@ -159,4 +167,4 @@ } }); -@stop \ No newline at end of file +@stop From abb50fcd0951618372139f870cdceb18cd38f2ef Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 13 Jan 2025 16:21:19 -0800 Subject: [PATCH 026/105] Delete unused partials --- .../views/partials/forms/edit/category.blade.php | 8 -------- resources/views/partials/forms/edit/company.blade.php | 11 ----------- .../views/partials/forms/edit/location.blade.php | 8 -------- .../views/partials/forms/edit/manufacturer.blade.php | 8 -------- .../views/partials/forms/edit/supplier.blade.php | 11 ----------- 5 files changed, 46 deletions(-) delete mode 100644 resources/views/partials/forms/edit/category.blade.php delete mode 100644 resources/views/partials/forms/edit/company.blade.php delete mode 100644 resources/views/partials/forms/edit/location.blade.php delete mode 100644 resources/views/partials/forms/edit/manufacturer.blade.php delete mode 100644 resources/views/partials/forms/edit/supplier.blade.php diff --git a/resources/views/partials/forms/edit/category.blade.php b/resources/views/partials/forms/edit/category.blade.php deleted file mode 100644 index 158cc3508b..0000000000 --- a/resources/views/partials/forms/edit/category.blade.php +++ /dev/null @@ -1,8 +0,0 @@ - -
- -
- {{ Form::select('category_id', $category_list , old('category_id', $item->category_id), array('class'=>'select2', 'style'=>'width:100%', 'required' => Helper::checkIfRequired($item, 'category_id') ? true : '')) }} - {!! $errors->first('category_id', '') !!} -
-
diff --git a/resources/views/partials/forms/edit/company.blade.php b/resources/views/partials/forms/edit/company.blade.php deleted file mode 100644 index 22cb623584..0000000000 --- a/resources/views/partials/forms/edit/company.blade.php +++ /dev/null @@ -1,11 +0,0 @@ -@if (\App\Models\Company::isCurrentUserAuthorized()) -
-
- {{ Form::label('company_id', trans('general.company'), array('class' => 'col-md-3 control-label', 'for' => 'company_id')) }} -
-
- {{ Form::select('company_id', $company_list , old('company_id', $item->company_id), array('class'=>'select2', 'style'=>'width:100%', 'required' => Helper::checkIfRequired($item, 'company_id') ? true : '')) }} - {!! $errors->first('company_id', '') !!} -
-
-@endif diff --git a/resources/views/partials/forms/edit/location.blade.php b/resources/views/partials/forms/edit/location.blade.php deleted file mode 100644 index 49bc2976d0..0000000000 --- a/resources/views/partials/forms/edit/location.blade.php +++ /dev/null @@ -1,8 +0,0 @@ - -
- -
- {{ Form::select('location_id', $location_list , old('location_id', $item->location_id), array('class'=>'select2', 'style'=>'width:350px')) }} - {!! $errors->first('location_id', '') !!} -
-
diff --git a/resources/views/partials/forms/edit/manufacturer.blade.php b/resources/views/partials/forms/edit/manufacturer.blade.php deleted file mode 100644 index 644b70d7ba..0000000000 --- a/resources/views/partials/forms/edit/manufacturer.blade.php +++ /dev/null @@ -1,8 +0,0 @@ - -
- -
- {{ Form::select('manufacturer_id', $manufacturer_list , old('manufacturer_id', $item->manufacturer_id), array('class'=>'select2', 'style'=>'width:100%', 'required' => Helper::checkIfRequired($item, 'manufacturer_id') ? true : '')) }} - {!! $errors->first('manufacturer_id', '') !!} -
-
diff --git a/resources/views/partials/forms/edit/supplier.blade.php b/resources/views/partials/forms/edit/supplier.blade.php deleted file mode 100644 index 40c1e2b279..0000000000 --- a/resources/views/partials/forms/edit/supplier.blade.php +++ /dev/null @@ -1,11 +0,0 @@ - -
- -
- {{ Form::select('supplier_id', $supplier_list , old('supplier_id', $item->supplier_id), ['class'=>'select2', 'style'=>'min-width:350px', 'id' => 'supplier_select_id', 'required' => Helper::checkIfRequired($item, 'supplier_id') ? true : '']) }} - {!! $errors->first('supplier_id', '') !!} -
- -
From f42fcd25b1fad27468ddf4f6a19849ab6bb2e6ee Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 6 Mar 2025 12:05:36 +0000 Subject: [PATCH 027/105] Make the assets tab active by default on locations page Signed-off-by: snipe --- resources/views/locations/view.blade.php | 68 +++++++++++++----------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/resources/views/locations/view.blade.php b/resources/views/locations/view.blade.php index b10cb315e0..770e004d00 100644 --- a/resources/views/locations/view.blade.php +++ b/resources/views/locations/view.blade.php @@ -157,7 +157,42 @@
-
+ +
+

{{ trans('admin/locations/message.current_location') }}

+ +
+ @include('partials.asset-bulk-actions') + +
+ +
+
+ + + +

{{ trans('general.users') }}

@include('partials.users-bulk-actions') @@ -188,37 +223,6 @@
-
-

{{ trans('admin/locations/message.current_location') }}

- -
- @include('partials.asset-bulk-actions') - -
- -
-

From 6d74053ca393da0ad48e6d3876480dc9e7f00a53 Mon Sep 17 00:00:00 2001 From: snipe Date: Thu, 6 Mar 2025 15:02:55 +0000 Subject: [PATCH 028/105] Added modal tests Signed-off-by: snipe --- tests/Feature/Modals/Ui/ShowModalsTest.php | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/Feature/Modals/Ui/ShowModalsTest.php diff --git a/tests/Feature/Modals/Ui/ShowModalsTest.php b/tests/Feature/Modals/Ui/ShowModalsTest.php new file mode 100644 index 0000000000..308460999f --- /dev/null +++ b/tests/Feature/Modals/Ui/ShowModalsTest.php @@ -0,0 +1,60 @@ +actingAs(User::factory()->createUsers()->create()) + ->get('modals/user') + ->assertOk(); + } + + public function testDepartmentModalRenders() + { + $this->actingAs(User::factory()->superuser()->create()) + ->get('modals/model') + ->assertOk(); + } + + public function testStatusLabelModalRenders() + { + $this->actingAs(User::factory()->superuser()->create()) + ->get('modals/statuslabel') + ->assertOk(); + } + + public function testLocationModalRenders() + { + $this->actingAs(User::factory()->superuser()->create()) + ->get('modals/location') + ->assertOk(); + } + + public function testCategoryModalRenders() + { + $this->actingAs(User::factory()->superuser()->create()) + ->get('modals/category') + ->assertOk(); + } + + public function testManufacturerModalRenders() + { + $this->actingAs(User::factory()->superuser()->create()) + ->get('modals/manufacturer') + ->assertOk(); + } + + public function testSupplierModalRenders() + { + $this->actingAs(User::factory()->superuser()->create()) + ->get('modals/supplier') + ->assertOk(); + } + + +} From 0b6d810ca63cb91bfd4c27888f8c4696f1df5cb0 Mon Sep 17 00:00:00 2001 From: James M <31522486+azmcnutt@users.noreply.github.com> Date: Thu, 6 Mar 2025 11:12:16 -0700 Subject: [PATCH 029/105] FEAT: Add Label 5520 with 1D barcode - remove 2D barcode FEAT: Add Label 5520 with 1D barcode - remove 2D barcode --- app/Models/Labels/Sheets/Avery/_5520_B.php | 84 ++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 app/Models/Labels/Sheets/Avery/_5520_B.php diff --git a/app/Models/Labels/Sheets/Avery/_5520_B.php b/app/Models/Labels/Sheets/Avery/_5520_B.php new file mode 100644 index 0000000000..eb6494e0aa --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/_5520_B.php @@ -0,0 +1,84 @@ +getLabelPrintableArea(); + + $currentX = $pa->x1; + $currentY = $pa->y1; + $usableWidth = $pa->w; + $usableHeight = $pa->h; + + if ($record->has('title')) { + static::writeText( + $pdf, $record->get('title'), + $pa->x1, $pa->y1, + 'freesans', '', self::TITLE_SIZE, 'C', + $pa->w, self::TITLE_SIZE, true, 0 + ); + $currentY += self::TITLE_SIZE + self::TITLE_MARGIN; + $usableHeight -= self::TITLE_SIZE + self::TITLE_MARGIN; + } + + if ($record->has('barcode1d')) { + static::write1DBarcode( + $pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type, + $pa->x1, $pa->y2 - self::BARCODE_SIZE, + $usableWidth, self::BARCODE_SIZE + ); + $usableHeight -= self::BARCODE_SIZE + self::BARCODE_MARGIN; + } + + foreach ($record->get('fields') as $field) { + static::writeText( + $pdf, $field['label'], + $currentX, $currentY, + 'freesans', '', self::LABEL_SIZE, 'L', + $usableWidth, self::LABEL_SIZE, true, 0 + ); + $currentY += self::LABEL_SIZE + self::LABEL_MARGIN; + + static::writeText( + $pdf, $field['value'], + $currentX, $currentY, + 'freemono', 'B', self::FIELD_SIZE, 'L', + $usableWidth, self::FIELD_SIZE, true, 0, 0.01 + ); + $currentY += self::FIELD_SIZE + self::FIELD_MARGIN; + } + + } +} + + +?> \ No newline at end of file From 618d81777a19aa881920a9e0a3a8651ca5a278e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Pittet?= Date: Thu, 6 Mar 2025 12:33:29 -0800 Subject: [PATCH 030/105] Fixes CVE-2025-27515 --- composer.lock | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index 617cc487d7..9acc60817e 100644 --- a/composer.lock +++ b/composer.lock @@ -2677,16 +2677,16 @@ }, { "name": "laravel/framework", - "version": "v11.38.2", + "version": "v11.44.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "9d290aa90fcad44048bedca5219d2b872e98772a" + "reference": "0883d4175f4e2b5c299e7087ad3c74f2ce195c6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/9d290aa90fcad44048bedca5219d2b872e98772a", - "reference": "9d290aa90fcad44048bedca5219d2b872e98772a", + "url": "https://api.github.com/repos/laravel/framework/zipball/0883d4175f4e2b5c299e7087ad3c74f2ce195c6d", + "reference": "0883d4175f4e2b5c299e7087ad3c74f2ce195c6d", "shasum": "" }, "require": { @@ -2712,7 +2712,7 @@ "league/flysystem-local": "^3.25.1", "league/uri": "^7.5.1", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.72.2|^3.4", + "nesbot/carbon": "^2.72.6|^3.8.4", "nunomaduro/termwind": "^2.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", @@ -2787,17 +2787,18 @@ "fakerphp/faker": "^1.24", "guzzlehttp/promises": "^2.0.3", "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", "league/flysystem-aws-s3-v3": "^3.25.1", "league/flysystem-ftp": "^3.25.1", "league/flysystem-path-prefixing": "^3.25.1", "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.6", + "orchestra/testbench-core": "^9.11.2", "pda/pheanstalk": "^5.0.6", "php-http/discovery": "^1.15", - "phpstan/phpstan": "^1.11.5", - "phpunit/phpunit": "^10.5.35|^11.3.6", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5.35|^11.3.6|^12.0.1", "predis/predis": "^2.3", "resend/resend-php": "^0.10.0", "symfony/cache": "^7.0.3", @@ -2829,7 +2830,7 @@ "mockery/mockery": "Required to use mocking (^1.6).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", - "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.3.6|^12.0.1).", "predis/predis": "Required to use the predis connector (^2.3).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", @@ -2887,7 +2888,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-15T00:06:46+00:00" + "time": "2025-03-05T15:34:10+00:00" }, { "name": "laravel/helpers", From bac2760c6def6b79ca00da5c0bf77fbccea93f0d Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 10 Mar 2025 12:55:35 -0700 Subject: [PATCH 031/105] Replace Form::close --- resources/views/depreciations/view.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/depreciations/view.blade.php b/resources/views/depreciations/view.blade.php index 02ac7f335b..8dda5e64f2 100644 --- a/resources/views/depreciations/view.blade.php +++ b/resources/views/depreciations/view.blade.php @@ -171,7 +171,7 @@

- {{ Form::close() }} +
From 71d93ca3c38f6813c46bae77e62ab8be70944c30 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 11 Mar 2025 11:41:26 -0700 Subject: [PATCH 032/105] Use dedicated location select component Copy/paste/modify from partials.forms.edit.location-select --- .../blade/input/location-select.blade.php | 62 +++++++++++++++++++ resources/views/hardware/checkin.blade.php | 21 ++++++- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 resources/views/blade/input/location-select.blade.php diff --git a/resources/views/blade/input/location-select.blade.php b/resources/views/blade/input/location-select.blade.php new file mode 100644 index 0000000000..3b513b95d4 --- /dev/null +++ b/resources/views/blade/input/location-select.blade.php @@ -0,0 +1,62 @@ +@use('App\Models\Location', 'Location') +@use('Illuminate\Support\Arr', 'Arr') + +@props([ + 'label', + 'name', + 'selected', + 'required' => false, + 'multiple' => false, + 'helpText' => null, + 'hideNewButton' => false, +]) + +
$errors->has($name), + ]) +> + + +
+ +
+ +
+ @unless($hideNewButton) + @can('create', Location::class) + {{ trans('button.new') }} + @endcan + @endunless +
+ + {!! $errors->first($name, '
') !!} + + @if ($helpText) +
+

{{ $helpText }}

+
+ @endif + +
diff --git a/resources/views/hardware/checkin.blade.php b/resources/views/hardware/checkin.blade.php index 5510ff89a5..fbf2aa75ce 100755 --- a/resources/views/hardware/checkin.blade.php +++ b/resources/views/hardware/checkin.blade.php @@ -95,7 +95,26 @@ - @include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id', 'help_text' => ($asset->defaultLoc) ? trans('general.checkin_to_diff_location', ['default_location' => $asset->defaultLoc->name]) : null, 'hide_location_radio' => true]) + + + +
+
+ + +
+
From 18b208bba24d2c544a3198c233596bf157fa1700 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 11 Mar 2025 11:46:38 -0700 Subject: [PATCH 033/105] Account for missing location --- resources/views/blade/input/location-select.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/blade/input/location-select.blade.php b/resources/views/blade/input/location-select.blade.php index 3b513b95d4..c3c1e50b99 100644 --- a/resources/views/blade/input/location-select.blade.php +++ b/resources/views/blade/input/location-select.blade.php @@ -36,7 +36,7 @@ @if ($selected) @foreach(Arr::wrap($selected) as $id) @endforeach @endif From fd854072b06944c78df15e429aba5214fe4dfdb4 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 11 Mar 2025 13:03:00 -0700 Subject: [PATCH 034/105] Properly handle route model bound LicenseSeat not being found --- app/Exceptions/Handler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 6e35f8217a..e340d70b09 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -141,6 +141,8 @@ class Handler extends ExceptionHandler $route = 'kits.index'; } elseif ($route == 'assetmaintenances.index') { $route = 'maintenances.index'; + } elseif ($route === 'licenseseats.index') { + $route = 'licenses.index'; } return redirect() @@ -203,4 +205,4 @@ class Handler extends ExceptionHandler // }); } -} \ No newline at end of file +} From 9cbcfba4e9a0b67c11483ab55abd079c8a0a1371 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 11 Mar 2025 13:35:50 -0700 Subject: [PATCH 035/105] Add test --- .../Redirects/ModelNotFoundRedirectTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/Feature/Redirects/ModelNotFoundRedirectTest.php diff --git a/tests/Feature/Redirects/ModelNotFoundRedirectTest.php b/tests/Feature/Redirects/ModelNotFoundRedirectTest.php new file mode 100644 index 0000000000..2663d21cdb --- /dev/null +++ b/tests/Feature/Redirects/ModelNotFoundRedirectTest.php @@ -0,0 +1,16 @@ +actingAs(User::factory()->viewLicenses()->create()) + ->get(route('licenses.checkin', 9999)) + ->assertRedirectToRoute('licenses.index'); + } +} From e2f643e7ed6621c55d3fcf0c4ee5b9119a81cc80 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 11 Mar 2025 13:46:21 -0700 Subject: [PATCH 036/105] Backfill tests --- database/factories/UserFactory.php | 10 ++++++ .../Redirects/ModelNotFoundRedirectTest.php | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 4b752b736f..604666eda4 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -150,6 +150,11 @@ class UserFactory extends Factory return $this->appendPermission(['models.delete' => '1']); } + public function viewAssetModels() + { + return $this->appendPermission(['models.view' => '1']); + } + public function viewAccessories() { return $this->appendPermission(['accessories.view' => '1']); @@ -360,6 +365,11 @@ class UserFactory extends Factory return $this->appendPermission(['kits.delete' => '1']); } + public function viewPredefinedKits() + { + return $this->appendPermission(['kits.view' => '1']); + } + public function deleteStatusLabels() { return $this->appendPermission(['statuslabels.delete' => '1']); diff --git a/tests/Feature/Redirects/ModelNotFoundRedirectTest.php b/tests/Feature/Redirects/ModelNotFoundRedirectTest.php index 2663d21cdb..26401e9607 100644 --- a/tests/Feature/Redirects/ModelNotFoundRedirectTest.php +++ b/tests/Feature/Redirects/ModelNotFoundRedirectTest.php @@ -7,10 +7,45 @@ use Tests\TestCase; class ModelNotFoundRedirectTest extends TestCase { + public function testHandlesAsset404() + { + $this->actingAs(User::factory()->viewAssets()->create()) + ->get(route('hardware.checkout.create', 9999)) + ->assertRedirectToRoute('hardware.index'); + } + + public function testHandlesAssetMaintenance404() + { + $this->actingAs(User::factory()->viewAssets()->create()) + ->get(route('maintenances.show', 9999)) + ->assertRedirectToRoute('maintenances.index'); + } + + public function testHandlesAssetModel404() + { + $this->actingAs(User::factory()->viewAssetModels()->create()) + ->get(route('models.show', 9999)) + ->assertRedirectToRoute('models.index'); + } + public function testHandlesLicenseSeat404() { $this->actingAs(User::factory()->viewLicenses()->create()) ->get(route('licenses.checkin', 9999)) ->assertRedirectToRoute('licenses.index'); } + + public function testHandlesPredefinedKit404() + { + $this->actingAs(User::factory()->viewPredefinedKits()->create()) + ->get(route('kits.show', 9999)) + ->assertRedirectToRoute('kits.index'); + } + + public function testHandlesReportTemplate404() + { + $this->actingAs(User::factory()->canViewReports()->create()) + ->get(route('report-templates.show', 9999)) + ->assertRedirectToRoute('reports/custom'); + } } From 2960a1377265aa215f95ba6eb472e579de779e11 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 11 Mar 2025 21:24:44 +0000 Subject: [PATCH 037/105] Upgrade webpack from 5.97.1 to 5.98.0 #16461 Signed-off-by: snipe --- package-lock.json | 147 ++++++++++++++++++++++++++++++++++++++++------ package.json | 2 +- 2 files changed, 130 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7452f9f61e..2cb35a981d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,7 @@ "signature_pad": "^4.2.0", "tableexport.jquery.plugin": "1.30.0", "tether": "^1.4.0", - "webpack": "^5.97.1" + "webpack": "^5.98.0" }, "devDependencies": { "all-contributors-cli": "^6.26.1", @@ -2605,7 +2605,6 @@ }, "node_modules/ajv-formats": { "version": "2.1.1", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -2621,7 +2620,6 @@ }, "node_modules/ajv-formats/node_modules/ajv": { "version": "8.13.0", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -2636,7 +2634,6 @@ }, "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", - "dev": true, "license": "MIT" }, "node_modules/ajv-keywords": { @@ -5628,6 +5625,21 @@ "version": "2.1.1", "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/fastclick": { "version": "1.0.6" }, @@ -9520,7 +9532,6 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10443,8 +10454,9 @@ } }, "node_modules/terser": { - "version": "5.31.0", - "license": "BSD-2-Clause", + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -10459,14 +10471,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "license": "MIT", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -10490,6 +10503,55 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "license": "MIT" @@ -10901,9 +10963,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "version": "5.98.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", + "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -10923,9 +10985,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", + "schema-utils": "^4.3.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", + "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, @@ -11208,6 +11270,55 @@ "source-map": "~0.6.1" } }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/webpack/node_modules/webpack-sources": { "version": "3.2.3", "license": "MIT", diff --git a/package.json b/package.json index b1af8ce525..4ae2bea149 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,6 @@ "signature_pad": "^4.2.0", "tableexport.jquery.plugin": "1.30.0", "tether": "^1.4.0", - "webpack": "^5.97.1" + "webpack": "^5.98.0" } } From b5fa538a54c12263568d3e698625fb33e9c4ec03 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 11 Mar 2025 16:05:02 -0700 Subject: [PATCH 038/105] Set created_by correctly in he action log --- app/Http/Controllers/Users/BulkUsersController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Users/BulkUsersController.php b/app/Http/Controllers/Users/BulkUsersController.php index 119da3f7c4..1909dd8214 100644 --- a/app/Http/Controllers/Users/BulkUsersController.php +++ b/app/Http/Controllers/Users/BulkUsersController.php @@ -360,7 +360,7 @@ class BulkUsersController extends Controller $logAction->item_type = Accessory::class; $logAction->target_id = $accessoryUserRow->assigned_to; $logAction->target_type = User::class; - $logAction->created_at = auth()->id(); + $logAction->created_by = auth()->id(); $logAction->note = 'Bulk checkin items'; $logAction->logaction('checkin from'); } @@ -374,7 +374,7 @@ class BulkUsersController extends Controller $logAction->item_type = Consumable::class; $logAction->target_id = $consumableUserRow->assigned_to; $logAction->target_type = User::class; - $logAction->created_at = auth()->id(); + $logAction->created_by = auth()->id(); $logAction->note = 'Bulk checkin items'; $logAction->logaction('checkin from'); } From c73dba4a435a44e24adf5de62a6edd34de917d7f Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 17:28:05 +0000 Subject: [PATCH 039/105] Better visual spacing Signed-off-by: snipe --- app/Http/Controllers/ModalController.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/ModalController.php b/app/Http/Controllers/ModalController.php index 5b8d5e0ea2..10b17ec8e2 100644 --- a/app/Http/Controllers/ModalController.php +++ b/app/Http/Controllers/ModalController.php @@ -41,10 +41,11 @@ class ModalController extends Controller $view = view("modals.${type}"); if ($type == "statuslabel") { - $view->with('statuslabel_types', Helper::statusTypeList()); - } - if (in_array($type, ['kit-model', 'kit-license', 'kit-consumable', 'kit-accessory'])) { - $view->with('kitId', $itemId); + $view->with('statuslabel_types', Helper::statusTypeList()); + } + + if (in_array($type, ['kit-model', 'kit-license', 'kit-consumable', 'kit-accessory'])) { + $view->with('kitId', $itemId); } return $view; } From 163ddc8026e1e8d3212b6ddd485cc3f74c7839cd Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 17:28:29 +0000 Subject: [PATCH 040/105] Check for value override Signed-off-by: snipe --- resources/views/modals/user.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/modals/user.blade.php b/resources/views/modals/user.blade.php index 5672119fa1..04d0c82357 100644 --- a/resources/views/modals/user.blade.php +++ b/resources/views/modals/user.blade.php @@ -42,10 +42,10 @@ @include ('partials.forms.edit.location-profile-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id'])
- @include('partials.forms.edit.name-first', [ 'required' => 'true', 'class' => 'col-md-8 col-xs-12-pull', 'style' => 'width:67%;']) + @include('partials.forms.edit.name-first', ['value' => '', 'required' => 'true', 'class' => 'col-md-8 col-xs-12-pull', 'style' => 'width:67%;'])
- @include('partials.forms.edit.name-last', ['required' => 'true', 'class' => 'col-md-8 col-xs-12-pull', 'style' => 'width:67%;']) + @include('partials.forms.edit.name-last', ['value' => '', 'required' => 'true', 'class' => 'col-md-8 col-xs-12-pull', 'style' => 'width:67%;'])
@include('partials.forms.edit.email') From aa8e9f61d28f417f03e0d02758cdd77301c5f741 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 17:28:44 +0000 Subject: [PATCH 041/105] Check for value override Signed-off-by: snipe --- resources/views/partials/forms/edit/name-first.blade.php | 3 ++- resources/views/partials/forms/edit/name-last.blade.php | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/views/partials/forms/edit/name-first.blade.php b/resources/views/partials/forms/edit/name-first.blade.php index a73b7cbdb5..bc95b4edd1 100644 --- a/resources/views/partials/forms/edit/name-first.blade.php +++ b/resources/views/partials/forms/edit/name-first.blade.php @@ -3,11 +3,12 @@ $class = $class ?? 'col-md-6'; $style = $style ?? ''; $required = $required ?? ''; + $value = $value ?? $user->first_name; @endphp
- + {!! $errors->first('first_name', '') !!}
diff --git a/resources/views/partials/forms/edit/name-last.blade.php b/resources/views/partials/forms/edit/name-last.blade.php index 127454d78c..354142a78a 100644 --- a/resources/views/partials/forms/edit/name-last.blade.php +++ b/resources/views/partials/forms/edit/name-last.blade.php @@ -2,12 +2,13 @@ @php $class = $class ?? 'col-md-6'; $style = $style ?? ''; - $required = $required ?? ''; + $required = $required ?? ''; + $value = $value ?? $user->last_name; @endphp
- + {!! $errors->first('last_name', '') !!}
From c1601b9a8c8a4a80fd7cd30015e7f62822b39b3d Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 17:28:50 +0000 Subject: [PATCH 042/105] Updated tests Signed-off-by: snipe --- tests/Feature/Modals/Ui/ShowModalsTest.php | 8 +++++++- tests/Feature/Users/Ui/CreateUserTest.php | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/Feature/Modals/Ui/ShowModalsTest.php b/tests/Feature/Modals/Ui/ShowModalsTest.php index 308460999f..6da781d319 100644 --- a/tests/Feature/Modals/Ui/ShowModalsTest.php +++ b/tests/Feature/Modals/Ui/ShowModalsTest.php @@ -9,9 +9,15 @@ class ShowModalsTest extends TestCase { public function testUserModalRenders() { - $this->actingAs(User::factory()->createUsers()->create()) + $admin = User::factory()->createUsers()->create(); + $response = $this->actingAs($admin) ->get('modals/user') ->assertOk(); + + $response->assertStatus(200); + $response->assertDontSee($admin->first_name); + $response->assertDontSee($admin->last_name); + $response->assertDontSee($admin->email); } public function testDepartmentModalRenders() diff --git a/tests/Feature/Users/Ui/CreateUserTest.php b/tests/Feature/Users/Ui/CreateUserTest.php index 63d27d3d1c..456190ee3e 100644 --- a/tests/Feature/Users/Ui/CreateUserTest.php +++ b/tests/Feature/Users/Ui/CreateUserTest.php @@ -9,8 +9,12 @@ class CreateUserTest extends TestCase { public function testPageRenders() { - $this->actingAs(User::factory()->superuser()->create()) + $admin = User::factory()->createUsers()->create(); + $response = $this->actingAs(User::factory()->superuser()->create()) ->get(route('users.create')) ->assertOk(); + $response->assertDontSee($admin->first_name); + $response->assertDontSee($admin->last_name); + $response->assertDontSee($admin->email); } } From dfacd876d5f960e12349bdabb86f06b843fa9f3e Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 20:13:15 +0000 Subject: [PATCH 043/105] Added remaining/min_amt to transformers Signed-off-by: snipe --- app/Http/Transformers/AccessoriesTransformer.php | 6 ++++-- app/Http/Transformers/AssetModelsTransformer.php | 3 ++- app/Http/Transformers/LicensesTransformer.php | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/Http/Transformers/AccessoriesTransformer.php b/app/Http/Transformers/AccessoriesTransformer.php index 58858cf897..bc3fe1172d 100644 --- a/app/Http/Transformers/AccessoriesTransformer.php +++ b/app/Http/Transformers/AccessoriesTransformer.php @@ -37,8 +37,10 @@ class AccessoriesTransformer 'purchase_date' => ($accessory->purchase_date) ? Helper::getFormattedDateObject($accessory->purchase_date, 'date') : null, 'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost), 'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null, - 'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null, - 'remaining_qty' => (int) ($accessory->qty - $accessory->checkouts_count), + 'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null, // Legacy - should phase out - replaced by below, for the bootstrap table formatter + 'min_amt' => ($accessory->min_amt) ? (int) $accessory->min_amt : null, + 'remaining_qty' => (int) ($accessory->qty - $accessory->checkouts_count), // Legacy - should phase out - replaced by below, for the bootstrap table formatter + 'remaining' => (int) ($accessory->qty - $accessory->checkouts_count), 'checkouts_count' => $accessory->checkouts_count, 'created_by' => ($accessory->adminuser) ? [ 'id' => (int) $accessory->adminuser->id, diff --git a/app/Http/Transformers/AssetModelsTransformer.php b/app/Http/Transformers/AssetModelsTransformer.php index d95120dbe2..d1b7900031 100644 --- a/app/Http/Transformers/AssetModelsTransformer.php +++ b/app/Http/Transformers/AssetModelsTransformer.php @@ -46,8 +46,9 @@ class AssetModelsTransformer 'name'=> e($assetmodel->manufacturer->name), ] : null, 'image' => ($assetmodel->image != '') ? Storage::disk('public')->url('models/'.e($assetmodel->image)) : null, - 'model_number' => e($assetmodel->model_number), + 'model_number' => ($assetmodel->model_number ? e($assetmodel->model_number): null), 'min_amt' => ($assetmodel->min_amt) ? (int) $assetmodel->min_amt : null, + 'remaining' => (int) ($assetmodel->assets_count - $assetmodel->min_amt), 'depreciation' => ($assetmodel->depreciation) ? [ 'id' => (int) $assetmodel->depreciation->id, 'name'=> e($assetmodel->depreciation->name), diff --git a/app/Http/Transformers/LicensesTransformer.php b/app/Http/Transformers/LicensesTransformer.php index 673ac06b3d..b63a5725a4 100644 --- a/app/Http/Transformers/LicensesTransformer.php +++ b/app/Http/Transformers/LicensesTransformer.php @@ -38,6 +38,7 @@ class LicensesTransformer 'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'), 'seats' => (int) $license->seats, 'free_seats_count' => (int) $license->free_seats_count, + 'remaining' => (int) $license->free_seats_count, 'min_amt' => ($license->min_amt) ? (int) ($license->min_amt) : null, 'license_name' => ($license->license_name) ? e($license->license_name) : null, 'license_email' => ($license->license_email) ? e($license->license_email) : null, From bf885971329319f41e2a05ec6940877f41a77ed3 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 20:14:23 +0000 Subject: [PATCH 044/105] Added qtySumFormatter and made seats formatter more generic Signed-off-by: snipe --- .../views/partials/bootstrap-table.blade.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index 8fd4891e1f..d5d1a3f25b 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -664,8 +664,8 @@ function minAmtFormatter(row, value) { if ((row) && (row!=undefined)) { - if (value.free_seats_count <= value.min_amt) { - return '' + value.min_amt + ''; + if (value.remaining <= value.min_amt) { + return ' ' + value.min_amt + ''; } return value.min_amt } @@ -906,6 +906,19 @@ return parseFloat(decimalfixed); } + + function qtySumFormatter(data) { + var currentField = this.field; + var total = 0; + var fieldname = this.field; + + $.each(data, function() { + var r = this; + total += this[currentField]; + }); + return total; + } + function sumFormatter(data) { if (Array.isArray(data)) { var field = this.field; From bdb4bd73d2e5f04c8f4c4761ca66edba89387928 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 20:18:51 +0000 Subject: [PATCH 045/105] Return an indicator for no value set Signed-off-by: snipe --- resources/views/partials/bootstrap-table.blade.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index d5d1a3f25b..2da7b63b1d 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -664,12 +664,13 @@ function minAmtFormatter(row, value) { if ((row) && (row!=undefined)) { + if (value.remaining <= value.min_amt) { return ' ' + value.min_amt + ''; } return value.min_amt } - + return '--'; } From 7421d089ffff51a363c7b3188c475489c2a2d4f7 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 20:25:00 +0000 Subject: [PATCH 046/105] Added formatter and centering in presenters Signed-off-by: snipe --- app/Presenters/AccessoryPresenter.php | 17 +++++++++------ app/Presenters/AssetModelPresenter.php | 16 ++++++++++++++ app/Presenters/ComponentPresenter.php | 18 ++++++++++------ app/Presenters/ConsumablePresenter.php | 17 +++++++++------ app/Presenters/LicensePresenter.php | 30 +++++++++++++++----------- 5 files changed, 67 insertions(+), 31 deletions(-) diff --git a/app/Presenters/AccessoryPresenter.php b/app/Presenters/AccessoryPresenter.php index 532441e5b6..5e79cb4a20 100644 --- a/app/Presenters/AccessoryPresenter.php +++ b/app/Presenters/AccessoryPresenter.php @@ -78,28 +78,33 @@ class AccessoryPresenter extends Presenter 'sortable' => true, 'title' => trans('general.location'), 'formatter' => 'locationsLinkObjFormatter', + ], [ + 'field' => 'min_amt', + 'searchable' => false, + 'sortable' => true, + 'title' => trans('general.min_amt'), + 'formatter' => 'minAmtFormatter', + 'class' => 'text-right', ], [ 'field' => 'qty', 'searchable' => false, 'sortable' => true, 'title' => trans('admin/accessories/general.total'), + 'footerFormatter' => 'qtySumFormatter', ], [ - 'field' => 'remaining_qty', + 'field' => 'remaining', 'searchable' => false, 'sortable' => false, 'visible' => false, 'title' => trans('admin/accessories/general.remaining'), + 'footerFormatter' => 'qtySumFormatter', ],[ 'field' => 'checkouts_count', 'searchable' => false, 'sortable' => true, 'visible' => true, 'title' => trans('general.checked_out'), - ], [ - 'field' => 'min_qty', - 'searchable' => false, - 'sortable' => true, - 'title' => trans('general.min_amt'), + 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'purchase_date', 'searchable' => true, diff --git a/app/Presenters/AssetModelPresenter.php b/app/Presenters/AssetModelPresenter.php index e3c39149d5..5eaf7dc448 100644 --- a/app/Presenters/AssetModelPresenter.php +++ b/app/Presenters/AssetModelPresenter.php @@ -74,7 +74,10 @@ class AssetModelPresenter extends Presenter 'switchable' => true, 'title' => trans('mail.min_QTY'), 'visible' => true, + 'formatter' => 'minAmtFormatter', + 'class' => 'text-right', ], + [ 'field' => 'assets_count', 'searchable' => false, @@ -82,6 +85,19 @@ class AssetModelPresenter extends Presenter 'switchable' => true, 'title' => trans('admin/models/table.numassets'), 'visible' => true, + 'class' => 'text-right', + 'footerFormatter' => 'qtySumFormatter', + ], + + [ + 'field' => 'remaining', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('general.remaining'), + 'visible' => true, + 'class' => 'text-right', + 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'depreciation', diff --git a/app/Presenters/ComponentPresenter.php b/app/Presenters/ComponentPresenter.php index 39a177592d..3bdc7728f9 100644 --- a/app/Presenters/ComponentPresenter.php +++ b/app/Presenters/ComponentPresenter.php @@ -79,24 +79,30 @@ class ComponentPresenter extends Presenter 'title' => trans('general.manufacturer'), 'visible' => false, 'formatter' => 'manufacturersLinkObjFormatter', + ], [ + 'field' => 'min_amt', + 'searchable' => false, + 'sortable' => true, + 'title' => trans('general.min_amt'), + 'visible' => true, + 'class' => 'text-right', + 'formatter' => 'minAmtFormatter', ], [ 'field' => 'qty', 'searchable' => false, 'sortable' => true, 'title' => trans('admin/components/general.total'), 'visible' => true, + 'class' => 'text-right', + 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'remaining', 'searchable' => false, 'sortable' => false, 'title' => trans('admin/components/general.remaining'), 'visible' => true, - ], [ - 'field' => 'min_amt', - 'searchable' => false, - 'sortable' => false, - 'title' => trans('general.min_amt'), - 'visible' => true, + 'class' => 'text-right', + 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'location', 'searchable' => true, diff --git a/app/Presenters/ConsumablePresenter.php b/app/Presenters/ConsumablePresenter.php index cab8bed8bb..db982cac6f 100644 --- a/app/Presenters/ConsumablePresenter.php +++ b/app/Presenters/ConsumablePresenter.php @@ -72,25 +72,30 @@ class ConsumablePresenter extends Presenter 'searchable' => true, 'sortable' => true, 'title' => trans('admin/consumables/general.item_no'), + ], [ + 'field' => 'min_amt', + 'searchable' => false, + 'sortable' => true, + 'title' => trans('general.min_amt'), + 'visible' => true, + 'formatter' => 'minAmtFormatter', + 'class' => 'text-right', ], [ 'field' => 'qty', 'searchable' => false, 'sortable' => true, 'title' => trans('admin/components/general.total'), 'visible' => true, + 'class' => 'text-right', + 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'remaining', 'searchable' => false, 'sortable' => true, 'title' => trans('admin/components/general.remaining'), 'visible' => true, + 'footerFormatter' => 'qtySumFormatter', ], [ - 'field' => 'min_amt', - 'searchable' => false, - 'sortable' => false, - 'title' => trans('general.min_amt'), - 'visible' => true, - ], [ 'field' => 'location', 'searchable' => true, 'sortable' => true, diff --git a/app/Presenters/LicensePresenter.php b/app/Presenters/LicensePresenter.php index 4256c2c686..a54431a87e 100644 --- a/app/Presenters/LicensePresenter.php +++ b/app/Presenters/LicensePresenter.php @@ -80,24 +80,28 @@ class LicensePresenter extends Presenter 'sortable' => true, 'title' => trans('general.manufacturer'), 'formatter' => 'manufacturersLinkObjFormatter', - ], [ - 'field' => 'seats', - 'searchable' => false, - 'sortable' => true, - 'title' => trans('admin/accessories/general.total'), - ], [ - 'field' => 'free_seats_count', - 'searchable' => false, - 'sortable' => true, - 'title' => trans('admin/accessories/general.remaining'), - ], - [ + ], [ 'field' => 'min_amt', 'searchable' => false, 'sortable' => true, 'title' => trans('mail.min_QTY'), 'formatter' => 'minAmtFormatter', - ],[ + 'class' => 'text-right', + ], [ + 'field' => 'seats', + 'searchable' => false, + 'sortable' => true, + 'title' => trans('admin/accessories/general.total'), + 'class' => 'text-right', + 'footerFormatter' => 'qtySumFormatter', + ], [ + 'field' => 'free_seats_count', + 'searchable' => false, + 'sortable' => true, + 'title' => trans('admin/accessories/general.remaining'), + 'class' => 'text-right', + 'footerFormatter' => 'qtySumFormatter', + ], [ 'field' => 'purchase_date', 'searchable' => true, 'sortable' => true, From 6cf7df22cd61c6a4167783cc76787316c12ea298 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Wed, 12 Mar 2025 13:28:34 -0700 Subject: [PATCH 047/105] Fix eol rate on asset show page --- resources/views/hardware/view.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index a59b0534b2..2bbc0b07c1 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -957,7 +957,7 @@
- {{ Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date) }} + {{ (int) Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date, true) }} {{ trans('admin/hardware/form.months') }}
From 1122cd85672b954c01a7fd9aff445b2dc5faec8e Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Wed, 12 Mar 2025 13:33:15 -0700 Subject: [PATCH 048/105] Fix eol rate in asset transformer --- app/Http/Transformers/AssetsTransformer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php index 378fba2873..b657984546 100644 --- a/app/Http/Transformers/AssetsTransformer.php +++ b/app/Http/Transformers/AssetsTransformer.php @@ -42,7 +42,7 @@ class AssetsTransformer 'requestable' => ($asset->requestable ? true : false), 'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null, - 'eol' => (($asset->asset_eol_date != '') && ($asset->purchase_date != '')) ? Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date).' months' : null, + 'eol' => (($asset->asset_eol_date != '') && ($asset->purchase_date != '')) ? (int) Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date, true) . ' months' : null, 'asset_eol_date' => ($asset->asset_eol_date != '') ? Helper::getFormattedDateObject($asset->asset_eol_date, 'date') : null, 'status_label' => ($asset->assetstatus) ? [ 'id' => (int) $asset->assetstatus->id, From 824271078e6557087ca26a686dda348f724a3be5 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Wed, 12 Mar 2025 13:55:12 -0700 Subject: [PATCH 049/105] Fix eol rate in AssetsController --- app/Http/Controllers/Assets/AssetsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index 6bd530a0e5..9578acea63 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -318,7 +318,7 @@ class AssetsController extends Controller $asset->eol_explicit = false; } elseif ($request->filled('asset_eol_date')) { $asset->asset_eol_date = $request->input('asset_eol_date', null); - $months = Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date); + $months = (int) Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date, true); if($asset->model->eol) { if($months != $asset->model->eol > 0) { $asset->eol_explicit = true; From 083cf7830518c3f25ccd437d3adcdda4ec7c94e9 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Mar 2025 21:17:41 +0000 Subject: [PATCH 050/105] Added footer style for nicer number cell padding Signed-off-by: snipe --- app/Presenters/AccessoryPresenter.php | 7 +- app/Presenters/AssetModelPresenter.php | 6 +- app/Presenters/ComponentPresenter.php | 6 +- app/Presenters/ConsumablePresenter.php | 7 +- app/Presenters/LicensePresenter.php | 6 +- public/css/build/app.css | 8 + public/css/build/overrides.css | 8 + public/css/dist/all.css | 16 + public/js/build/app.js | 1712 ++++++++--------- public/js/dist/all.js | 1712 ++++++++--------- public/mix-manifest.json | 10 +- resources/assets/less/overrides.less | 13 +- resources/views/accessories/index.blade.php | 1 + resources/views/components/index.blade.php | 1 + resources/views/consumables/index.blade.php | 1 + resources/views/licenses/index.blade.php | 1 + resources/views/models/index.blade.php | 1 + .../views/partials/bootstrap-table.blade.php | 32 +- 18 files changed, 1815 insertions(+), 1733 deletions(-) diff --git a/app/Presenters/AccessoryPresenter.php b/app/Presenters/AccessoryPresenter.php index 5e79cb4a20..6d423bed78 100644 --- a/app/Presenters/AccessoryPresenter.php +++ b/app/Presenters/AccessoryPresenter.php @@ -84,13 +84,14 @@ class AccessoryPresenter extends Presenter 'sortable' => true, 'title' => trans('general.min_amt'), 'formatter' => 'minAmtFormatter', - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', ], [ 'field' => 'qty', 'searchable' => false, 'sortable' => true, 'title' => trans('admin/accessories/general.total'), 'footerFormatter' => 'qtySumFormatter', + 'class' => 'text-right text-padding-number-cell', ], [ 'field' => 'remaining', 'searchable' => false, @@ -98,6 +99,7 @@ class AccessoryPresenter extends Presenter 'visible' => false, 'title' => trans('admin/accessories/general.remaining'), 'footerFormatter' => 'qtySumFormatter', + 'class' => 'text-right text-padding-number-cell', ],[ 'field' => 'checkouts_count', 'searchable' => false, @@ -105,6 +107,7 @@ class AccessoryPresenter extends Presenter 'visible' => true, 'title' => trans('general.checked_out'), 'footerFormatter' => 'qtySumFormatter', + 'class' => 'text-right text-padding-number-cell', ], [ 'field' => 'purchase_date', 'searchable' => true, @@ -118,7 +121,7 @@ class AccessoryPresenter extends Presenter 'sortable' => true, 'title' => trans('general.purchase_cost'), 'footerFormatter' => 'sumFormatterQuantity', - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', ], [ 'field' => 'order_number', 'searchable' => true, diff --git a/app/Presenters/AssetModelPresenter.php b/app/Presenters/AssetModelPresenter.php index 5eaf7dc448..2ed019eaea 100644 --- a/app/Presenters/AssetModelPresenter.php +++ b/app/Presenters/AssetModelPresenter.php @@ -75,7 +75,7 @@ class AssetModelPresenter extends Presenter 'title' => trans('mail.min_QTY'), 'visible' => true, 'formatter' => 'minAmtFormatter', - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', ], [ @@ -85,7 +85,7 @@ class AssetModelPresenter extends Presenter 'switchable' => true, 'title' => trans('admin/models/table.numassets'), 'visible' => true, - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', 'footerFormatter' => 'qtySumFormatter', ], @@ -96,7 +96,7 @@ class AssetModelPresenter extends Presenter 'switchable' => true, 'title' => trans('general.remaining'), 'visible' => true, - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', 'footerFormatter' => 'qtySumFormatter', ], [ diff --git a/app/Presenters/ComponentPresenter.php b/app/Presenters/ComponentPresenter.php index 3bdc7728f9..61f7a0ef4b 100644 --- a/app/Presenters/ComponentPresenter.php +++ b/app/Presenters/ComponentPresenter.php @@ -85,7 +85,7 @@ class ComponentPresenter extends Presenter 'sortable' => true, 'title' => trans('general.min_amt'), 'visible' => true, - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', 'formatter' => 'minAmtFormatter', ], [ 'field' => 'qty', @@ -93,7 +93,7 @@ class ComponentPresenter extends Presenter 'sortable' => true, 'title' => trans('admin/components/general.total'), 'visible' => true, - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'remaining', @@ -101,7 +101,7 @@ class ComponentPresenter extends Presenter 'sortable' => false, 'title' => trans('admin/components/general.remaining'), 'visible' => true, - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'location', diff --git a/app/Presenters/ConsumablePresenter.php b/app/Presenters/ConsumablePresenter.php index db982cac6f..fd4cee272c 100644 --- a/app/Presenters/ConsumablePresenter.php +++ b/app/Presenters/ConsumablePresenter.php @@ -79,14 +79,14 @@ class ConsumablePresenter extends Presenter 'title' => trans('general.min_amt'), 'visible' => true, 'formatter' => 'minAmtFormatter', - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', ], [ 'field' => 'qty', 'searchable' => false, 'sortable' => true, 'title' => trans('admin/components/general.total'), 'visible' => true, - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'remaining', @@ -94,6 +94,7 @@ class ConsumablePresenter extends Presenter 'sortable' => true, 'title' => trans('admin/components/general.remaining'), 'visible' => true, + 'class' => 'text-right text-padding-number-cell', 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'location', @@ -128,7 +129,7 @@ class ConsumablePresenter extends Presenter 'title' => trans('general.purchase_cost'), 'visible' => true, 'footerFormatter' => 'sumFormatterQuantity', - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', ], [ 'field' => 'notes', 'searchable' => true, diff --git a/app/Presenters/LicensePresenter.php b/app/Presenters/LicensePresenter.php index a54431a87e..f6ccafa8ac 100644 --- a/app/Presenters/LicensePresenter.php +++ b/app/Presenters/LicensePresenter.php @@ -86,20 +86,20 @@ class LicensePresenter extends Presenter 'sortable' => true, 'title' => trans('mail.min_QTY'), 'formatter' => 'minAmtFormatter', - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', ], [ 'field' => 'seats', 'searchable' => false, 'sortable' => true, 'title' => trans('admin/accessories/general.total'), - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'free_seats_count', 'searchable' => false, 'sortable' => true, 'title' => trans('admin/accessories/general.remaining'), - 'class' => 'text-right', + 'class' => 'text-right text-padding-number-cell', 'footerFormatter' => 'qtySumFormatter', ], [ 'field' => 'purchase_date', diff --git a/public/css/build/app.css b/public/css/build/app.css index 7ea8f083b9..eb8a1db230 100644 --- a/public/css/build/app.css +++ b/public/css/build/app.css @@ -1418,4 +1418,12 @@ input[type="radio"]:checked::before { .bootstrap-table .fixed-table-container .table tbody tr .card-view { display: table-row !important; } +td.text-right.text-padding-number-cell { + padding-right: 30px !important; + white-space: nowrap; +} +th.text-right.text-padding-number-footer-cell { + padding-right: 20px !important; + white-space: nowrap; +} diff --git a/public/css/build/overrides.css b/public/css/build/overrides.css index 1074758f2b..edf9a25d82 100644 --- a/public/css/build/overrides.css +++ b/public/css/build/overrides.css @@ -1049,4 +1049,12 @@ input[type="radio"]:checked::before { .bootstrap-table .fixed-table-container .table tbody tr .card-view { display: table-row !important; } +td.text-right.text-padding-number-cell { + padding-right: 30px !important; + white-space: nowrap; +} +th.text-right.text-padding-number-footer-cell { + padding-right: 20px !important; + white-space: nowrap; +} diff --git a/public/css/dist/all.css b/public/css/dist/all.css index 848587ae70..7f8f7a5717 100644 --- a/public/css/dist/all.css +++ b/public/css/dist/all.css @@ -22753,6 +22753,14 @@ input[type="radio"]:checked::before { .bootstrap-table .fixed-table-container .table tbody tr .card-view { display: table-row !important; } +td.text-right.text-padding-number-cell { + padding-right: 30px !important; + white-space: nowrap; +} +th.text-right.text-padding-number-footer-cell { + padding-right: 20px !important; + white-space: nowrap; +} .select2-container { @@ -24288,4 +24296,12 @@ input[type="radio"]:checked::before { .bootstrap-table .fixed-table-container .table tbody tr .card-view { display: table-row !important; } +td.text-right.text-padding-number-cell { + padding-right: 30px !important; + white-space: nowrap; +} +th.text-right.text-padding-number-footer-cell { + padding-right: 20px !important; + white-space: nowrap; +} diff --git a/public/js/build/app.js b/public/js/build/app.js index 9bd78d354e..1f061aabd1 100644 --- a/public/js/build/app.js +++ b/public/js/build/app.js @@ -1,799 +1,16 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ "./resources/assets/js/bootstrap.js": -/*!******************************************!*\ - !*** ./resources/assets/js/bootstrap.js ***! - \******************************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { +/***/ "./node_modules/admin-lte/build/less/AdminLTE.less": +/*!*********************************************************!*\ + !*** ./node_modules/admin-lte/build/less/AdminLTE.less ***! + \*********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -window._ = __webpack_require__(/*! lodash */ "./node_modules/lodash/lodash.js"); +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin -/** - * We'll load jQuery and the Bootstrap jQuery plugin which provides support - * for JavaScript based Bootstrap features such as modals and tabs. This - * code may be modified to fit the specific needs of your application. - */ -//window.$ = window.jQuery = require('jquery'); - -/** - * jQuery UI is loaded here and then the tooltip is assigned another funtion name - * This resolves the issue of jquery-ui & bootstrap tooltip conflict - */ -__webpack_require__(/*! jquery-ui */ "./node_modules/jquery-ui/ui/widget.js"); -jQuery.fn.uitooltip = jQuery.fn.tooltip; - -/** - * Load boostrap - */ -__webpack_require__(/*! bootstrap-less */ "./node_modules/bootstrap-less/js/bootstrap.js"); - -// require('admin-lte'); - -// require('chart.js'); - -// require('jquery-form-validator'); //says something about dependency -/** - * Echo exposes an expressive API for subscribing to channels and listening - * for events that are broadcast by Laravel. Echo and event broadcasting - * allows your team to easily build robust real-time web applications. - */ - -// import Echo from "laravel-echo" - -// window.Echo = new Echo({ -// broadcaster: 'pusher', -// key: 'your-pusher-key' -// }); - -/***/ }), - -/***/ "./resources/assets/js/snipeit.js": -/*!****************************************!*\ - !*** ./resources/assets/js/snipeit.js ***! - \****************************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -// var jQuery = require('jquery'); -// window.jQuery = jQuery -// window.$ = jQuery - -__webpack_require__(/*! ./bootstrap */ "./resources/assets/js/bootstrap.js"); - -/** - * Module containing core application logic. - * @param {jQuery} $ Insulated jQuery object - * @param {JSON} settings Insulated `window.snipeit.settings` object. - * @return {IIFE} Immediately invoked. Returns self. - */ - -lineOptions = { - legend: { - position: "bottom" - }, - scales: { - yAxes: [{ - ticks: { - fontColor: "rgba(0,0,0,0.5)", - fontStyle: "bold", - beginAtZero: true, - maxTicksLimit: 5, - padding: 20 - }, - gridLines: { - drawTicks: false, - display: false - } - }], - xAxes: [{ - gridLines: { - zeroLineColor: "transparent" - }, - ticks: { - padding: 20, - fontColor: "rgba(0,0,0,0.5)", - fontStyle: "bold" - } - }] - } -}; -pieOptions = { - //Boolean - Whether we should show a stroke on each segment - segmentShowStroke: true, - //String - The colour of each segment stroke - segmentStrokeColor: "#fff", - //Number - The width of each segment stroke - segmentStrokeWidth: 1, - //Number - The percentage of the chart that we cut out of the middle - percentageInnerCutout: 50, - // This is 0 for Pie charts - //Number - Amount of animation steps - animationSteps: 100, - //String - Animation easing effect - animationEasing: "easeOutBounce", - //Boolean - Whether we animate the rotation of the Doughnut - animateRotate: true, - //Boolean - Whether we animate scaling the Doughnut from the centre - animateScale: false, - //Boolean - whether to make the chart responsive to window resizing - responsive: true, - // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container - maintainAspectRatio: false, - //String - A legend template - legendTemplate: "
    -legend\"><% for (var i=0; i
  • " + "" + "<%if(segments[i].label){%><%=segments[i].label%><%}%> foo
  • <%}%>
", - //String - A tooltip template - tooltipTemplate: "<%=value %> <%=label%> " -}; - -//----------------- -//- END PIE CHART - -//----------------- - -var baseUrl = $('meta[name="baseUrl"]').attr('content'); -$(function () { - var $el = $('table'); - - // confirm restore modal - - $el.on('click', '.restore-asset', function (evnt) { - var $context = $(this); - var $restoreConfirmModal = $('#restoreConfirmModal'); - var href = $context.attr('href'); - var message = $context.attr('data-content'); - var title = $context.attr('data-title'); - $('#confirmModalLabel').text(title); - $restoreConfirmModal.find('.modal-body').text(message); - $('#restoreForm').attr('action', href); - $restoreConfirmModal.modal({ - show: true - }); - return false; - }); - - // confirm delete modal - - $el.on('click', '.delete-asset', function (evnt) { - var $context = $(this); - var $dataConfirmModal = $('#dataConfirmModal'); - var href = $context.attr('href'); - var message = $context.attr('data-content'); - var title = $context.attr('data-title'); - $('#myModalLabel').text(title); - $dataConfirmModal.find('.modal-body').text(message); - $('#deleteForm').attr('action', href); - $dataConfirmModal.modal({ - show: true - }); - return false; - }); - - /* - * Slideout help menu - */ - $('.slideout-menu-toggle').on('click', function (event) { - event.preventDefault(); - // create menu variables - var slideoutMenu = $('.slideout-menu'); - var slideoutMenuWidth = $('.slideout-menu').width(); - - // toggle open class - slideoutMenu.toggleClass("open"); - - // slide menu - if (slideoutMenu.hasClass("open")) { - slideoutMenu.show(); - slideoutMenu.animate({ - right: "0px" - }); - } else { - slideoutMenu.animate({ - right: -slideoutMenuWidth - }, "-350px"); - slideoutMenu.fadeOut(); - } - }); - - /* - * Select2 - */ - - $('select.select2:not(".select2-hidden-accessible")').each(function (i, obj) { - { - $(obj).select2(); - } - }); - - // $('.datepicker').datepicker(); - // var datepicker = $.fn.datepicker.noConflict(); // return $.fn.datepicker to previously assigned value - // $.fn.bootstrapDP = datepicker; - // $('.datepicker').datepicker(); - - // Crazy select2 rich dropdowns with images! - $('.js-data-ajax').each(function (i, item) { - var link = $(item); - var endpoint = link.data("endpoint"); - var select = link.data("select"); - link.select2({ - /** - * Adds an empty placeholder, allowing every select2 instance to be cleared. - * This placeholder can be overridden with the "data-placeholder" attribute. - */ - placeholder: '', - allowClear: true, - language: $('meta[name="language"]').attr('content'), - dir: $('meta[name="language-direction"]').attr('content'), - ajax: { - // the baseUrl includes a trailing slash - url: baseUrl + 'api/v1/' + endpoint + '/selectlist', - dataType: 'json', - delay: 250, - headers: { - "X-Requested-With": 'XMLHttpRequest', - "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') - }, - data: function data(params) { - var data = { - search: params.term, - page: params.page || 1, - assetStatusType: link.data("asset-status-type") - }; - return data; - }, - /* processResults: function (data, params) { - params.page = params.page || 1; - var answer = { - results: data.items, - pagination: { - more: data.pagination.more - } - }; - return answer; - }, */ - cache: true - }, - //escapeMarkup: function (markup) { return markup; }, // let our custom formatter work - templateResult: formatDatalistSafe - //templateSelection: formatDataSelection - }); - }); - function getSelect2Value(element) { - // if the passed object is not a jquery object, assuming 'element' is a selector - if (!(element instanceof jQuery)) element = $(element); - var select = element.data("select2"); - - // There's two different locations where the select2-generated input element can be. - searchElement = select.dropdown.$search || select.$container.find(".select2-search__field"); - var value = searchElement.val(); - return value; - } - $(".select2-hidden-accessible").on('select2:selecting', function (e) { - var data = e.params.args.data; - var isMouseUp = false; - var element = $(this); - var value = getSelect2Value(element); - if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup"; - - // if selected item does not match typed text, do not allow it to pass - force close for ajax. - if (!isMouseUp) { - if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) < 0) { - e.preventDefault(); - element.select2('close'); - - // if it does match, we set a flag in the event (which gets passed to subsequent events), telling it not to worry about the ajax - } else if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) > -1) { - e.params.args.noForceAjax = true; - } - } - }); - $(".select2-hidden-accessible").on('select2:closing', function (e) { - var element = $(this); - var value = getSelect2Value(element); - var noForceAjax = false; - var isMouseUp = false; - if (e.params.args.originalSelect2Event) noForceAjax = e.params.args.originalSelect2Event.noForceAjax; - if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup"; - if (value && !noForceAjax && !isMouseUp) { - var endpoint = element.data("endpoint"); - var assetStatusType = element.data("asset-status-type"); - $.ajax({ - url: baseUrl + 'api/v1/' + endpoint + '/selectlist?search=' + value + '&page=1' + (assetStatusType ? '&assetStatusType=' + assetStatusType : ''), - dataType: 'json', - headers: { - "X-Requested-With": 'XMLHttpRequest', - "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') - } - }).done(function (response) { - var currentlySelected = element.select2('data').map(function (x) { - return +x.id; - }).filter(function (x) { - return x !== 0; - }); - - // makes sure we're not selecting the same thing twice for multiples - var filteredResponse = response.results.filter(function (item) { - return currentlySelected.indexOf(+item.id) < 0; - }); - var first = currentlySelected.length > 0 ? filteredResponse[0] : response.results[0]; - if (first && first.id) { - first.selected = true; - if ($("option[value='" + first.id + "']", element).length < 1) { - var option = new Option(first.text, first.id, true, true); - element.append(option); - } else { - var isMultiple = element.attr("multiple") == "multiple"; - element.val(isMultiple ? element.val().concat(first.id) : element.val(first.id)); - } - element.trigger('change'); - element.trigger({ - type: 'select2:select', - params: { - data: first - } - }); - } - }); - } - }); - function formatDatalist(datalist) { - var loading_markup = ' Loading...'; - if (datalist.loading) { - return loading_markup; - } - var markup = '
'; - markup += '
'; - if (datalist.image) { - markup += "
" + datalist.text + "
"; - } else { - markup += '
'; - } - markup += "
" + datalist.text + "
"; - markup += "
"; - return markup; - } - function formatDatalistSafe(datalist) { - // console.warn("What in the hell is going on with Select2?!?!!?!?"); - // console.warn($.select2); - if (datalist.loading) { - return $(' Loading...'); - } - var root_div = $("
"); - var left_pull = $("
"); - if (datalist.image) { - var inner_div = $("
"); - /****************************************************************** - * - * We are specifically chosing empty alt-text below, because this - * image conveys no additional information, relative to the text - * that will *always* be there in any select2 list that is in use - * in Snipe-IT. If that changes, we would probably want to change - * some signatures of some functions, but right now, we don't want - * screen readers to say "HP SuperJet 5000, .... picture of HP - * SuperJet 5000..." and so on, for every single row in a list of - * assets or models or whatever. - * - *******************************************************************/ - var img = $(""); - // console.warn("Img is: "); - // console.dir(img); - // console.warn("Strigularly, that's: "); - // console.log(img); - img.attr("src", datalist.image); - inner_div.append(img); - } else { - var inner_div = $("
"); - } - left_pull.append(inner_div); - root_div.append(left_pull); - var name_div = $("
"); - name_div.text(datalist.text); - root_div.append(name_div); - var safe_html = root_div.get(0).outerHTML; - var old_html = formatDatalist(datalist); - if (safe_html != old_html) { - //console.log("HTML MISMATCH: "); - //console.log("FormatDatalistSafe: "); - // console.dir(root_div.get(0)); - //console.log(safe_html); - //console.log("FormatDataList: "); - //console.log(old_html); - } - return root_div; - } - function formatDataSelection(datalist) { - // This a heinous workaround for a known bug in Select2. - // Without this, the rich selectlists are vulnerable to XSS. - // Many thanks to @uberbrady for this fix. It ain't pretty, - // but it resolves the issue until Select2 addresses it on their end. - // - // Bug was reported in 2016 :{ - // https://github.com/select2/select2/issues/4587 - - return datalist.text.replace(/>/g, '>').replace(/Click me - $('a[data-toggle="tab"]').click(function (e) { - var href = $(this).attr("href"); - history.pushState(null, null, href); - e.preventDefault(); - $('a[href="' + $(this).attr('href') + '"]').tab('show'); - }); - - // ------------------------------------------------ - // End Deep Linking for Bootstrap tabs - // ------------------------------------------------ - - // Image preview - function readURL(input, $preview) { - if (input.files && input.files[0]) { - var reader = new FileReader(); - reader.onload = function (e) { - $preview.attr('src', e.target.result); - }; - reader.readAsDataURL(input.files[0]); - } - } - function formatBytes(bytes) { - if (bytes < 1024) return bytes + " Bytes";else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + " KB";else if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + " MB";else return (bytes / 1073741824).toFixed(2) + " GB"; - } - - // File size validation - $('.js-uploadFile').bind('change', function () { - var $this = $(this); - var id = '#' + $this.attr('id'); - var status = id + '-status'; - var $status = $(status); - var delete_id = $(id + '-deleteCheckbox'); - var preview_container = $(id + '-previewContainer'); - $status.removeClass('text-success').removeClass('text-danger'); - $(status + ' .goodfile').remove(); - $(status + ' .badfile').remove(); - $(status + ' .previewSize').hide(); - preview_container.hide(); - $(id + '-info').html(''); - var max_size = $this.data('maxsize'); - var total_size = 0; - for (var i = 0; i < this.files.length; i++) { - total_size += this.files[i].size; - $(id + '-info').append('' + htmlEntities(this.files[i].name) + ' (' + formatBytes(this.files[i].size) + ') '); - } - if (total_size > max_size) { - $status.addClass('text-danger').removeClass('help-block').prepend(' ').append(' Upload is ' + formatBytes(total_size) + '.'); - } else { - $status.addClass('text-success').removeClass('help-block').prepend(' '); - var $preview = $(id + '-imagePreview'); - readURL(this, $preview); - $preview.fadeIn(); - preview_container.fadeIn(); - delete_id.hide(); - } - }); -}); -function htmlEntities(str) { - return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} - -/** - * Toggle disabled - */ -(function ($) { - $.fn.toggleDisabled = function (callback) { - return this.each(function () { - var disabled, - $this = $(this); - if ($this.attr('disabled')) { - $this.removeAttr('disabled'); - disabled = false; - } else { - $this.attr('disabled', 'disabled'); - disabled = true; - } - if (callback && typeof callback === 'function') { - callback(this, disabled); - } - }); - }; -})(jQuery); - -/** - * Universal Livewire Select2 integration - * - * How to use: - * - * 1. Set the class of your select2 elements to 'livewire-select2'). - * 2. Name your element to match a property in your Livewire component - * 3. Add an attribute called 'data-livewire-component' that points to $this->getId() (via `{{ }}` if you're in a blade, - * or just $this->getId() if not). - */ -document.addEventListener('livewire:init', function () { - $('.livewire-select2').select2(); - $(document).on('select2:select', '.livewire-select2', function (event) { - var target = $(event.target); - if (!event.target.name || !target.data('livewire-component')) { - console.error("You need to set both name (which should match a Livewire property) and data-livewire-component on your Livewire-ed select2 elements!"); - console.error("For data-livewire-component, you probably want to use $this->getId() or {{ $this->getId() }}, as appropriate"); - return false; - } - Livewire.find(target.data('livewire-component')).set(event.target.name, this.options[this.selectedIndex].value); - }); - Livewire.hook('request', function (_ref) { - var succeed = _ref.succeed; - succeed(function () { - queueMicrotask(function () { - $('.livewire-select2').select2(); - }); - }); - }); -}); - -/***/ }), - -/***/ "./resources/assets/js/snipeit_modals.js": -/*!***********************************************!*\ - !*** ./resources/assets/js/snipeit_modals.js ***! - \***********************************************/ -/***/ (() => { - -/* - * - * Snipe-IT Universal Modal support - * - * Enables modal dialogs to create sub-resources throughout Snipe-IT - * - */ - -/* -HOW TO USE - Create a Button looking like this: - New - If you don't have access to Blade commands (like {{ and }}, etc), you can hard-code a URL as the 'href' - data-toggle="modal" - required for Bootstrap Modals -data-target="#createModal" - fixed ID for the modal, do not change -data-select="assigned_to" - What is the *ID* of the select-dropdown that you're going to be adding to, if the modal-create was a success? Be on the lookout for duplicate ID's, it will confuse this library! -class="btn btn-sm btn-primary" - makes it look button-ey, feel free to change :) - -If you want to pass additional variables to the modal (In the Category Create one, for example, you can pass category_id), you can encode them as URL variables in the href - -*/ - -$(function () { - var baseUrl = $('meta[name="baseUrl"]').attr('content'); - //handle modal-add-interstitial calls - var model, select, refreshSelector; - if ($('#createModal').length == 0) { - $('body').append(''); - } - $('#createModal').on("show.bs.modal", function (event) { - var link = $(event.relatedTarget); - model = link.data("dependency"); - select = link.data("select"); - refreshSelector = link.data("refresh"); - $('#createModal').load(link.attr('href'), function () { - // this sets the focus to be the name field - $('#modal-name').focus(); - - //do we need to re-select2 this, after load? Probably. - $('#createModal').find('select.select2').select2(); - // Initialize the ajaxy select2 with images. - // This is a copy/paste of the code from snipeit.js, would be great to only have this in one place. - - $('.js-data-ajax').each(function (i, item) { - var link = $(item); - var endpoint = link.data("endpoint"); - var select = link.data("select"); - link.select2({ - ajax: { - // the baseUrl includes a trailing slash - url: baseUrl + 'api/v1/' + endpoint + '/selectlist', - //WARNING - we're hoping that's defined on the page somewhere... - dataType: 'json', - delay: 250, - headers: { - "X-Requested-With": 'XMLHttpRequest', - "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') - }, - data: function data(params) { - var data = { - search: params.term, - page: params.page || 1, - assetStatusType: link.data("asset-status-type") - }; - return data; - }, - /*processResults: function (data, params) { - params.page = params.page || 1; - var answer = { - results: data.items, - pagination: { - more: data.pagination.more - } - }; - return answer; - },*/ - cache: true - }, - //escapeMarkup: function (markup) { return markup; }, // let our custom formatter work - templateResult: formatDatalistSafe - //templateSelection: formatDataSelection - }); - }); - }); - }); - $('#createModal').on('click', '#modal-save', function () { - $.ajax({ - type: 'POST', - url: $('.modal-body form').attr('action'), - headers: { - "X-Requested-With": 'XMLHttpRequest', - "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') - }, - data: $('.modal-body form').serialize(), - success: function success(result) { - if (result.status == "error") { - var error_message = ""; - for (var field in result.messages) { - error_message += "
  • Problem(s) with field " + field + ": " + result.messages[field]; - } - $('#modal_error_msg').html(error_message).show(); - return false; - } - var id = result.payload.id; - var name = result.payload.name || result.payload.first_name + " " + result.payload.last_name; - if (!id || !name) { - console.error("Could not find resulting name or ID from modal-create. Name: " + name + ", id: " + id); - return false; - } - $('#createModal').modal('hide'); - $('#createModal').html(""); - var refreshTable = $('#' + refreshSelector); - if (refreshTable.length > 0) { - refreshTable.bootstrapTable('refresh'); - } - - // "select" is the original drop-down menu that someone - // clicked 'add' on to add a new 'thing' - // this code adds the newly created object to that select - var selector = document.getElementById(select); - if (!selector) { - return false; - } - selector.options[selector.length] = new Option(name, id); - selector.selectedIndex = selector.length - 1; - $(selector).trigger("change"); - if (window.fetchCustomFields) { - fetchCustomFields(); - } - }, - error: function error(result) { - msg = result.responseJSON.messages || result.responseJSON.error; - $('#modal_error_msg').html("Server Error: " + msg).show(); - } - }); - }); -}); -function formatDatalistSafe(datalist) { - // console.warn("What in the hell is going on with Select2?!?!!?!?"); - // console.warn($.select2); - if (datalist.loading) { - return $(' Loading...'); - } - var root_div = $("
    "); - var left_pull = $("
    "); - if (datalist.image) { - var inner_div = $("
    "); - /****************************************************************** - * - * We are specifically chosing empty alt-text below, because this - * image conveys no additional information, relative to the text - * that will *always* be there in any select2 list that is in use - * in Snipe-IT. If that changes, we would probably want to change - * some signatures of some functions, but right now, we don't want - * screen readers to say "HP SuperJet 5000, .... picture of HP - * SuperJet 5000..." and so on, for every single row in a list of - * assets or models or whatever. - * - *******************************************************************/ - var img = $(""); - // console.warn("Img is: "); - // console.dir(img); - // console.warn("Strigularly, that's: "); - // console.log(img); - img.attr("src", datalist.image); - inner_div.append(img); - } else { - var inner_div = $("
    "); - } - left_pull.append(inner_div); - root_div.append(left_pull); - var name_div = $("
    "); - name_div.text(datalist.text); - root_div.append(name_div); - var safe_html = root_div.get(0).outerHTML; - var old_html = formatDatalist(datalist); - if (safe_html != old_html) { - // console.log("HTML MISMATCH: "); - // console.log("FormatDatalistSafe: "); - // console.dir(root_div.get(0)); - // console.log(safe_html); - // console.log("FormatDataList: "); - // console.log(old_html); - } - return root_div; -} -function formatDatalist(datalist) { - var loading_markup = ' Loading...'; - if (datalist.loading) { - return loading_markup; - } - var markup = "
    "; - markup += "
    "; - if (datalist.image) { - markup += "
    " + datalist.tex + "
    "; - } else { - markup += "
    "; - } - markup += "
    " + datalist.text + "
    "; - markup += "
    "; - return markup; -} -function formatDataSelection(datalist) { - return datalist.text.replace(/>/g, '>').replace(/ { + +window._ = __webpack_require__(/*! lodash */ "./node_modules/lodash/lodash.js"); + +/** + * We'll load jQuery and the Bootstrap jQuery plugin which provides support + * for JavaScript based Bootstrap features such as modals and tabs. This + * code may be modified to fit the specific needs of your application. + */ +//window.$ = window.jQuery = require('jquery'); + +/** + * jQuery UI is loaded here and then the tooltip is assigned another funtion name + * This resolves the issue of jquery-ui & bootstrap tooltip conflict + */ +__webpack_require__(/*! jquery-ui */ "./node_modules/jquery-ui/ui/widget.js"); +jQuery.fn.uitooltip = jQuery.fn.tooltip; + +/** + * Load boostrap + */ +__webpack_require__(/*! bootstrap-less */ "./node_modules/bootstrap-less/js/bootstrap.js"); + +// require('admin-lte'); + +// require('chart.js'); + +// require('jquery-form-validator'); //says something about dependency +/** + * Echo exposes an expressive API for subscribing to channels and listening + * for events that are broadcast by Laravel. Echo and event broadcasting + * allows your team to easily build robust real-time web applications. + */ + +// import Echo from "laravel-echo" + +// window.Echo = new Echo({ +// broadcaster: 'pusher', +// key: 'your-pusher-key' +// }); + +/***/ }), + +/***/ "./resources/assets/js/snipeit.js": +/*!****************************************!*\ + !*** ./resources/assets/js/snipeit.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +// var jQuery = require('jquery'); +// window.jQuery = jQuery +// window.$ = jQuery + +__webpack_require__(/*! ./bootstrap */ "./resources/assets/js/bootstrap.js"); + +/** + * Module containing core application logic. + * @param {jQuery} $ Insulated jQuery object + * @param {JSON} settings Insulated `window.snipeit.settings` object. + * @return {IIFE} Immediately invoked. Returns self. + */ + +lineOptions = { + legend: { + position: "bottom" + }, + scales: { + yAxes: [{ + ticks: { + fontColor: "rgba(0,0,0,0.5)", + fontStyle: "bold", + beginAtZero: true, + maxTicksLimit: 5, + padding: 20 + }, + gridLines: { + drawTicks: false, + display: false + } + }], + xAxes: [{ + gridLines: { + zeroLineColor: "transparent" + }, + ticks: { + padding: 20, + fontColor: "rgba(0,0,0,0.5)", + fontStyle: "bold" + } + }] + } +}; +pieOptions = { + //Boolean - Whether we should show a stroke on each segment + segmentShowStroke: true, + //String - The colour of each segment stroke + segmentStrokeColor: "#fff", + //Number - The width of each segment stroke + segmentStrokeWidth: 1, + //Number - The percentage of the chart that we cut out of the middle + percentageInnerCutout: 50, + // This is 0 for Pie charts + //Number - Amount of animation steps + animationSteps: 100, + //String - Animation easing effect + animationEasing: "easeOutBounce", + //Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + //Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false, + //Boolean - whether to make the chart responsive to window resizing + responsive: true, + // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container + maintainAspectRatio: false, + //String - A legend template + legendTemplate: "
      -legend\"><% for (var i=0; i
    • " + "" + "<%if(segments[i].label){%><%=segments[i].label%><%}%> foo
    • <%}%>
    ", + //String - A tooltip template + tooltipTemplate: "<%=value %> <%=label%> " +}; + +//----------------- +//- END PIE CHART - +//----------------- + +var baseUrl = $('meta[name="baseUrl"]').attr('content'); +$(function () { + var $el = $('table'); + + // confirm restore modal + + $el.on('click', '.restore-asset', function (evnt) { + var $context = $(this); + var $restoreConfirmModal = $('#restoreConfirmModal'); + var href = $context.attr('href'); + var message = $context.attr('data-content'); + var title = $context.attr('data-title'); + $('#confirmModalLabel').text(title); + $restoreConfirmModal.find('.modal-body').text(message); + $('#restoreForm').attr('action', href); + $restoreConfirmModal.modal({ + show: true + }); + return false; + }); + + // confirm delete modal + + $el.on('click', '.delete-asset', function (evnt) { + var $context = $(this); + var $dataConfirmModal = $('#dataConfirmModal'); + var href = $context.attr('href'); + var message = $context.attr('data-content'); + var title = $context.attr('data-title'); + $('#myModalLabel').text(title); + $dataConfirmModal.find('.modal-body').text(message); + $('#deleteForm').attr('action', href); + $dataConfirmModal.modal({ + show: true + }); + return false; + }); + + /* + * Slideout help menu + */ + $('.slideout-menu-toggle').on('click', function (event) { + event.preventDefault(); + // create menu variables + var slideoutMenu = $('.slideout-menu'); + var slideoutMenuWidth = $('.slideout-menu').width(); + + // toggle open class + slideoutMenu.toggleClass("open"); + + // slide menu + if (slideoutMenu.hasClass("open")) { + slideoutMenu.show(); + slideoutMenu.animate({ + right: "0px" + }); + } else { + slideoutMenu.animate({ + right: -slideoutMenuWidth + }, "-350px"); + slideoutMenu.fadeOut(); + } + }); + + /* + * Select2 + */ + + $('select.select2:not(".select2-hidden-accessible")').each(function (i, obj) { + { + $(obj).select2(); + } + }); + + // $('.datepicker').datepicker(); + // var datepicker = $.fn.datepicker.noConflict(); // return $.fn.datepicker to previously assigned value + // $.fn.bootstrapDP = datepicker; + // $('.datepicker').datepicker(); + + // Crazy select2 rich dropdowns with images! + $('.js-data-ajax').each(function (i, item) { + var link = $(item); + var endpoint = link.data("endpoint"); + var select = link.data("select"); + link.select2({ + /** + * Adds an empty placeholder, allowing every select2 instance to be cleared. + * This placeholder can be overridden with the "data-placeholder" attribute. + */ + placeholder: '', + allowClear: true, + language: $('meta[name="language"]').attr('content'), + dir: $('meta[name="language-direction"]').attr('content'), + ajax: { + // the baseUrl includes a trailing slash + url: baseUrl + 'api/v1/' + endpoint + '/selectlist', + dataType: 'json', + delay: 250, + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + }, + data: function data(params) { + var data = { + search: params.term, + page: params.page || 1, + assetStatusType: link.data("asset-status-type") + }; + return data; + }, + /* processResults: function (data, params) { + params.page = params.page || 1; + var answer = { + results: data.items, + pagination: { + more: data.pagination.more + } + }; + return answer; + }, */ + cache: true + }, + //escapeMarkup: function (markup) { return markup; }, // let our custom formatter work + templateResult: formatDatalistSafe + //templateSelection: formatDataSelection + }); + }); + function getSelect2Value(element) { + // if the passed object is not a jquery object, assuming 'element' is a selector + if (!(element instanceof jQuery)) element = $(element); + var select = element.data("select2"); + + // There's two different locations where the select2-generated input element can be. + searchElement = select.dropdown.$search || select.$container.find(".select2-search__field"); + var value = searchElement.val(); + return value; + } + $(".select2-hidden-accessible").on('select2:selecting', function (e) { + var data = e.params.args.data; + var isMouseUp = false; + var element = $(this); + var value = getSelect2Value(element); + if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup"; + + // if selected item does not match typed text, do not allow it to pass - force close for ajax. + if (!isMouseUp) { + if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) < 0) { + e.preventDefault(); + element.select2('close'); + + // if it does match, we set a flag in the event (which gets passed to subsequent events), telling it not to worry about the ajax + } else if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) > -1) { + e.params.args.noForceAjax = true; + } + } + }); + $(".select2-hidden-accessible").on('select2:closing', function (e) { + var element = $(this); + var value = getSelect2Value(element); + var noForceAjax = false; + var isMouseUp = false; + if (e.params.args.originalSelect2Event) noForceAjax = e.params.args.originalSelect2Event.noForceAjax; + if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup"; + if (value && !noForceAjax && !isMouseUp) { + var endpoint = element.data("endpoint"); + var assetStatusType = element.data("asset-status-type"); + $.ajax({ + url: baseUrl + 'api/v1/' + endpoint + '/selectlist?search=' + value + '&page=1' + (assetStatusType ? '&assetStatusType=' + assetStatusType : ''), + dataType: 'json', + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + } + }).done(function (response) { + var currentlySelected = element.select2('data').map(function (x) { + return +x.id; + }).filter(function (x) { + return x !== 0; + }); + + // makes sure we're not selecting the same thing twice for multiples + var filteredResponse = response.results.filter(function (item) { + return currentlySelected.indexOf(+item.id) < 0; + }); + var first = currentlySelected.length > 0 ? filteredResponse[0] : response.results[0]; + if (first && first.id) { + first.selected = true; + if ($("option[value='" + first.id + "']", element).length < 1) { + var option = new Option(first.text, first.id, true, true); + element.append(option); + } else { + var isMultiple = element.attr("multiple") == "multiple"; + element.val(isMultiple ? element.val().concat(first.id) : element.val(first.id)); + } + element.trigger('change'); + element.trigger({ + type: 'select2:select', + params: { + data: first + } + }); + } + }); + } + }); + function formatDatalist(datalist) { + var loading_markup = ' Loading...'; + if (datalist.loading) { + return loading_markup; + } + var markup = '
    '; + markup += '
    '; + if (datalist.image) { + markup += "
    " + datalist.text + "
    "; + } else { + markup += '
    '; + } + markup += "
    " + datalist.text + "
    "; + markup += "
    "; + return markup; + } + function formatDatalistSafe(datalist) { + // console.warn("What in the hell is going on with Select2?!?!!?!?"); + // console.warn($.select2); + if (datalist.loading) { + return $(' Loading...'); + } + var root_div = $("
    "); + var left_pull = $("
    "); + if (datalist.image) { + var inner_div = $("
    "); + /****************************************************************** + * + * We are specifically chosing empty alt-text below, because this + * image conveys no additional information, relative to the text + * that will *always* be there in any select2 list that is in use + * in Snipe-IT. If that changes, we would probably want to change + * some signatures of some functions, but right now, we don't want + * screen readers to say "HP SuperJet 5000, .... picture of HP + * SuperJet 5000..." and so on, for every single row in a list of + * assets or models or whatever. + * + *******************************************************************/ + var img = $(""); + // console.warn("Img is: "); + // console.dir(img); + // console.warn("Strigularly, that's: "); + // console.log(img); + img.attr("src", datalist.image); + inner_div.append(img); + } else { + var inner_div = $("
    "); + } + left_pull.append(inner_div); + root_div.append(left_pull); + var name_div = $("
    "); + name_div.text(datalist.text); + root_div.append(name_div); + var safe_html = root_div.get(0).outerHTML; + var old_html = formatDatalist(datalist); + if (safe_html != old_html) { + //console.log("HTML MISMATCH: "); + //console.log("FormatDatalistSafe: "); + // console.dir(root_div.get(0)); + //console.log(safe_html); + //console.log("FormatDataList: "); + //console.log(old_html); + } + return root_div; + } + function formatDataSelection(datalist) { + // This a heinous workaround for a known bug in Select2. + // Without this, the rich selectlists are vulnerable to XSS. + // Many thanks to @uberbrady for this fix. It ain't pretty, + // but it resolves the issue until Select2 addresses it on their end. + // + // Bug was reported in 2016 :{ + // https://github.com/select2/select2/issues/4587 + + return datalist.text.replace(/>/g, '>').replace(/Click me + $('a[data-toggle="tab"]').click(function (e) { + var href = $(this).attr("href"); + history.pushState(null, null, href); + e.preventDefault(); + $('a[href="' + $(this).attr('href') + '"]').tab('show'); + }); + + // ------------------------------------------------ + // End Deep Linking for Bootstrap tabs + // ------------------------------------------------ + + // Image preview + function readURL(input, $preview) { + if (input.files && input.files[0]) { + var reader = new FileReader(); + reader.onload = function (e) { + $preview.attr('src', e.target.result); + }; + reader.readAsDataURL(input.files[0]); + } + } + function formatBytes(bytes) { + if (bytes < 1024) return bytes + " Bytes";else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + " KB";else if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + " MB";else return (bytes / 1073741824).toFixed(2) + " GB"; + } + + // File size validation + $('.js-uploadFile').bind('change', function () { + var $this = $(this); + var id = '#' + $this.attr('id'); + var status = id + '-status'; + var $status = $(status); + var delete_id = $(id + '-deleteCheckbox'); + var preview_container = $(id + '-previewContainer'); + $status.removeClass('text-success').removeClass('text-danger'); + $(status + ' .goodfile').remove(); + $(status + ' .badfile').remove(); + $(status + ' .previewSize').hide(); + preview_container.hide(); + $(id + '-info').html(''); + var max_size = $this.data('maxsize'); + var total_size = 0; + for (var i = 0; i < this.files.length; i++) { + total_size += this.files[i].size; + $(id + '-info').append('' + htmlEntities(this.files[i].name) + ' (' + formatBytes(this.files[i].size) + ') '); + } + if (total_size > max_size) { + $status.addClass('text-danger').removeClass('help-block').prepend(' ').append(' Upload is ' + formatBytes(total_size) + '.'); + } else { + $status.addClass('text-success').removeClass('help-block').prepend(' '); + var $preview = $(id + '-imagePreview'); + readURL(this, $preview); + $preview.fadeIn(); + preview_container.fadeIn(); + delete_id.hide(); + } + }); +}); +function htmlEntities(str) { + return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +} + +/** + * Toggle disabled + */ +(function ($) { + $.fn.toggleDisabled = function (callback) { + return this.each(function () { + var disabled, + $this = $(this); + if ($this.attr('disabled')) { + $this.removeAttr('disabled'); + disabled = false; + } else { + $this.attr('disabled', 'disabled'); + disabled = true; + } + if (callback && typeof callback === 'function') { + callback(this, disabled); + } + }); + }; +})(jQuery); + +/** + * Universal Livewire Select2 integration + * + * How to use: + * + * 1. Set the class of your select2 elements to 'livewire-select2'). + * 2. Name your element to match a property in your Livewire component + * 3. Add an attribute called 'data-livewire-component' that points to $this->getId() (via `{{ }}` if you're in a blade, + * or just $this->getId() if not). + */ +document.addEventListener('livewire:init', function () { + $('.livewire-select2').select2(); + $(document).on('select2:select', '.livewire-select2', function (event) { + var target = $(event.target); + if (!event.target.name || !target.data('livewire-component')) { + console.error("You need to set both name (which should match a Livewire property) and data-livewire-component on your Livewire-ed select2 elements!"); + console.error("For data-livewire-component, you probably want to use $this->getId() or {{ $this->getId() }}, as appropriate"); + return false; + } + Livewire.find(target.data('livewire-component')).set(event.target.name, this.options[this.selectedIndex].value); + }); + Livewire.hook('request', function (_ref) { + var succeed = _ref.succeed; + succeed(function () { + queueMicrotask(function () { + $('.livewire-select2').select2(); + }); + }); + }); +}); + +/***/ }), + +/***/ "./resources/assets/js/snipeit_modals.js": +/*!***********************************************!*\ + !*** ./resources/assets/js/snipeit_modals.js ***! + \***********************************************/ +/***/ (() => { + +/* + * + * Snipe-IT Universal Modal support + * + * Enables modal dialogs to create sub-resources throughout Snipe-IT + * + */ + +/* +HOW TO USE + Create a Button looking like this: + New + If you don't have access to Blade commands (like {{ and }}, etc), you can hard-code a URL as the 'href' + data-toggle="modal" - required for Bootstrap Modals +data-target="#createModal" - fixed ID for the modal, do not change +data-select="assigned_to" - What is the *ID* of the select-dropdown that you're going to be adding to, if the modal-create was a success? Be on the lookout for duplicate ID's, it will confuse this library! +class="btn btn-sm btn-primary" - makes it look button-ey, feel free to change :) + +If you want to pass additional variables to the modal (In the Category Create one, for example, you can pass category_id), you can encode them as URL variables in the href + +*/ + +$(function () { + var baseUrl = $('meta[name="baseUrl"]').attr('content'); + //handle modal-add-interstitial calls + var model, select, refreshSelector; + if ($('#createModal').length == 0) { + $('body').append(''); + } + $('#createModal').on("show.bs.modal", function (event) { + var link = $(event.relatedTarget); + model = link.data("dependency"); + select = link.data("select"); + refreshSelector = link.data("refresh"); + $('#createModal').load(link.attr('href'), function () { + // this sets the focus to be the name field + $('#modal-name').focus(); + + //do we need to re-select2 this, after load? Probably. + $('#createModal').find('select.select2').select2(); + // Initialize the ajaxy select2 with images. + // This is a copy/paste of the code from snipeit.js, would be great to only have this in one place. + + $('.js-data-ajax').each(function (i, item) { + var link = $(item); + var endpoint = link.data("endpoint"); + var select = link.data("select"); + link.select2({ + ajax: { + // the baseUrl includes a trailing slash + url: baseUrl + 'api/v1/' + endpoint + '/selectlist', + //WARNING - we're hoping that's defined on the page somewhere... + dataType: 'json', + delay: 250, + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + }, + data: function data(params) { + var data = { + search: params.term, + page: params.page || 1, + assetStatusType: link.data("asset-status-type") + }; + return data; + }, + /*processResults: function (data, params) { + params.page = params.page || 1; + var answer = { + results: data.items, + pagination: { + more: data.pagination.more + } + }; + return answer; + },*/ + cache: true + }, + //escapeMarkup: function (markup) { return markup; }, // let our custom formatter work + templateResult: formatDatalistSafe + //templateSelection: formatDataSelection + }); + }); + }); + }); + $('#createModal').on('click', '#modal-save', function () { + $.ajax({ + type: 'POST', + url: $('.modal-body form').attr('action'), + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + }, + data: $('.modal-body form').serialize(), + success: function success(result) { + if (result.status == "error") { + var error_message = ""; + for (var field in result.messages) { + error_message += "
  • Problem(s) with field " + field + ": " + result.messages[field]; + } + $('#modal_error_msg').html(error_message).show(); + return false; + } + var id = result.payload.id; + var name = result.payload.name || result.payload.first_name + " " + result.payload.last_name; + if (!id || !name) { + console.error("Could not find resulting name or ID from modal-create. Name: " + name + ", id: " + id); + return false; + } + $('#createModal').modal('hide'); + $('#createModal').html(""); + var refreshTable = $('#' + refreshSelector); + if (refreshTable.length > 0) { + refreshTable.bootstrapTable('refresh'); + } + + // "select" is the original drop-down menu that someone + // clicked 'add' on to add a new 'thing' + // this code adds the newly created object to that select + var selector = document.getElementById(select); + if (!selector) { + return false; + } + selector.options[selector.length] = new Option(name, id); + selector.selectedIndex = selector.length - 1; + $(selector).trigger("change"); + if (window.fetchCustomFields) { + fetchCustomFields(); + } + }, + error: function error(result) { + msg = result.responseJSON.messages || result.responseJSON.error; + $('#modal_error_msg').html("Server Error: " + msg).show(); + } + }); + }); +}); +function formatDatalistSafe(datalist) { + // console.warn("What in the hell is going on with Select2?!?!!?!?"); + // console.warn($.select2); + if (datalist.loading) { + return $(' Loading...'); + } + var root_div = $("
    "); + var left_pull = $("
    "); + if (datalist.image) { + var inner_div = $("
    "); + /****************************************************************** + * + * We are specifically chosing empty alt-text below, because this + * image conveys no additional information, relative to the text + * that will *always* be there in any select2 list that is in use + * in Snipe-IT. If that changes, we would probably want to change + * some signatures of some functions, but right now, we don't want + * screen readers to say "HP SuperJet 5000, .... picture of HP + * SuperJet 5000..." and so on, for every single row in a list of + * assets or models or whatever. + * + *******************************************************************/ + var img = $(""); + // console.warn("Img is: "); + // console.dir(img); + // console.warn("Strigularly, that's: "); + // console.log(img); + img.attr("src", datalist.image); + inner_div.append(img); + } else { + var inner_div = $("
    "); + } + left_pull.append(inner_div); + root_div.append(left_pull); + var name_div = $("
    "); + name_div.text(datalist.text); + root_div.append(name_div); + var safe_html = root_div.get(0).outerHTML; + var old_html = formatDatalist(datalist); + if (safe_html != old_html) { + // console.log("HTML MISMATCH: "); + // console.log("FormatDatalistSafe: "); + // console.dir(root_div.get(0)); + // console.log(safe_html); + // console.log("FormatDataList: "); + // console.log(old_html); + } + return root_div; +} +function formatDatalist(datalist) { + var loading_markup = ' Loading...'; + if (datalist.loading) { + return loading_markup; + } + var markup = "
    "; + markup += "
    "; + if (datalist.image) { + markup += "
    " + datalist.tex + "
    "; + } else { + markup += "
    "; + } + markup += "
    " + datalist.text + "
    "; + markup += "
    "; + return markup; +} +function formatDataSelection(datalist) { + return datalist.text.replace(/>/g, '>').replace(/ { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }), + +/***/ "./resources/assets/less/overrides.less": +/*!**********************************************!*\ + !*** ./resources/assets/less/overrides.less ***! + \**********************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }), + +/***/ "./resources/assets/less/skins/_all-skins.less": +/*!*****************************************************!*\ + !*** ./resources/assets/less/skins/_all-skins.less ***! + \*****************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }), + +/***/ "./resources/assets/less/skins/skin-black-dark.less": +/*!**********************************************************!*\ + !*** ./resources/assets/less/skins/skin-black-dark.less ***! + \**********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + /***/ }), /***/ "./resources/assets/less/skins/skin-black.less": @@ -33097,71 +33162,6 @@ __webpack_require__.r(__webpack_exports__); // extracted by mini-css-extract-plugin -/***/ }), - -/***/ "./node_modules/admin-lte/build/less/AdminLTE.less": -/*!*********************************************************!*\ - !*** ./node_modules/admin-lte/build/less/AdminLTE.less ***! - \*********************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./resources/assets/less/app.less": -/*!****************************************!*\ - !*** ./resources/assets/less/app.less ***! - \****************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./resources/assets/less/overrides.less": -/*!**********************************************!*\ - !*** ./resources/assets/less/overrides.less ***! - \**********************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./resources/assets/less/skins/_all-skins.less": -/*!*****************************************************!*\ - !*** ./resources/assets/less/skins/_all-skins.less ***! - \*****************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./resources/assets/less/skins/skin-black-dark.less": -/*!**********************************************************!*\ - !*** ./resources/assets/less/skins/skin-black-dark.less ***! - \**********************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - /***/ }) /******/ }); diff --git a/public/js/dist/all.js b/public/js/dist/all.js index f69d193940..b31634ca30 100644 --- a/public/js/dist/all.js +++ b/public/js/dist/all.js @@ -58934,799 +58934,16 @@ module.exports.TinyEmitter = E; /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ "./resources/assets/js/bootstrap.js": -/*!******************************************!*\ - !*** ./resources/assets/js/bootstrap.js ***! - \******************************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { +/***/ "./node_modules/admin-lte/build/less/AdminLTE.less": +/*!*********************************************************!*\ + !*** ./node_modules/admin-lte/build/less/AdminLTE.less ***! + \*********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -window._ = __webpack_require__(/*! lodash */ "./node_modules/lodash/lodash.js"); +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin -/** - * We'll load jQuery and the Bootstrap jQuery plugin which provides support - * for JavaScript based Bootstrap features such as modals and tabs. This - * code may be modified to fit the specific needs of your application. - */ -//window.$ = window.jQuery = require('jquery'); - -/** - * jQuery UI is loaded here and then the tooltip is assigned another funtion name - * This resolves the issue of jquery-ui & bootstrap tooltip conflict - */ -__webpack_require__(/*! jquery-ui */ "./node_modules/jquery-ui/ui/widget.js"); -jQuery.fn.uitooltip = jQuery.fn.tooltip; - -/** - * Load boostrap - */ -__webpack_require__(/*! bootstrap-less */ "./node_modules/bootstrap-less/js/bootstrap.js"); - -// require('admin-lte'); - -// require('chart.js'); - -// require('jquery-form-validator'); //says something about dependency -/** - * Echo exposes an expressive API for subscribing to channels and listening - * for events that are broadcast by Laravel. Echo and event broadcasting - * allows your team to easily build robust real-time web applications. - */ - -// import Echo from "laravel-echo" - -// window.Echo = new Echo({ -// broadcaster: 'pusher', -// key: 'your-pusher-key' -// }); - -/***/ }), - -/***/ "./resources/assets/js/snipeit.js": -/*!****************************************!*\ - !*** ./resources/assets/js/snipeit.js ***! - \****************************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -// var jQuery = require('jquery'); -// window.jQuery = jQuery -// window.$ = jQuery - -__webpack_require__(/*! ./bootstrap */ "./resources/assets/js/bootstrap.js"); - -/** - * Module containing core application logic. - * @param {jQuery} $ Insulated jQuery object - * @param {JSON} settings Insulated `window.snipeit.settings` object. - * @return {IIFE} Immediately invoked. Returns self. - */ - -lineOptions = { - legend: { - position: "bottom" - }, - scales: { - yAxes: [{ - ticks: { - fontColor: "rgba(0,0,0,0.5)", - fontStyle: "bold", - beginAtZero: true, - maxTicksLimit: 5, - padding: 20 - }, - gridLines: { - drawTicks: false, - display: false - } - }], - xAxes: [{ - gridLines: { - zeroLineColor: "transparent" - }, - ticks: { - padding: 20, - fontColor: "rgba(0,0,0,0.5)", - fontStyle: "bold" - } - }] - } -}; -pieOptions = { - //Boolean - Whether we should show a stroke on each segment - segmentShowStroke: true, - //String - The colour of each segment stroke - segmentStrokeColor: "#fff", - //Number - The width of each segment stroke - segmentStrokeWidth: 1, - //Number - The percentage of the chart that we cut out of the middle - percentageInnerCutout: 50, - // This is 0 for Pie charts - //Number - Amount of animation steps - animationSteps: 100, - //String - Animation easing effect - animationEasing: "easeOutBounce", - //Boolean - Whether we animate the rotation of the Doughnut - animateRotate: true, - //Boolean - Whether we animate scaling the Doughnut from the centre - animateScale: false, - //Boolean - whether to make the chart responsive to window resizing - responsive: true, - // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container - maintainAspectRatio: false, - //String - A legend template - legendTemplate: "
      -legend\"><% for (var i=0; i
    • " + "" + "<%if(segments[i].label){%><%=segments[i].label%><%}%> foo
    • <%}%>
    ", - //String - A tooltip template - tooltipTemplate: "<%=value %> <%=label%> " -}; - -//----------------- -//- END PIE CHART - -//----------------- - -var baseUrl = $('meta[name="baseUrl"]').attr('content'); -$(function () { - var $el = $('table'); - - // confirm restore modal - - $el.on('click', '.restore-asset', function (evnt) { - var $context = $(this); - var $restoreConfirmModal = $('#restoreConfirmModal'); - var href = $context.attr('href'); - var message = $context.attr('data-content'); - var title = $context.attr('data-title'); - $('#confirmModalLabel').text(title); - $restoreConfirmModal.find('.modal-body').text(message); - $('#restoreForm').attr('action', href); - $restoreConfirmModal.modal({ - show: true - }); - return false; - }); - - // confirm delete modal - - $el.on('click', '.delete-asset', function (evnt) { - var $context = $(this); - var $dataConfirmModal = $('#dataConfirmModal'); - var href = $context.attr('href'); - var message = $context.attr('data-content'); - var title = $context.attr('data-title'); - $('#myModalLabel').text(title); - $dataConfirmModal.find('.modal-body').text(message); - $('#deleteForm').attr('action', href); - $dataConfirmModal.modal({ - show: true - }); - return false; - }); - - /* - * Slideout help menu - */ - $('.slideout-menu-toggle').on('click', function (event) { - event.preventDefault(); - // create menu variables - var slideoutMenu = $('.slideout-menu'); - var slideoutMenuWidth = $('.slideout-menu').width(); - - // toggle open class - slideoutMenu.toggleClass("open"); - - // slide menu - if (slideoutMenu.hasClass("open")) { - slideoutMenu.show(); - slideoutMenu.animate({ - right: "0px" - }); - } else { - slideoutMenu.animate({ - right: -slideoutMenuWidth - }, "-350px"); - slideoutMenu.fadeOut(); - } - }); - - /* - * Select2 - */ - - $('select.select2:not(".select2-hidden-accessible")').each(function (i, obj) { - { - $(obj).select2(); - } - }); - - // $('.datepicker').datepicker(); - // var datepicker = $.fn.datepicker.noConflict(); // return $.fn.datepicker to previously assigned value - // $.fn.bootstrapDP = datepicker; - // $('.datepicker').datepicker(); - - // Crazy select2 rich dropdowns with images! - $('.js-data-ajax').each(function (i, item) { - var link = $(item); - var endpoint = link.data("endpoint"); - var select = link.data("select"); - link.select2({ - /** - * Adds an empty placeholder, allowing every select2 instance to be cleared. - * This placeholder can be overridden with the "data-placeholder" attribute. - */ - placeholder: '', - allowClear: true, - language: $('meta[name="language"]').attr('content'), - dir: $('meta[name="language-direction"]').attr('content'), - ajax: { - // the baseUrl includes a trailing slash - url: baseUrl + 'api/v1/' + endpoint + '/selectlist', - dataType: 'json', - delay: 250, - headers: { - "X-Requested-With": 'XMLHttpRequest', - "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') - }, - data: function data(params) { - var data = { - search: params.term, - page: params.page || 1, - assetStatusType: link.data("asset-status-type") - }; - return data; - }, - /* processResults: function (data, params) { - params.page = params.page || 1; - var answer = { - results: data.items, - pagination: { - more: data.pagination.more - } - }; - return answer; - }, */ - cache: true - }, - //escapeMarkup: function (markup) { return markup; }, // let our custom formatter work - templateResult: formatDatalistSafe - //templateSelection: formatDataSelection - }); - }); - function getSelect2Value(element) { - // if the passed object is not a jquery object, assuming 'element' is a selector - if (!(element instanceof jQuery)) element = $(element); - var select = element.data("select2"); - - // There's two different locations where the select2-generated input element can be. - searchElement = select.dropdown.$search || select.$container.find(".select2-search__field"); - var value = searchElement.val(); - return value; - } - $(".select2-hidden-accessible").on('select2:selecting', function (e) { - var data = e.params.args.data; - var isMouseUp = false; - var element = $(this); - var value = getSelect2Value(element); - if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup"; - - // if selected item does not match typed text, do not allow it to pass - force close for ajax. - if (!isMouseUp) { - if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) < 0) { - e.preventDefault(); - element.select2('close'); - - // if it does match, we set a flag in the event (which gets passed to subsequent events), telling it not to worry about the ajax - } else if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) > -1) { - e.params.args.noForceAjax = true; - } - } - }); - $(".select2-hidden-accessible").on('select2:closing', function (e) { - var element = $(this); - var value = getSelect2Value(element); - var noForceAjax = false; - var isMouseUp = false; - if (e.params.args.originalSelect2Event) noForceAjax = e.params.args.originalSelect2Event.noForceAjax; - if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup"; - if (value && !noForceAjax && !isMouseUp) { - var endpoint = element.data("endpoint"); - var assetStatusType = element.data("asset-status-type"); - $.ajax({ - url: baseUrl + 'api/v1/' + endpoint + '/selectlist?search=' + value + '&page=1' + (assetStatusType ? '&assetStatusType=' + assetStatusType : ''), - dataType: 'json', - headers: { - "X-Requested-With": 'XMLHttpRequest', - "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') - } - }).done(function (response) { - var currentlySelected = element.select2('data').map(function (x) { - return +x.id; - }).filter(function (x) { - return x !== 0; - }); - - // makes sure we're not selecting the same thing twice for multiples - var filteredResponse = response.results.filter(function (item) { - return currentlySelected.indexOf(+item.id) < 0; - }); - var first = currentlySelected.length > 0 ? filteredResponse[0] : response.results[0]; - if (first && first.id) { - first.selected = true; - if ($("option[value='" + first.id + "']", element).length < 1) { - var option = new Option(first.text, first.id, true, true); - element.append(option); - } else { - var isMultiple = element.attr("multiple") == "multiple"; - element.val(isMultiple ? element.val().concat(first.id) : element.val(first.id)); - } - element.trigger('change'); - element.trigger({ - type: 'select2:select', - params: { - data: first - } - }); - } - }); - } - }); - function formatDatalist(datalist) { - var loading_markup = ' Loading...'; - if (datalist.loading) { - return loading_markup; - } - var markup = '
    '; - markup += '
    '; - if (datalist.image) { - markup += "
    " + datalist.text + "
    "; - } else { - markup += '
    '; - } - markup += "
    " + datalist.text + "
    "; - markup += "
    "; - return markup; - } - function formatDatalistSafe(datalist) { - // console.warn("What in the hell is going on with Select2?!?!!?!?"); - // console.warn($.select2); - if (datalist.loading) { - return $(' Loading...'); - } - var root_div = $("
    "); - var left_pull = $("
    "); - if (datalist.image) { - var inner_div = $("
    "); - /****************************************************************** - * - * We are specifically chosing empty alt-text below, because this - * image conveys no additional information, relative to the text - * that will *always* be there in any select2 list that is in use - * in Snipe-IT. If that changes, we would probably want to change - * some signatures of some functions, but right now, we don't want - * screen readers to say "HP SuperJet 5000, .... picture of HP - * SuperJet 5000..." and so on, for every single row in a list of - * assets or models or whatever. - * - *******************************************************************/ - var img = $(""); - // console.warn("Img is: "); - // console.dir(img); - // console.warn("Strigularly, that's: "); - // console.log(img); - img.attr("src", datalist.image); - inner_div.append(img); - } else { - var inner_div = $("
    "); - } - left_pull.append(inner_div); - root_div.append(left_pull); - var name_div = $("
    "); - name_div.text(datalist.text); - root_div.append(name_div); - var safe_html = root_div.get(0).outerHTML; - var old_html = formatDatalist(datalist); - if (safe_html != old_html) { - //console.log("HTML MISMATCH: "); - //console.log("FormatDatalistSafe: "); - // console.dir(root_div.get(0)); - //console.log(safe_html); - //console.log("FormatDataList: "); - //console.log(old_html); - } - return root_div; - } - function formatDataSelection(datalist) { - // This a heinous workaround for a known bug in Select2. - // Without this, the rich selectlists are vulnerable to XSS. - // Many thanks to @uberbrady for this fix. It ain't pretty, - // but it resolves the issue until Select2 addresses it on their end. - // - // Bug was reported in 2016 :{ - // https://github.com/select2/select2/issues/4587 - - return datalist.text.replace(/>/g, '>').replace(/Click me - $('a[data-toggle="tab"]').click(function (e) { - var href = $(this).attr("href"); - history.pushState(null, null, href); - e.preventDefault(); - $('a[href="' + $(this).attr('href') + '"]').tab('show'); - }); - - // ------------------------------------------------ - // End Deep Linking for Bootstrap tabs - // ------------------------------------------------ - - // Image preview - function readURL(input, $preview) { - if (input.files && input.files[0]) { - var reader = new FileReader(); - reader.onload = function (e) { - $preview.attr('src', e.target.result); - }; - reader.readAsDataURL(input.files[0]); - } - } - function formatBytes(bytes) { - if (bytes < 1024) return bytes + " Bytes";else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + " KB";else if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + " MB";else return (bytes / 1073741824).toFixed(2) + " GB"; - } - - // File size validation - $('.js-uploadFile').bind('change', function () { - var $this = $(this); - var id = '#' + $this.attr('id'); - var status = id + '-status'; - var $status = $(status); - var delete_id = $(id + '-deleteCheckbox'); - var preview_container = $(id + '-previewContainer'); - $status.removeClass('text-success').removeClass('text-danger'); - $(status + ' .goodfile').remove(); - $(status + ' .badfile').remove(); - $(status + ' .previewSize').hide(); - preview_container.hide(); - $(id + '-info').html(''); - var max_size = $this.data('maxsize'); - var total_size = 0; - for (var i = 0; i < this.files.length; i++) { - total_size += this.files[i].size; - $(id + '-info').append('' + htmlEntities(this.files[i].name) + ' (' + formatBytes(this.files[i].size) + ') '); - } - if (total_size > max_size) { - $status.addClass('text-danger').removeClass('help-block').prepend(' ').append(' Upload is ' + formatBytes(total_size) + '.'); - } else { - $status.addClass('text-success').removeClass('help-block').prepend(' '); - var $preview = $(id + '-imagePreview'); - readURL(this, $preview); - $preview.fadeIn(); - preview_container.fadeIn(); - delete_id.hide(); - } - }); -}); -function htmlEntities(str) { - return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -} - -/** - * Toggle disabled - */ -(function ($) { - $.fn.toggleDisabled = function (callback) { - return this.each(function () { - var disabled, - $this = $(this); - if ($this.attr('disabled')) { - $this.removeAttr('disabled'); - disabled = false; - } else { - $this.attr('disabled', 'disabled'); - disabled = true; - } - if (callback && typeof callback === 'function') { - callback(this, disabled); - } - }); - }; -})(jQuery); - -/** - * Universal Livewire Select2 integration - * - * How to use: - * - * 1. Set the class of your select2 elements to 'livewire-select2'). - * 2. Name your element to match a property in your Livewire component - * 3. Add an attribute called 'data-livewire-component' that points to $this->getId() (via `{{ }}` if you're in a blade, - * or just $this->getId() if not). - */ -document.addEventListener('livewire:init', function () { - $('.livewire-select2').select2(); - $(document).on('select2:select', '.livewire-select2', function (event) { - var target = $(event.target); - if (!event.target.name || !target.data('livewire-component')) { - console.error("You need to set both name (which should match a Livewire property) and data-livewire-component on your Livewire-ed select2 elements!"); - console.error("For data-livewire-component, you probably want to use $this->getId() or {{ $this->getId() }}, as appropriate"); - return false; - } - Livewire.find(target.data('livewire-component')).set(event.target.name, this.options[this.selectedIndex].value); - }); - Livewire.hook('request', function (_ref) { - var succeed = _ref.succeed; - succeed(function () { - queueMicrotask(function () { - $('.livewire-select2').select2(); - }); - }); - }); -}); - -/***/ }), - -/***/ "./resources/assets/js/snipeit_modals.js": -/*!***********************************************!*\ - !*** ./resources/assets/js/snipeit_modals.js ***! - \***********************************************/ -/***/ (() => { - -/* - * - * Snipe-IT Universal Modal support - * - * Enables modal dialogs to create sub-resources throughout Snipe-IT - * - */ - -/* -HOW TO USE - Create a Button looking like this: - New - If you don't have access to Blade commands (like {{ and }}, etc), you can hard-code a URL as the 'href' - data-toggle="modal" - required for Bootstrap Modals -data-target="#createModal" - fixed ID for the modal, do not change -data-select="assigned_to" - What is the *ID* of the select-dropdown that you're going to be adding to, if the modal-create was a success? Be on the lookout for duplicate ID's, it will confuse this library! -class="btn btn-sm btn-primary" - makes it look button-ey, feel free to change :) - -If you want to pass additional variables to the modal (In the Category Create one, for example, you can pass category_id), you can encode them as URL variables in the href - -*/ - -$(function () { - var baseUrl = $('meta[name="baseUrl"]').attr('content'); - //handle modal-add-interstitial calls - var model, select, refreshSelector; - if ($('#createModal').length == 0) { - $('body').append(''); - } - $('#createModal').on("show.bs.modal", function (event) { - var link = $(event.relatedTarget); - model = link.data("dependency"); - select = link.data("select"); - refreshSelector = link.data("refresh"); - $('#createModal').load(link.attr('href'), function () { - // this sets the focus to be the name field - $('#modal-name').focus(); - - //do we need to re-select2 this, after load? Probably. - $('#createModal').find('select.select2').select2(); - // Initialize the ajaxy select2 with images. - // This is a copy/paste of the code from snipeit.js, would be great to only have this in one place. - - $('.js-data-ajax').each(function (i, item) { - var link = $(item); - var endpoint = link.data("endpoint"); - var select = link.data("select"); - link.select2({ - ajax: { - // the baseUrl includes a trailing slash - url: baseUrl + 'api/v1/' + endpoint + '/selectlist', - //WARNING - we're hoping that's defined on the page somewhere... - dataType: 'json', - delay: 250, - headers: { - "X-Requested-With": 'XMLHttpRequest', - "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') - }, - data: function data(params) { - var data = { - search: params.term, - page: params.page || 1, - assetStatusType: link.data("asset-status-type") - }; - return data; - }, - /*processResults: function (data, params) { - params.page = params.page || 1; - var answer = { - results: data.items, - pagination: { - more: data.pagination.more - } - }; - return answer; - },*/ - cache: true - }, - //escapeMarkup: function (markup) { return markup; }, // let our custom formatter work - templateResult: formatDatalistSafe - //templateSelection: formatDataSelection - }); - }); - }); - }); - $('#createModal').on('click', '#modal-save', function () { - $.ajax({ - type: 'POST', - url: $('.modal-body form').attr('action'), - headers: { - "X-Requested-With": 'XMLHttpRequest', - "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') - }, - data: $('.modal-body form').serialize(), - success: function success(result) { - if (result.status == "error") { - var error_message = ""; - for (var field in result.messages) { - error_message += "
  • Problem(s) with field " + field + ": " + result.messages[field]; - } - $('#modal_error_msg').html(error_message).show(); - return false; - } - var id = result.payload.id; - var name = result.payload.name || result.payload.first_name + " " + result.payload.last_name; - if (!id || !name) { - console.error("Could not find resulting name or ID from modal-create. Name: " + name + ", id: " + id); - return false; - } - $('#createModal').modal('hide'); - $('#createModal').html(""); - var refreshTable = $('#' + refreshSelector); - if (refreshTable.length > 0) { - refreshTable.bootstrapTable('refresh'); - } - - // "select" is the original drop-down menu that someone - // clicked 'add' on to add a new 'thing' - // this code adds the newly created object to that select - var selector = document.getElementById(select); - if (!selector) { - return false; - } - selector.options[selector.length] = new Option(name, id); - selector.selectedIndex = selector.length - 1; - $(selector).trigger("change"); - if (window.fetchCustomFields) { - fetchCustomFields(); - } - }, - error: function error(result) { - msg = result.responseJSON.messages || result.responseJSON.error; - $('#modal_error_msg').html("Server Error: " + msg).show(); - } - }); - }); -}); -function formatDatalistSafe(datalist) { - // console.warn("What in the hell is going on with Select2?!?!!?!?"); - // console.warn($.select2); - if (datalist.loading) { - return $(' Loading...'); - } - var root_div = $("
    "); - var left_pull = $("
    "); - if (datalist.image) { - var inner_div = $("
    "); - /****************************************************************** - * - * We are specifically chosing empty alt-text below, because this - * image conveys no additional information, relative to the text - * that will *always* be there in any select2 list that is in use - * in Snipe-IT. If that changes, we would probably want to change - * some signatures of some functions, but right now, we don't want - * screen readers to say "HP SuperJet 5000, .... picture of HP - * SuperJet 5000..." and so on, for every single row in a list of - * assets or models or whatever. - * - *******************************************************************/ - var img = $(""); - // console.warn("Img is: "); - // console.dir(img); - // console.warn("Strigularly, that's: "); - // console.log(img); - img.attr("src", datalist.image); - inner_div.append(img); - } else { - var inner_div = $("
    "); - } - left_pull.append(inner_div); - root_div.append(left_pull); - var name_div = $("
    "); - name_div.text(datalist.text); - root_div.append(name_div); - var safe_html = root_div.get(0).outerHTML; - var old_html = formatDatalist(datalist); - if (safe_html != old_html) { - // console.log("HTML MISMATCH: "); - // console.log("FormatDatalistSafe: "); - // console.dir(root_div.get(0)); - // console.log(safe_html); - // console.log("FormatDataList: "); - // console.log(old_html); - } - return root_div; -} -function formatDatalist(datalist) { - var loading_markup = ' Loading...'; - if (datalist.loading) { - return loading_markup; - } - var markup = "
    "; - markup += "
    "; - if (datalist.image) { - markup += "
    " + datalist.tex + "
    "; - } else { - markup += "
    "; - } - markup += "
    " + datalist.text + "
    "; - markup += "
    "; - return markup; -} -function formatDataSelection(datalist) { - return datalist.text.replace(/>/g, '>').replace(/ { + +window._ = __webpack_require__(/*! lodash */ "./node_modules/lodash/lodash.js"); + +/** + * We'll load jQuery and the Bootstrap jQuery plugin which provides support + * for JavaScript based Bootstrap features such as modals and tabs. This + * code may be modified to fit the specific needs of your application. + */ +//window.$ = window.jQuery = require('jquery'); + +/** + * jQuery UI is loaded here and then the tooltip is assigned another funtion name + * This resolves the issue of jquery-ui & bootstrap tooltip conflict + */ +__webpack_require__(/*! jquery-ui */ "./node_modules/jquery-ui/ui/widget.js"); +jQuery.fn.uitooltip = jQuery.fn.tooltip; + +/** + * Load boostrap + */ +__webpack_require__(/*! bootstrap-less */ "./node_modules/bootstrap-less/js/bootstrap.js"); + +// require('admin-lte'); + +// require('chart.js'); + +// require('jquery-form-validator'); //says something about dependency +/** + * Echo exposes an expressive API for subscribing to channels and listening + * for events that are broadcast by Laravel. Echo and event broadcasting + * allows your team to easily build robust real-time web applications. + */ + +// import Echo from "laravel-echo" + +// window.Echo = new Echo({ +// broadcaster: 'pusher', +// key: 'your-pusher-key' +// }); + +/***/ }), + +/***/ "./resources/assets/js/snipeit.js": +/*!****************************************!*\ + !*** ./resources/assets/js/snipeit.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +// var jQuery = require('jquery'); +// window.jQuery = jQuery +// window.$ = jQuery + +__webpack_require__(/*! ./bootstrap */ "./resources/assets/js/bootstrap.js"); + +/** + * Module containing core application logic. + * @param {jQuery} $ Insulated jQuery object + * @param {JSON} settings Insulated `window.snipeit.settings` object. + * @return {IIFE} Immediately invoked. Returns self. + */ + +lineOptions = { + legend: { + position: "bottom" + }, + scales: { + yAxes: [{ + ticks: { + fontColor: "rgba(0,0,0,0.5)", + fontStyle: "bold", + beginAtZero: true, + maxTicksLimit: 5, + padding: 20 + }, + gridLines: { + drawTicks: false, + display: false + } + }], + xAxes: [{ + gridLines: { + zeroLineColor: "transparent" + }, + ticks: { + padding: 20, + fontColor: "rgba(0,0,0,0.5)", + fontStyle: "bold" + } + }] + } +}; +pieOptions = { + //Boolean - Whether we should show a stroke on each segment + segmentShowStroke: true, + //String - The colour of each segment stroke + segmentStrokeColor: "#fff", + //Number - The width of each segment stroke + segmentStrokeWidth: 1, + //Number - The percentage of the chart that we cut out of the middle + percentageInnerCutout: 50, + // This is 0 for Pie charts + //Number - Amount of animation steps + animationSteps: 100, + //String - Animation easing effect + animationEasing: "easeOutBounce", + //Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + //Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false, + //Boolean - whether to make the chart responsive to window resizing + responsive: true, + // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container + maintainAspectRatio: false, + //String - A legend template + legendTemplate: "
      -legend\"><% for (var i=0; i
    • " + "" + "<%if(segments[i].label){%><%=segments[i].label%><%}%> foo
    • <%}%>
    ", + //String - A tooltip template + tooltipTemplate: "<%=value %> <%=label%> " +}; + +//----------------- +//- END PIE CHART - +//----------------- + +var baseUrl = $('meta[name="baseUrl"]').attr('content'); +$(function () { + var $el = $('table'); + + // confirm restore modal + + $el.on('click', '.restore-asset', function (evnt) { + var $context = $(this); + var $restoreConfirmModal = $('#restoreConfirmModal'); + var href = $context.attr('href'); + var message = $context.attr('data-content'); + var title = $context.attr('data-title'); + $('#confirmModalLabel').text(title); + $restoreConfirmModal.find('.modal-body').text(message); + $('#restoreForm').attr('action', href); + $restoreConfirmModal.modal({ + show: true + }); + return false; + }); + + // confirm delete modal + + $el.on('click', '.delete-asset', function (evnt) { + var $context = $(this); + var $dataConfirmModal = $('#dataConfirmModal'); + var href = $context.attr('href'); + var message = $context.attr('data-content'); + var title = $context.attr('data-title'); + $('#myModalLabel').text(title); + $dataConfirmModal.find('.modal-body').text(message); + $('#deleteForm').attr('action', href); + $dataConfirmModal.modal({ + show: true + }); + return false; + }); + + /* + * Slideout help menu + */ + $('.slideout-menu-toggle').on('click', function (event) { + event.preventDefault(); + // create menu variables + var slideoutMenu = $('.slideout-menu'); + var slideoutMenuWidth = $('.slideout-menu').width(); + + // toggle open class + slideoutMenu.toggleClass("open"); + + // slide menu + if (slideoutMenu.hasClass("open")) { + slideoutMenu.show(); + slideoutMenu.animate({ + right: "0px" + }); + } else { + slideoutMenu.animate({ + right: -slideoutMenuWidth + }, "-350px"); + slideoutMenu.fadeOut(); + } + }); + + /* + * Select2 + */ + + $('select.select2:not(".select2-hidden-accessible")').each(function (i, obj) { + { + $(obj).select2(); + } + }); + + // $('.datepicker').datepicker(); + // var datepicker = $.fn.datepicker.noConflict(); // return $.fn.datepicker to previously assigned value + // $.fn.bootstrapDP = datepicker; + // $('.datepicker').datepicker(); + + // Crazy select2 rich dropdowns with images! + $('.js-data-ajax').each(function (i, item) { + var link = $(item); + var endpoint = link.data("endpoint"); + var select = link.data("select"); + link.select2({ + /** + * Adds an empty placeholder, allowing every select2 instance to be cleared. + * This placeholder can be overridden with the "data-placeholder" attribute. + */ + placeholder: '', + allowClear: true, + language: $('meta[name="language"]').attr('content'), + dir: $('meta[name="language-direction"]').attr('content'), + ajax: { + // the baseUrl includes a trailing slash + url: baseUrl + 'api/v1/' + endpoint + '/selectlist', + dataType: 'json', + delay: 250, + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + }, + data: function data(params) { + var data = { + search: params.term, + page: params.page || 1, + assetStatusType: link.data("asset-status-type") + }; + return data; + }, + /* processResults: function (data, params) { + params.page = params.page || 1; + var answer = { + results: data.items, + pagination: { + more: data.pagination.more + } + }; + return answer; + }, */ + cache: true + }, + //escapeMarkup: function (markup) { return markup; }, // let our custom formatter work + templateResult: formatDatalistSafe + //templateSelection: formatDataSelection + }); + }); + function getSelect2Value(element) { + // if the passed object is not a jquery object, assuming 'element' is a selector + if (!(element instanceof jQuery)) element = $(element); + var select = element.data("select2"); + + // There's two different locations where the select2-generated input element can be. + searchElement = select.dropdown.$search || select.$container.find(".select2-search__field"); + var value = searchElement.val(); + return value; + } + $(".select2-hidden-accessible").on('select2:selecting', function (e) { + var data = e.params.args.data; + var isMouseUp = false; + var element = $(this); + var value = getSelect2Value(element); + if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup"; + + // if selected item does not match typed text, do not allow it to pass - force close for ajax. + if (!isMouseUp) { + if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) < 0) { + e.preventDefault(); + element.select2('close'); + + // if it does match, we set a flag in the event (which gets passed to subsequent events), telling it not to worry about the ajax + } else if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) > -1) { + e.params.args.noForceAjax = true; + } + } + }); + $(".select2-hidden-accessible").on('select2:closing', function (e) { + var element = $(this); + var value = getSelect2Value(element); + var noForceAjax = false; + var isMouseUp = false; + if (e.params.args.originalSelect2Event) noForceAjax = e.params.args.originalSelect2Event.noForceAjax; + if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup"; + if (value && !noForceAjax && !isMouseUp) { + var endpoint = element.data("endpoint"); + var assetStatusType = element.data("asset-status-type"); + $.ajax({ + url: baseUrl + 'api/v1/' + endpoint + '/selectlist?search=' + value + '&page=1' + (assetStatusType ? '&assetStatusType=' + assetStatusType : ''), + dataType: 'json', + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + } + }).done(function (response) { + var currentlySelected = element.select2('data').map(function (x) { + return +x.id; + }).filter(function (x) { + return x !== 0; + }); + + // makes sure we're not selecting the same thing twice for multiples + var filteredResponse = response.results.filter(function (item) { + return currentlySelected.indexOf(+item.id) < 0; + }); + var first = currentlySelected.length > 0 ? filteredResponse[0] : response.results[0]; + if (first && first.id) { + first.selected = true; + if ($("option[value='" + first.id + "']", element).length < 1) { + var option = new Option(first.text, first.id, true, true); + element.append(option); + } else { + var isMultiple = element.attr("multiple") == "multiple"; + element.val(isMultiple ? element.val().concat(first.id) : element.val(first.id)); + } + element.trigger('change'); + element.trigger({ + type: 'select2:select', + params: { + data: first + } + }); + } + }); + } + }); + function formatDatalist(datalist) { + var loading_markup = ' Loading...'; + if (datalist.loading) { + return loading_markup; + } + var markup = '
    '; + markup += '
    '; + if (datalist.image) { + markup += "
    " + datalist.text + "
    "; + } else { + markup += '
    '; + } + markup += "
    " + datalist.text + "
    "; + markup += "
    "; + return markup; + } + function formatDatalistSafe(datalist) { + // console.warn("What in the hell is going on with Select2?!?!!?!?"); + // console.warn($.select2); + if (datalist.loading) { + return $(' Loading...'); + } + var root_div = $("
    "); + var left_pull = $("
    "); + if (datalist.image) { + var inner_div = $("
    "); + /****************************************************************** + * + * We are specifically chosing empty alt-text below, because this + * image conveys no additional information, relative to the text + * that will *always* be there in any select2 list that is in use + * in Snipe-IT. If that changes, we would probably want to change + * some signatures of some functions, but right now, we don't want + * screen readers to say "HP SuperJet 5000, .... picture of HP + * SuperJet 5000..." and so on, for every single row in a list of + * assets or models or whatever. + * + *******************************************************************/ + var img = $(""); + // console.warn("Img is: "); + // console.dir(img); + // console.warn("Strigularly, that's: "); + // console.log(img); + img.attr("src", datalist.image); + inner_div.append(img); + } else { + var inner_div = $("
    "); + } + left_pull.append(inner_div); + root_div.append(left_pull); + var name_div = $("
    "); + name_div.text(datalist.text); + root_div.append(name_div); + var safe_html = root_div.get(0).outerHTML; + var old_html = formatDatalist(datalist); + if (safe_html != old_html) { + //console.log("HTML MISMATCH: "); + //console.log("FormatDatalistSafe: "); + // console.dir(root_div.get(0)); + //console.log(safe_html); + //console.log("FormatDataList: "); + //console.log(old_html); + } + return root_div; + } + function formatDataSelection(datalist) { + // This a heinous workaround for a known bug in Select2. + // Without this, the rich selectlists are vulnerable to XSS. + // Many thanks to @uberbrady for this fix. It ain't pretty, + // but it resolves the issue until Select2 addresses it on their end. + // + // Bug was reported in 2016 :{ + // https://github.com/select2/select2/issues/4587 + + return datalist.text.replace(/>/g, '>').replace(/Click me + $('a[data-toggle="tab"]').click(function (e) { + var href = $(this).attr("href"); + history.pushState(null, null, href); + e.preventDefault(); + $('a[href="' + $(this).attr('href') + '"]').tab('show'); + }); + + // ------------------------------------------------ + // End Deep Linking for Bootstrap tabs + // ------------------------------------------------ + + // Image preview + function readURL(input, $preview) { + if (input.files && input.files[0]) { + var reader = new FileReader(); + reader.onload = function (e) { + $preview.attr('src', e.target.result); + }; + reader.readAsDataURL(input.files[0]); + } + } + function formatBytes(bytes) { + if (bytes < 1024) return bytes + " Bytes";else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + " KB";else if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + " MB";else return (bytes / 1073741824).toFixed(2) + " GB"; + } + + // File size validation + $('.js-uploadFile').bind('change', function () { + var $this = $(this); + var id = '#' + $this.attr('id'); + var status = id + '-status'; + var $status = $(status); + var delete_id = $(id + '-deleteCheckbox'); + var preview_container = $(id + '-previewContainer'); + $status.removeClass('text-success').removeClass('text-danger'); + $(status + ' .goodfile').remove(); + $(status + ' .badfile').remove(); + $(status + ' .previewSize').hide(); + preview_container.hide(); + $(id + '-info').html(''); + var max_size = $this.data('maxsize'); + var total_size = 0; + for (var i = 0; i < this.files.length; i++) { + total_size += this.files[i].size; + $(id + '-info').append('' + htmlEntities(this.files[i].name) + ' (' + formatBytes(this.files[i].size) + ') '); + } + if (total_size > max_size) { + $status.addClass('text-danger').removeClass('help-block').prepend(' ').append(' Upload is ' + formatBytes(total_size) + '.'); + } else { + $status.addClass('text-success').removeClass('help-block').prepend(' '); + var $preview = $(id + '-imagePreview'); + readURL(this, $preview); + $preview.fadeIn(); + preview_container.fadeIn(); + delete_id.hide(); + } + }); +}); +function htmlEntities(str) { + return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +} + +/** + * Toggle disabled + */ +(function ($) { + $.fn.toggleDisabled = function (callback) { + return this.each(function () { + var disabled, + $this = $(this); + if ($this.attr('disabled')) { + $this.removeAttr('disabled'); + disabled = false; + } else { + $this.attr('disabled', 'disabled'); + disabled = true; + } + if (callback && typeof callback === 'function') { + callback(this, disabled); + } + }); + }; +})(jQuery); + +/** + * Universal Livewire Select2 integration + * + * How to use: + * + * 1. Set the class of your select2 elements to 'livewire-select2'). + * 2. Name your element to match a property in your Livewire component + * 3. Add an attribute called 'data-livewire-component' that points to $this->getId() (via `{{ }}` if you're in a blade, + * or just $this->getId() if not). + */ +document.addEventListener('livewire:init', function () { + $('.livewire-select2').select2(); + $(document).on('select2:select', '.livewire-select2', function (event) { + var target = $(event.target); + if (!event.target.name || !target.data('livewire-component')) { + console.error("You need to set both name (which should match a Livewire property) and data-livewire-component on your Livewire-ed select2 elements!"); + console.error("For data-livewire-component, you probably want to use $this->getId() or {{ $this->getId() }}, as appropriate"); + return false; + } + Livewire.find(target.data('livewire-component')).set(event.target.name, this.options[this.selectedIndex].value); + }); + Livewire.hook('request', function (_ref) { + var succeed = _ref.succeed; + succeed(function () { + queueMicrotask(function () { + $('.livewire-select2').select2(); + }); + }); + }); +}); + +/***/ }), + +/***/ "./resources/assets/js/snipeit_modals.js": +/*!***********************************************!*\ + !*** ./resources/assets/js/snipeit_modals.js ***! + \***********************************************/ +/***/ (() => { + +/* + * + * Snipe-IT Universal Modal support + * + * Enables modal dialogs to create sub-resources throughout Snipe-IT + * + */ + +/* +HOW TO USE + Create a Button looking like this: + New + If you don't have access to Blade commands (like {{ and }}, etc), you can hard-code a URL as the 'href' + data-toggle="modal" - required for Bootstrap Modals +data-target="#createModal" - fixed ID for the modal, do not change +data-select="assigned_to" - What is the *ID* of the select-dropdown that you're going to be adding to, if the modal-create was a success? Be on the lookout for duplicate ID's, it will confuse this library! +class="btn btn-sm btn-primary" - makes it look button-ey, feel free to change :) + +If you want to pass additional variables to the modal (In the Category Create one, for example, you can pass category_id), you can encode them as URL variables in the href + +*/ + +$(function () { + var baseUrl = $('meta[name="baseUrl"]').attr('content'); + //handle modal-add-interstitial calls + var model, select, refreshSelector; + if ($('#createModal').length == 0) { + $('body').append(''); + } + $('#createModal').on("show.bs.modal", function (event) { + var link = $(event.relatedTarget); + model = link.data("dependency"); + select = link.data("select"); + refreshSelector = link.data("refresh"); + $('#createModal').load(link.attr('href'), function () { + // this sets the focus to be the name field + $('#modal-name').focus(); + + //do we need to re-select2 this, after load? Probably. + $('#createModal').find('select.select2').select2(); + // Initialize the ajaxy select2 with images. + // This is a copy/paste of the code from snipeit.js, would be great to only have this in one place. + + $('.js-data-ajax').each(function (i, item) { + var link = $(item); + var endpoint = link.data("endpoint"); + var select = link.data("select"); + link.select2({ + ajax: { + // the baseUrl includes a trailing slash + url: baseUrl + 'api/v1/' + endpoint + '/selectlist', + //WARNING - we're hoping that's defined on the page somewhere... + dataType: 'json', + delay: 250, + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + }, + data: function data(params) { + var data = { + search: params.term, + page: params.page || 1, + assetStatusType: link.data("asset-status-type") + }; + return data; + }, + /*processResults: function (data, params) { + params.page = params.page || 1; + var answer = { + results: data.items, + pagination: { + more: data.pagination.more + } + }; + return answer; + },*/ + cache: true + }, + //escapeMarkup: function (markup) { return markup; }, // let our custom formatter work + templateResult: formatDatalistSafe + //templateSelection: formatDataSelection + }); + }); + }); + }); + $('#createModal').on('click', '#modal-save', function () { + $.ajax({ + type: 'POST', + url: $('.modal-body form').attr('action'), + headers: { + "X-Requested-With": 'XMLHttpRequest', + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + }, + data: $('.modal-body form').serialize(), + success: function success(result) { + if (result.status == "error") { + var error_message = ""; + for (var field in result.messages) { + error_message += "
  • Problem(s) with field " + field + ": " + result.messages[field]; + } + $('#modal_error_msg').html(error_message).show(); + return false; + } + var id = result.payload.id; + var name = result.payload.name || result.payload.first_name + " " + result.payload.last_name; + if (!id || !name) { + console.error("Could not find resulting name or ID from modal-create. Name: " + name + ", id: " + id); + return false; + } + $('#createModal').modal('hide'); + $('#createModal').html(""); + var refreshTable = $('#' + refreshSelector); + if (refreshTable.length > 0) { + refreshTable.bootstrapTable('refresh'); + } + + // "select" is the original drop-down menu that someone + // clicked 'add' on to add a new 'thing' + // this code adds the newly created object to that select + var selector = document.getElementById(select); + if (!selector) { + return false; + } + selector.options[selector.length] = new Option(name, id); + selector.selectedIndex = selector.length - 1; + $(selector).trigger("change"); + if (window.fetchCustomFields) { + fetchCustomFields(); + } + }, + error: function error(result) { + msg = result.responseJSON.messages || result.responseJSON.error; + $('#modal_error_msg').html("Server Error: " + msg).show(); + } + }); + }); +}); +function formatDatalistSafe(datalist) { + // console.warn("What in the hell is going on with Select2?!?!!?!?"); + // console.warn($.select2); + if (datalist.loading) { + return $(' Loading...'); + } + var root_div = $("
    "); + var left_pull = $("
    "); + if (datalist.image) { + var inner_div = $("
    "); + /****************************************************************** + * + * We are specifically chosing empty alt-text below, because this + * image conveys no additional information, relative to the text + * that will *always* be there in any select2 list that is in use + * in Snipe-IT. If that changes, we would probably want to change + * some signatures of some functions, but right now, we don't want + * screen readers to say "HP SuperJet 5000, .... picture of HP + * SuperJet 5000..." and so on, for every single row in a list of + * assets or models or whatever. + * + *******************************************************************/ + var img = $(""); + // console.warn("Img is: "); + // console.dir(img); + // console.warn("Strigularly, that's: "); + // console.log(img); + img.attr("src", datalist.image); + inner_div.append(img); + } else { + var inner_div = $("
    "); + } + left_pull.append(inner_div); + root_div.append(left_pull); + var name_div = $("
    "); + name_div.text(datalist.text); + root_div.append(name_div); + var safe_html = root_div.get(0).outerHTML; + var old_html = formatDatalist(datalist); + if (safe_html != old_html) { + // console.log("HTML MISMATCH: "); + // console.log("FormatDatalistSafe: "); + // console.dir(root_div.get(0)); + // console.log(safe_html); + // console.log("FormatDataList: "); + // console.log(old_html); + } + return root_div; +} +function formatDatalist(datalist) { + var loading_markup = ' Loading...'; + if (datalist.loading) { + return loading_markup; + } + var markup = "
    "; + markup += "
    "; + if (datalist.image) { + markup += "
    " + datalist.tex + "
    "; + } else { + markup += "
    "; + } + markup += "
    " + datalist.text + "
    "; + markup += "
    "; + return markup; +} +function formatDataSelection(datalist) { + return datalist.text.replace(/>/g, '>').replace(/ { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }), + +/***/ "./resources/assets/less/overrides.less": +/*!**********************************************!*\ + !*** ./resources/assets/less/overrides.less ***! + \**********************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }), + +/***/ "./resources/assets/less/skins/_all-skins.less": +/*!*****************************************************!*\ + !*** ./resources/assets/less/skins/_all-skins.less ***! + \*****************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }), + +/***/ "./resources/assets/less/skins/skin-black-dark.less": +/*!**********************************************************!*\ + !*** ./resources/assets/less/skins/skin-black-dark.less ***! + \**********************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + /***/ }), /***/ "./resources/assets/less/skins/skin-black.less": @@ -92030,71 +92095,6 @@ __webpack_require__.r(__webpack_exports__); // extracted by mini-css-extract-plugin -/***/ }), - -/***/ "./node_modules/admin-lte/build/less/AdminLTE.less": -/*!*********************************************************!*\ - !*** ./node_modules/admin-lte/build/less/AdminLTE.less ***! - \*********************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./resources/assets/less/app.less": -/*!****************************************!*\ - !*** ./resources/assets/less/app.less ***! - \****************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./resources/assets/less/overrides.less": -/*!**********************************************!*\ - !*** ./resources/assets/less/overrides.less ***! - \**********************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./resources/assets/less/skins/_all-skins.less": -/*!*****************************************************!*\ - !*** ./resources/assets/less/skins/_all-skins.less ***! - \*****************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./resources/assets/less/skins/skin-black-dark.less": -/*!**********************************************************!*\ - !*** ./resources/assets/less/skins/skin-black-dark.less ***! - \**********************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - /***/ }) /******/ }); diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 84f3b91e8d..8b34c64378 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,9 +1,9 @@ { - "/js/build/app.js": "/js/build/app.js?id=65d7af7b9fa7fd0e05737526a0d1d282", + "/js/build/app.js": "/js/build/app.js?id=607de09b70b83ef82a427e4b36341682", "/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=06c13e817cc022028b3f4a33c0ca303a", "/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=79aa889a1a6691013be6c342ca7391cd", - "/css/build/overrides.css": "/css/build/overrides.css?id=6528155ed5ed8fddf4047de7f0d0298d", - "/css/build/app.css": "/css/build/app.css?id=3422f2ca2056b952c3c361adf00c10b8", + "/css/build/overrides.css": "/css/build/overrides.css?id=f188c07f91348503bc38f4b6683993aa", + "/css/build/app.css": "/css/build/app.css?id=7f6827b6d2149b8eab327d00e2dd66f0", "/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9", "/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690", "/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=ea22079836a432d7f46a5d390c445e13", @@ -19,7 +19,7 @@ "/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=a82b065847bf3cd5d713c04ee8dc86c6", "/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=6ea836d8126de101081c49abbdb89417", "/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb", - "/css/dist/all.css": "/css/dist/all.css?id=18ebb9c284b49dcf6c8e4fdb923ad923", + "/css/dist/all.css": "/css/dist/all.css?id=fe65a4b6cd3acaf2737dec72b98b650b", "/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7", "/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7", "/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde", @@ -111,5 +111,5 @@ "/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=ceded08e0cc745a83c13647035b03406", "/js/build/vendor.js": "/js/build/vendor.js?id=89dffa552c6e3abe3a2aac6c9c7b466b", "/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=61285c8ac5ea7b46002ea8c451c94e60", - "/js/dist/all.js": "/js/dist/all.js?id=21e041dec60e0785db6d64961b13a9b0" + "/js/dist/all.js": "/js/dist/all.js?id=cae553daff19b328b3ba51a62f891442" } diff --git a/resources/assets/less/overrides.less b/resources/assets/less/overrides.less index 67ee7030e1..bbbb74ec88 100644 --- a/resources/assets/less/overrides.less +++ b/resources/assets/less/overrides.less @@ -1163,4 +1163,15 @@ input[type="radio"]:checked::before { /** this is needed to override ekko-lightboxes card view styles **/ .bootstrap-table .fixed-table-container .table tbody tr .card-view { display: table-row !important; -} \ No newline at end of file +} + +td.text-right.text-padding-number-cell { + padding-right: 30px !important; + white-space: nowrap; +} + +th.text-right.text-padding-number-footer-cell { + padding-right: 20px !important; + white-space: nowrap; +} + diff --git a/resources/views/accessories/index.blade.php b/resources/views/accessories/index.blade.php index a3ea41db27..1c82d782fa 100755 --- a/resources/views/accessories/index.blade.php +++ b/resources/views/accessories/index.blade.php @@ -34,6 +34,7 @@ data-show-refresh="true" data-show-footer="true" data-sort-order="asc" + data-footer-style="footerStyle" id="accessoriesTable" class="table table-striped snipe-table" data-url="{{route('api.accessories.index') }}" diff --git a/resources/views/components/index.blade.php b/resources/views/components/index.blade.php index 418852095b..34f4c9babb 100644 --- a/resources/views/components/index.blade.php +++ b/resources/views/components/index.blade.php @@ -25,6 +25,7 @@ data-id-table="componentsTable" data-search="true" data-side-pagination="server" + data-footer-style="footerStyle" data-show-columns="true" data-show-fullscreen="true" data-show-export="true" diff --git a/resources/views/consumables/index.blade.php b/resources/views/consumables/index.blade.php index e2235e86e3..08582e557c 100644 --- a/resources/views/consumables/index.blade.php +++ b/resources/views/consumables/index.blade.php @@ -27,6 +27,7 @@ data-id-table="consumablesTable" data-search="true" data-side-pagination="server" + data-footer-style="footerStyle" data-show-columns="true" data-show-export="true" data-show-fullscreen="true" diff --git a/resources/views/licenses/index.blade.php b/resources/views/licenses/index.blade.php index 82e34b1909..94d94d1d45 100755 --- a/resources/views/licenses/index.blade.php +++ b/resources/views/licenses/index.blade.php @@ -33,6 +33,7 @@ data-pagination="true" data-search="true" data-side-pagination="server" + data-footer-style="footerStyle" data-show-columns="true" data-show-fullscreen="true" data-show-export="true" diff --git a/resources/views/models/index.blade.php b/resources/views/models/index.blade.php index 6f3f5e78df..5ae3fb9450 100755 --- a/resources/views/models/index.blade.php +++ b/resources/views/models/index.blade.php @@ -45,6 +45,7 @@ data-search="true" data-show-footer="true" data-side-pagination="server" + data-footer-style="footerStyle" data-show-columns="true" data-toolbar="#modelsBulkEditToolbar" data-bulk-button-id="#bulkModelsEditButton" diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index 2da7b63b1d..b9e02bbbda 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -13,6 +13,7 @@