Integrate ajax select2 menus in all asset checkouts

This commit is contained in:
snipe 2017-10-26 02:28:17 -07:00
parent 75b527ab59
commit 82690e1fd7
17 changed files with 89958 additions and 198 deletions

View file

@ -263,6 +263,61 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
} }
/**
* Display a formatted JSON response for the select2 menus
*
* @todo: create a transformer for handling these responses
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @return \Illuminate\Http\Response
*/
public function selectlist(Request $request)
{
$this->authorize('view', Asset::class);
$assets = Company::scopeCompanyables(Asset::select([
'assets.id',
'assets.name',
'assets.asset_tag',
'assets.model_id',
]))->with('model')->RTD();
if ($request->has('search')) {
$assets = $assets->where('assets.name', 'LIKE', '%'.$request->get('search').'%')
->orWhere('assets.asset_tag', 'LIKE', '%'.$request->get('search').'%')
->join('models AS assets_models',function ($join) use ($request) {
$join->on('assets_models.id', "=", "assets.model_id");
})->orWhere('assets_models.name','LIKE','%'.$request->get('search').'%');
}
$assets = $assets->paginate(50);
$assets_array = [];
foreach ($assets as $asset) {
$assets_array[] =
[
'id' => (int) $asset->id,
'text' => e($asset->present()->fullName),
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
];
}
$results = [
'items' => $assets_array,
'pagination' =>
[
'more' => ($assets->currentPage() >= $assets->lastPage()) ? false : true,
'per_page' => $assets->perPage()
],
'total_count' => $assets->total(),
'page' => $assets->currentPage(),
'page_count' => $assets->lastPage()
];
return $results;
}
/** /**
* Accepts a POST request to create a new asset * Accepts a POST request to create a new asset

View file

@ -139,4 +139,55 @@ class LocationsController extends Controller
$location->delete(); $location->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/locations/message.delete.success'))); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/locations/message.delete.success')));
} }
/**
* Display a formatted JSON response for the select2 menus
*
* @todo: create a transformer for handling these responses
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @return \Illuminate\Http\Response
*/
public function selectlist(Request $request)
{
$this->authorize('view', Location::class);
$locations = Location::select([
'locations.id',
'locations.name',
'locations.image',
]);
if ($request->has('search')) {
$locations = $locations->where('locations.name', 'LIKE', '%'.$request->get('search').'%');
}
$locations = $locations->paginate(50);
$locations_array = [];
foreach ($locations as $location) {
$locations_array[] =
[
'id' => (int) $location->id,
'text' => e($location->name),
'image' => ($location->image) ? url('/').'/uploads/locations/'.$location->image : null,
];
}
$results = [
'items' => $locations_array,
'pagination' =>
[
'more' => ($locations->currentPage() >= $locations->lastPage()) ? false : true,
'per_page' => $locations->perPage()
],
'total_count' => $locations->total(),
'page' => $locations->currentPage(),
'page_count' => $locations->lastPage()
];
return $results;
}
} }

View file

