diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 89704242ba..df498da8c9 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -1092,6 +1092,15 @@ class Helper return $file_name; } + + /** + * Universal helper to show file size in human-readable formats + * + * @author A. Gianotto + * @since 5.0 + * + * @return string[] + */ public static function formatFilesizeUnits($bytes) { if ($bytes >= 1073741824) @@ -1121,30 +1130,56 @@ class Helper return $bytes; } + + /** + * This is weird but used by the side nav to determine which URL to point the user to + * + * @author A. Gianotto + * @since 5.0 + * + * @return string[] + */ public static function SettingUrls(){ $settings=['#','fields.index', 'statuslabels.index', 'models.index', 'categories.index', 'manufacturers.index', 'suppliers.index', 'departments.index', 'locations.index', 'companies.index', 'depreciations.index']; return $settings; } - public static function AgeFormat($date) { - $year = Carbon::parse($date) - ->diff(now())->y; - $month = Carbon::parse($date) - ->diff(now())->m; - $days = Carbon::parse($date) - ->diff(now())->d; - $age=''; - if ($year) { - $age .= $year.'y '; - } - if ($month) { - $age .= $month.'m '; - } - if ($days) { - $age .= $days.'d'; - } - return $age; + + /** + * Generic helper (largely used by livewire right now) that returns the font-awesome icon + * for the object type. + * + * @author A. Gianotto + * @since 6.1.0 + * + * @return string + */ + public static function iconTypeByItem($item) { + + switch ($item) { + case 'asset': + return 'fas fa-barcode'; + break; + case 'accessory': + return 'fas fa-keyboard'; + break; + case 'component': + return 'fas fa-hdd'; + break; + case 'consumable': + return 'fas fa-tint'; + break; + case 'license': + return 'far fa-save'; + break; + case 'location': + return 'fas fa-map-marker-alt'; + break; + case 'user': + return 'fas fa-user'; + break; + } } diff --git a/app/Http/Controllers/ImportsController.php b/app/Http/Controllers/ImportsController.php deleted file mode 100644 index 5c2ca6175b..0000000000 --- a/app/Http/Controllers/ImportsController.php +++ /dev/null @@ -1,22 +0,0 @@ -authorize('import'); - $imports = (new ImportsTransformer)->transformImports(Import::latest()->get()); - - return view('importer/import')->with('imports', $imports); - } -} diff --git a/app/Http/Livewire/Importer.php b/app/Http/Livewire/Importer.php new file mode 100644 index 0000000000..31bbfcfbc7 --- /dev/null +++ b/app/Http/Livewire/Importer.php @@ -0,0 +1,322 @@ + 'required|string', + 'files.*.created_at' => 'required|string', + 'files.*.filesize' => 'required|integer', + 'activeFile' => 'Import', + 'activeFile.import_type' => 'string', + 'activeFile.field_map' => 'array', + 'activeFile.header_row' => 'array', + 'field_map' => 'array' + ]; + + public function generate_field_map() + { + \Log::debug("header row is: ".print_r($this->activeFile->header_row,true)); + \Log::debug("Field map is: ".print_r($this->field_map,true)); + $tmp = array_combine($this->activeFile->header_row, $this->field_map); + return json_encode(array_filter($tmp)); + } + + // all of these 'statics', alas, may have to change to something else to handle translations? + // I'm not sure. Maybe I use them to 'populate' the translations? TBH, I don't know yet. + static $general = [ + 'category' => 'Category', + 'company' => 'Company', + 'email' => 'Email', + 'item_name' => 'Item Name', + 'location' => 'Location', + 'maintained' => 'Maintained', + 'manufacturer' => 'Manufacturer', + 'notes' => 'Notes', + 'order_number' => 'Order Number', + 'purchase_cost' => 'Purchase Cost', + 'purchase_date' => 'Purchase Date', + 'quantity' => 'Quantity', + 'requestable' => 'Requestable', + 'serial' => 'Serial Number', + 'supplier' => 'Supplier', + 'username' => 'Username', + 'department' => 'Department', + ]; + + static $accessories = [ + 'model_number' => 'Model Number', + ]; + + static $assets = [ + 'asset_tag' => 'Asset Tag', + 'asset_model' => 'Model Name', + 'byod' => 'BYOD', + 'checkout_class' => 'Checkout Type', + 'checkout_location' => 'Checkout Location', + 'image' => 'Image Filename', + 'model_number' => 'Model Number', + 'full_name' => 'Full Name', + 'status' => 'Status', + 'warranty_months' => 'Warranty Months', + ]; + + static $consumables = [ + 'item_no' => "Item Number", + 'model_number' => "Model Number", + 'min_amt' => "Minimum Quantity", + ]; + + static $licenses = [ + 'asset_tag' => 'Assigned To Asset', + 'expiration_date' => 'Expiration Date', + 'full_name' => 'Full Name', + 'license_email' => 'Licensed To Email', + 'license_name' => 'Licensed To Name', + 'purchase_order' => 'Purchase Order', + 'reassignable' => 'Reassignable', + 'seats' => 'Seats', + ]; + + static $users = [ + 'employee_num' => 'Employee Number', + 'first_name' => 'First Name', + 'jobtitle' => 'Job Title', + 'last_name' => 'Last Name', + 'phone_number' => 'Phone Number', + 'manager_first_name' => 'Manager First Name', + 'manager_last_name' => 'Manager Last Name', + 'activated' => 'Activated', + 'address' => 'Address', + 'city' => 'City', + 'state' => 'State', + 'country' => 'Country', + 'vip' => 'VIP' + ]; + + //array of "real fieldnames" to a list of aliases for that field + static $aliases = [ + 'model_number' => + [ + 'model', + 'model no', + 'model no.', + 'model number', + 'model num', + 'model num.' + ], + 'warranty_months' => + [ + 'Warranty', + 'Warranty Months' + ], + 'qty' => + [ + 'QTY', + 'Quantity' + ], + 'min_amt' => + [ + 'Min Amount', + 'Min QTY' + ], + 'next_audit_date' => + [ + 'Next Audit', + ], + + + ]; + + private function getColumns($type) + { + switch ($type) { + case 'asset': + $results = self::$general + self::$assets; + break; + case 'accessory': + $results = self::$general + self::$accessories; + break; + case 'consumable': + $results = self::$general + self::$consumables; + break; + case 'license': + $results = self::$general + self::$licenses; + break; + case 'user': + $results = self::$general + self::$users; + break; + default: + $results = self::$general; + } + asort($results, SORT_FLAG_CASE | SORT_STRING); + if ($type == "asset") { + // add Custom Fields after a horizontal line + $results['-'] = "———" . trans('admin/custom_fields/general.custom_fields') . "———’"; + foreach (CustomField::orderBy('name')->get() as $field) { + $results[$field->db_column_name()] = $field->name; + } + } + return $results; + } + + public function updating($name, $new_import_type) + { + if ($name == "activeFile.import_type") { + \Log::info("WE ARE CHANGING THE import_type!!!!! TO: " . $new_import_type); + \Log::info("so, what's \$this->>field_map at?: " . print_r($this->field_map, true)); + // go through each header, find a matching field to try and map it to. + foreach ($this->activeFile->header_row as $i => $header) { + // do we have something mapped already? + if (array_key_exists($i, $this->field_map)) { + // yes, we do. Is it valid for this type of import? + // (e.g. the import type might have been changed...?) + if (array_key_exists($this->field_map[$i], $this->columnOptions[$new_import_type])) { + //yes, this key *is* valid. Continue on to the next field. + continue; + } else { + //no, this key is *INVALID* for this import type. Better set it to null + // and we'll hope that the aliases or something else picks it up. + $this->field_map[$i] = null; // fingers crossed! But it's not likely, tbh. + } // TODO - strictly speaking, this isn't necessary here I don't think. + } + // first, check for exact matches + foreach ($this->columnOptions[$new_import_type] as $value => $text) { + if (strcasecmp($text, $header) === 0) { // case-INSENSITIVe on purpose! + $this->field_map[$i] = $value; + continue 2; //don't bother with the alias check, go to the next header + } + } + // if you got here, we didn't find a match. Try the aliases + foreach (self::$aliases as $key => $alias_values) { + foreach ($alias_values as $alias_value) { + if (strcasecmp($alias_value, $header) === 0) { // aLsO CaSe-INSENSitiVE! + // Make *absolutely* sure that this key actually _exists_ in this import type - + // you can trigger this by importing accessories with a 'Warranty' column (which don't exist + // in "Accessories"!) + if (array_key_exists($key, $this->columnOptions[$new_import_type])) { + $this->field_map[$i] = $key; + continue 3; // bust out of both of these loops; as well as the surrounding one - e.g. move on to the next header + } + } + } + } + // and if you got here, we got nothing. Let's recommend 'null' + $this->field_map[$i] = null; // Booooo :( + } + } + } + + public function boot() { // FIXME - delete or undelete. + ///////$this->activeFile = null; // I do *not* understand why I have to do this, but, well, whatever. + } + + + public function mount() + { + $this->authorize('import'); + $this->progress = -1; // '-1' means 'don't show the progressbar' + $this->progress_bar_class = 'progress-bar-warning'; + \Log::info("Hey, we are calling MOUNT (in the importer-file) !!!!!!!!"); //fcuk + $this->importTypes = [ + 'asset' => trans('general.assets'), + 'accessory' => trans('general.accessories'), + 'consumable' => trans('general.consumables'), + 'component' => trans('general.components'), + 'license' => trans('general.licenses'), + 'user' => trans('general.users'), + ]; + + $this->columnOptions[''] = $this->getColumns(''); //blank mode? I don't know what this is supposed to mean + foreach($this->importTypes AS $type => $name) { + $this->columnOptions[$type] = $this->getColumns($type); + } + if ($this->activeFile) { + $this->field_map = $this->activeFile->field_map ? array_values($this->activeFile->field_map) : []; + } + } + + public function selectFile($id) + { + \Log::info("TOGGLE EVENT FIRED!"); + \Log::error("The ID we are trying to find is AS FOLLOWS: ".$id); + $this->activeFile = Import::find($id); + $this->field_map = null; + foreach($this->activeFile->header_row as $element) { + if(isset($this->activeFile->field_map[$element])) { + $this->field_map[] = $this->activeFile->field_map[$element]; + } else { + $this->field_map[] = null; // re-inject the 'nulls' if a file was imported with some 'Do Not Import' settings + } + } + //$this->field_map = $this->activeFile->field_map ? array_values($this->activeFile->field_map) : []; // this is wrong + $this->file_id = $id; + $this->import_errors = null; + $this->statusText = null; + \Log::error("The import type we are about to try and load up is gonna be this: ".$this->activeFile->import_type); + + } + + public function destroy($id) + { + // TODO: why don't we just do File::find($id)? This seems dumb. + foreach($this->files as $file) { + \Log::debug("File id is: ".$file->id); + if($id == $file->id) { + if(Storage::delete('private_uploads/imports/'.$file->file_path)) { + $file->delete(); + + $this->message = trans('admin/hardware/message.import.file_delete_success'); + $this->message_type = 'success'; + return; + } else { + $this->message = trans('admin/hardware/message.import.file_delete_error'); + $this->message_type = 'danger'; + } + } + } + } + + public function render() + { + $this->files = Import::orderBy('id','desc')->get(); //HACK - slows down renders. + return view('livewire.importer') + ->extends('layouts.default') + ->section('content'); + } +} diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php index 740e905d8d..d431ec890d 100644 --- a/app/Http/Transformers/AssetsTransformer.php +++ b/app/Http/Transformers/AssetsTransformer.php @@ -84,7 +84,7 @@ class AssetsTransformer 'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date, 'date'), 'deleted_at' => Helper::getFormattedDateObject($asset->deleted_at, 'datetime'), 'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'), - 'age' => $asset->purchase_date ? Helper::AgeFormat($asset->purchase_date) : '', + 'age' => $asset->purchase_date ? $asset->purchase_date->diffForHumans() : '', 'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'), 'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'), 'purchase_cost' => Helper::formatCurrencyOutput($asset->purchase_cost), diff --git a/app/Models/CustomField.php b/app/Models/CustomField.php index 83fa93ec56..fcab5b25ff 100644 --- a/app/Models/CustomField.php +++ b/app/Models/CustomField.php @@ -238,7 +238,7 @@ class CustomField extends Model * * @author [A. Gianotto] [] * @since [v3.0] - * @return \Illuminate\Database\Eloquent\Relations\Relation + * @return string */ public function db_column_name() { diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 90520738d0..d5b10bde3a 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -39,7 +39,7 @@ class RouteServiceProvider extends ServiceProvider { Route::group([ 'middleware' => 'web', - 'namespace' => $this->namespace, +// 'namespace' => $this->namespace, //okay, I don't know what this means, but somehow this might be a problem for us? ], function ($router) { require base_path('routes/web/hardware.php'); require base_path('routes/web/models.php'); @@ -65,7 +65,7 @@ class RouteServiceProvider extends ServiceProvider { Route::group([ 'middleware' => 'auth:api', - 'namespace' => $this->namespace, +// 'namespace' => $this->namespace, // this might also be a problem? I don't really know :/ 'prefix' => 'api', ], function ($router) { require base_path('routes/api.php'); diff --git a/public/js/build/app.js b/public/js/build/app.js index c9e1d08a17..3c65e5fa81 100644 Binary files a/public/js/build/app.js and b/public/js/build/app.js differ diff --git a/public/js/dist/all.js b/public/js/dist/all.js index e948c91469..f8861c6b0a 100644 Binary files a/public/js/dist/all.js and b/public/js/dist/all.js differ diff --git a/public/mix-manifest.json b/public/mix-manifest.json index f0fc15863a..0e447acd4e 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,5 +1,5 @@ { - "/js/build/app.js": "/js/build/app.js?id=da037f537476ebca094531163cb611f5", + "/js/build/app.js": "/js/build/app.js?id=bcb572126085fb7637accdcff1e0c0c6", "/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374", "/css/build/overrides.css": "/css/build/overrides.css?id=d9175e3d9b9074397343dddebfe23888", "/css/build/app.css": "/css/build/app.css?id=dcb8aa9f4501a370214a67442e88daf0", @@ -34,7 +34,7 @@ "/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=ee4896df8b8f008ce73a9a0c2549aefd", "/js/build/vendor.js": "/js/build/vendor.js?id=47ecbb4bb3b0e02315f391caadbdf971", "/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=14d9a2affec7b066d20fcba2e6e67ad2", - "/js/dist/all.js": "/js/dist/all.js?id=758f256419ccaf4b4266da3bbc742b0b", + "/js/dist/all.js": "/js/dist/all.js?id=eb7becb7a5a2ebf0dae7926190d95832", "/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397", "/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=c0d21166315b7c2cdd4819fa4a5e4d1e", "/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb", diff --git a/resources/assets/js/components/alert.vue b/resources/assets/js/components/alert.vue deleted file mode 100644 index 1a20334656..0000000000 --- a/resources/assets/js/components/alert.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - diff --git a/resources/assets/js/components/importer/importer-errors.vue b/resources/assets/js/components/importer/importer-errors.vue deleted file mode 100644 index adc6841d4d..0000000000 --- a/resources/assets/js/components/importer/importer-errors.vue +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/resources/assets/js/components/importer/importer-file.vue b/resources/assets/js/components/importer/importer-file.vue deleted file mode 100644 index 8541f7c58d..0000000000 --- a/resources/assets/js/components/importer/importer-file.vue +++ /dev/null @@ -1,327 +0,0 @@ - - - diff --git a/resources/assets/js/components/importer/importer.vue b/resources/assets/js/components/importer/importer.vue deleted file mode 100644 index 474f8f1451..0000000000 --- a/resources/assets/js/components/importer/importer.vue +++ /dev/null @@ -1,130 +0,0 @@ - - - diff --git a/resources/assets/js/snipeit.js b/resources/assets/js/snipeit.js index 6d5340c80f..95fb268657 100755 --- a/resources/assets/js/snipeit.js +++ b/resources/assets/js/snipeit.js @@ -605,3 +605,54 @@ function htmlEntities(str) { }; })(jQuery); + +/** + * Universal Livewire Select2 and iCheck integration + * + * How to use: + * + * 1. Set the class of your select2 elements to 'livewire-select2' and your icheck elements to 'livewire-icheck' (as appropriate). + * (For iCheck, you may still need to apply the other iCheck classes like 'minimal' or 'iCheck') + * 2. Name your element to match a property in your Livewire component + * 3. Add an attribute called 'data-livewire-component' that points to $_instance->id (via `{{ }}` if you're in a blade, + * or just $_instance->id if not). + * 4. For iCheck, you need to wrap the 'checkbox' element with wire:ignore - perhaps in the