@ -273,14 +273,16 @@ class AssetPresenter extends Presenter
**/ **/
public function name() public function name()
{ {
if (empty($this->name)) {
if (isset($this->model)) { if (empty($this->model->name)) {
return $this->model->name.' ('.$this->asset_tag.')'; if (isset($this->model->model)) {
return $this->model->model->name.' ('.$this->model->asset_tag.')';
} }
return $this->asset_tag; return $this->model->asset_tag;
} else { } else {
return $this->name.' ('.$this->asset_tag.')'; return $this->model->name . ' (' . $this->model->asset_tag . ')';
} }
} }
/** /**
@ -289,7 +291,18 @@ class AssetPresenter extends Presenter
*/ */
public function fullName() public function fullName()
{ {
return $this->name(); $str = '';
if ($this->model->name) {
$str .= $this->name;
}
if ($this->asset_tag) {
$str .= ' ('.$this->model->asset_tag.')';
}
if ($this->model->model) {
$str .= ' - '.$this->model->model->name;
}
return $str;
} }
/** /**
* Returns the date this item hits EOL. * Returns the date this item hits EOL.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4377
public/css/dist/all.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8692
public/js/dist/all.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,14 +1,10 @@
{ {
"/js/build/vue.js": "/js/build/vue.js?id=e6804371942215bd1d7d", "/js/build/vue.js": "/js/build/vue.js?id=e9504cad01a748f9b0fa",
"/css/AdminLTE.css": "/css/AdminLTE.css?id=b8be19a285eaf44eec37", "/css/AdminLTE.css": "/css/AdminLTE.css?id=889dc040f2ddfca6efde",
"/css/app.css": "/css/app.css?id=407edb63cc6b6dc62405", "/css/app.css": "/css/app.css?id=3a1e8c168fa8714043a6",
"/css/overrides.css": "/css/overrides.css?id=9ae1a3c861441320c5a1", "/css/overrides.css": "/css/overrides.css?id=3911514a8a64a4247483",
"/js/build/vue.js.map": "/js/build/vue.js.map?id=3b3d417664a61dcce3e9", "/css/dist/all.css": "/css/dist/all.css?id=f2d4896e67e878a47434",
"/css/AdminLTE.css.map": "/css/AdminLTE.css.map?id=99f5a5a03c4155cf69f6", "/js/dist/all.js": "/js/dist/all.js?id=b967aad5fdaca0a91359",
"/css/app.css.map": "/css/app.css.map?id=bdbe05e6ecd70ccfac72", "/css/build/all.css": "/css/build/all.css?id=f2d4896e67e878a47434",
"/css/overrides.css.map": "/css/overrides.css.map?id=898c91d4a425b01b589b", "/js/build/all.js": "/js/build/all.js?id=b967aad5fdaca0a91359"
"/css/dist/all.css": "/css/dist/all.css?id=7c3842d2639193ac7e88",
"/js/dist/all.js": "/js/dist/all.js?id=f14abfc2506d42ffb0f5",
"/css/build/all.css": "/css/build/all.css?id=7c3842d2639193ac7e88",
"/js/build/all.js": "/js/build/all.js?id=f14abfc2506d42ffb0f5"
} }

View file

@ -180,14 +180,78 @@ $(document).ready(function () {
} }
$('.datepicker').datepicker(); $('.datepicker').datepicker();
$(document).ready(function() { var datepicker = $.fn.datepicker.noConflict(); // return $.fn.datepicker to previously assigned value
$("#toggle_nav").toggle(function() { $.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({
ajax: {
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 (params) {
var data = {
search: params.term,
page: params.page || 1
};
return data;
},
processResults: function (data, params) {
params.page = params.page || 1;
var answer = {
results: data.items,
pagination: {
more: "true" //(params.page < data.page_count)
}
};
return answer;
},
cache: true
},
escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
templateResult: formatDatalist,
templateSelection: formatDataSelection
}); });
}); });
function formatDatalist (datalist) {
var loading_markup = '<i class="fa fa-spinner fa-spin" aria-hidden="true"></i> Loading...';
if (datalist.loading) {
return loading_markup;
}
var markup = "<div class='clearfix'>" ;
markup +="<div class='pull-left' style='padding-right: 10px;'>";
if (datalist.image) {
markup += "<img src='" + datalist.image + "' style='max-height: 20px'>";
} else {
markup += "<div style='height: 20px; width: 20px;'></div>";
}
markup += "</div><div>" + datalist.text + "</div>";
markup += "</div>";
return markup;
}
function formatDataSelection (datalist) {
return datalist.text;
}

View file

@ -95,10 +95,14 @@
// clicked 'add' on to add a new 'thing' // clicked 'add' on to add a new 'thing'
// this code adds the newly created object to that select // this code adds the newly created object to that select
var selector = document.getElementById(select); var selector = document.getElementById(select);
console.warn("The selector we should've selecte dis: "+select);
console.dir(selector);
if(!selector) { if(!selector) {
return false; return false;
} }
console.warn("onChange Selector Thing should've activated? Here's the selector");
console.dir(selector);
selector.options[selector.length] = new Option(name, id); selector.options[selector.length] = new Option(name, id);
selector.selectedIndex = selector.length - 1; selector.selectedIndex = selector.length - 1;
$(selector).trigger("change"); $(selector).trigger("change");

View file

@ -47,45 +47,58 @@
<div id="assigned_user" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}"> <div id="assigned_user" class="form-group{{ $errors->has('assigned_user') ? ' has-error' : '' }}">
{{ Form::label('assigned_user', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }} {{ Form::label('assigned_user', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7 required"> <div class="col-md-7 required">
<select class="js-data-user-ajax" name="assigned_user" style="width: 350px;"> <select class="js-data-ajax" data-endpoint="users" name="assigned_user" style="width: 100%" id="assigned_user_select">
<option value="">{{ trans('general.select_user') }}</option> <option value="">{{ trans('general.select_user') }}</option>
</select> </select>
</div> </div>
{!! $errors->first('assigned_user', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
<div class="col-md-1 col-sm-1 text-left"> <div class="col-md-1 col-sm-1 text-left">
@can('create', \App\Models\User::class) @can('create', \App\Models\User::class)
<a href='{{ route('modal.user') }}' data-toggle="modal" data-target="#createModal" data-dependency="user" data-select='assigned_user_select' class="btn btn-sm btn-default">New</a> <a href='{{ route('modal.user') }}' data-toggle="modal" data-target="#createModal" data-dependency="user" data-select='assigned_user_select' class="btn btn-sm btn-default">New</a>
@endcan @endcan
</div> </div>
</div>
@if (!$asset->requireAcceptance())
<!-- Assets -->
<div id="assigned_asset" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}">
{{ Form::label('assigned_asset', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7 required">
{{ Form::select('assigned_asset', $assets_list , Input::old('assigned_asset', $asset->assigned_type == 'App\Models\Asset' ? $asset->assigned_to : 0), array('class'=>'select2', 'id'=>'assigned_asset', 'style'=>'width:100%')) }}
{!! $errors->first('assigned_asset', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
</div>
</div>
<!-- Locations --> {!! $errors->first('assigned_user', '<div class="col-md-8 col-md-offset-3"><span class="alert-msg"><i class="fa fa-times"></i> :message</span></div>') !!}
<div id="assigned_location" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}">
{{ Form::label('assigned_location', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }} </div>
<div class="col-md-7 required">
{{ Form::select('assigned_location', $locations_list , Input::old('assigned_location', $asset->assigned_type == 'App\Models\Asset' ? $asset->assigned_to : 0), array('class'=>'select2', 'id'=>'assigned_location', 'style'=>'width:100%')) }} @if (!$asset->requireAcceptance())
{!! $errors->first('assigned_location', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
</div> <!-- Asset -->
</div> <div id="assigned_asset" class="form-group{{ $errors->has('assigned_asset') ? ' has-error' : '' }}">
{{ Form::label('assigned_asset', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7 required">
<select class="js-data-ajax" data-endpoint="hardware" name="assigned_asset" style="width: 100%" id="assigned_asset_select">
<option value="">{{ trans('general.select_asset') }}</option>
</select>
</div>
{!! $errors->first('assigned_asset', '<div class="col-md-8 col-md-offset-3"><span class="alert-msg"><i class="fa fa-times"></i> :message</span></div>') !!}
</div>
<!-- Location -->
<div id="assigned_location" class="form-group{{ $errors->has('assigned_location') ? ' has-error' : '' }}">
{{ Form::label('assigned_location', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7 required">
<select class="js-data-ajax" data-endpoint="locations" name="assigned_location" style="width: 100%" id="assigned_location_select">
<option value="">{{ trans('general.select_location') }}</option>
</select>
</div>
<div class="col-md-1 col-sm-1 text-left">
@can('create', \App\Models\Location::class)
<a href='{{ route('modal.location') }}' data-toggle="modal" data-target="#createModal" data-dependency="location" data-select='assigned_location_select' class="btn btn-sm btn-default">New</a>
@endcan
</div>
{!! $errors->first('assigned_location', '<div class="col-md-8 col-md-offset-3"><span class="alert-msg"><i class="fa fa-times"></i> :message</span></div>') !!}
</div>
@endif @endif
<!-- Checkout/Checkin Date --> <!-- Checkout/Checkin Date -->
<div class="form-group {{ $errors->has('checkout_at') ? 'error' : '' }}"> <div class="form-group {{ $errors->has('checkout_at') ? 'error' : '' }}">
@ -164,9 +177,12 @@
@section('moar_scripts') @section('moar_scripts')
<script nonce="{{ csrf_token() }}"> <script nonce="{{ csrf_token() }}">
// create the assigned assets listing box for the right side of the screen
$(function() { $(function() {
$('#assigned_user').on("change",function () { $('#assigned_user').on("change",function () {
var userid = $('#assigned_user option:selected').val(); var userid = $('#assigned_user option:selected').val();
if(userid=='') { if(userid=='') {
console.warn('no user selected'); console.warn('no user selected');
$('#current_assets_box').fadeOut(); $('#current_assets_box').fadeOut();

View file

@ -658,73 +658,6 @@
<script src="{{ url(mix('js/dist/all.js')) }}" nonce="{{ csrf_token() }}"></script> <script src="{{ url(mix('js/dist/all.js')) }}" nonce="{{ csrf_token() }}"></script>
<script nonce="{{ csrf_token() }}">
$(function () {
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-user-ajax").select2({
ajax: {
url: '{{ route('api.users.selectlist') }}',
dataType: 'json',
delay: 250,
headers: {
"X-Requested-With": 'XMLHttpRequest',
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
},
data: function (params) {
var data = {
search: params.term,
page: params.page || 1
};
return data;
},
processResults: function (data, params) {
params.page = params.page || 1;
var answer = {
results: data.items,
pagination: {
more: "true" //(params.page < data.page_count)
}
};
return answer;
},
cache: true
},
escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
templateResult: formatUserlist,
templateSelection: formatUserSelection
});
function formatUserlist (userlist) {
var loading_markup = '<i class="fa fa-spinner fa-spin" aria-hidden="true"></i> Loading...';
if (userlist.loading) {
return loading_markup;
}
var markup = "<div class='clearfix'>" ;
markup +="<div class='pull-left' style='padding-right: 10px;'>";
markup += "<img src='" + userlist.image + "' style='max-height: 20px'></div>";
markup += "<div>" + userlist.text + "</div>";
markup += "</div>";
return markup;
}
function formatUserSelection (userlist) {
return userlist.text;
}
</script>
@section('moar_scripts') @section('moar_scripts')
@show @show

View file

@ -226,6 +226,12 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
Route::group(['prefix' => 'hardware'], function () { Route::group(['prefix' => 'hardware'], function () {
Route::get( 'selectlist', [
'as' => 'assets.selectlist',
'uses' => 'AssetsController@selectlist'
]);
Route::post('audit', [ Route::post('audit', [
'as' => 'api.asset.audit', 'as' => 'api.asset.audit',
'uses' => 'AssetsController@audit' 'uses' => 'AssetsController@audit'
@ -331,21 +337,6 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
/*--- Locations API ---*/ /*--- Locations API ---*/
Route::resource('locations', 'LocationsController',
[
'names' =>
[
'index' => 'api.locations.index',
'show' => 'api.locations.show',
'store' => 'api.locations.store',
'update' => 'api.locations.update',
'destroy' => 'api.locations.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['location' => 'location_id']
]
); // Locations resource
Route::group(['prefix' => 'locations'], function () { Route::group(['prefix' => 'locations'], function () {
Route::get('{location}/users', Route::get('{location}/users',
@ -369,9 +360,33 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
'uses' => 'LocationsController@show' 'uses' => 'LocationsController@show'
] ]
); );
Route::get( 'selectlist', [
'as' => 'locations.selectlist',
'uses' => 'LocationsController@selectlist'
]);
}); // Locations group }); // Locations group
Route::resource('locations', 'LocationsController',
[
'names' =>
[
'index' => 'api.locations.index',
'show' => 'api.locations.show',
'store' => 'api.locations.store',
'update' => 'api.locations.update',
'destroy' => 'api.locations.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['location' => 'location_id']
]
); // Locations resource
/*--- Manufacturers API ---*/ /*--- Manufacturers API ---*/
Route::resource('manufacturers', 'ManufacturersController', Route::resource('manufacturers', 'ManufacturersController',