- improved "status" select field, #662

This commit is contained in:
Mark Friedrich
2018-07-27 22:15:15 +02:00
parent 99022edc58
commit 081bb36231
10 changed files with 236 additions and 95 deletions

View File

@@ -34,12 +34,8 @@ class System extends Controller\AccessController {
$mapData = (array)$postData['mapData'];
$systemModel = null;
if( isset($systemData['statusId']) ){
if( (int)$systemData['statusId'] <= 0){
unset($systemData['statusId']);
}else{
$systemData['statusId'] = (int)$systemData['statusId'];
}
if( (int)$systemData['statusId'] <= 0 ){
unset($systemData['statusId']);
}
if( isset($systemData['id']) ){
@@ -72,7 +68,7 @@ class System extends Controller\AccessController {
if( !is_null($systemModel) ){
// set/update system custom data
$systemModel->copyfrom($systemData, ['locked', 'rallyUpdated', 'position', 'description']);
$systemModel->copyfrom($systemData, ['statusId', 'locked', 'rallyUpdated', 'position', 'description']);
if($systemModel->save($activeCharacter)){
// get data from "fresh" model (e.g. some relational data has changed: "statusId")

View File

@@ -74,6 +74,7 @@ class SystemModel extends AbstractMapTrackingModel {
'on-delete' => 'CASCADE'
]
],
'validate' => true,
'activity-log' => true
],
'locked' => [
@@ -142,9 +143,9 @@ class SystemModel extends AbstractMapTrackingModel {
$systemData->type = $this->typeId->getData();
}
$systemData->status = (object) [];
$systemData->status->id = is_object($this->statusId) ? $this->statusId->id : 1;
$systemData->status->name = is_object($this->statusId) ? $this->statusId->name : 'unknown';
if(is_object($this->statusId)){
$systemData->status = $this->statusId->getData();
}
$systemData->locked = $this->locked;
$systemData->rallyUpdated = strtotime($this->rallyUpdated);
@@ -245,6 +246,22 @@ class SystemModel extends AbstractMapTrackingModel {
return $valid;
}
/**
* @param string $key
* @param int $val
* @return bool
* @throws \Exception\ValidationException
*/
protected function validate_statusId(string $key, int $val): bool {
$valid = true;
if( !$this->rel('statusId')::getStatusById($val) ){
$valid = false;
$this->throwValidationException($key, 'Validation failed: "' . $key . '" = "' . $val . '"');
}
return $valid;
}
/**
* setter for system alias
* @param string $alias

View File

@@ -77,4 +77,26 @@ class SystemStatusModel extends BasicModel {
]
];
/**
* get system status data
* @return \stdClass
*/
public function getData(){
$statusData = (object)[];
$statusData->id = $this->_id;
$statusData->name = $this->name;
return $statusData;
}
/**
* get status by id
* @param int $statusId
* @return self|null
*/
public static function getStatusById(int $statusId = 1){
$status = (new self())->getById($statusId);
return $status->dry() ? null : $status;
}
}

View File

@@ -58,6 +58,7 @@ define([
// dialogs
systemDialogId: 'pf-system-dialog', // id for system dialog
systemDialogSelectClass: 'pf-system-dialog-select', // class for system select Element
systemDialogStatusSelectId: 'pf-system-dialog-status-select', // id for "status" select
// system security classes
systemSec: 'pf-system-sec'
@@ -1378,21 +1379,13 @@ define([
* @param map
* @param options
*/
let showNewSystemDialog = function(map, options){
let showNewSystemDialog = (map, options) => {
let mapContainer = $(map.getContainer());
// format system status for form select -----------------------------------------------------------------------
let systemStatus = {};
// "default" selection (id = 0) prevents status from being overwritten
// -> e.g. keep status information if system was just inactive (active = 0)
systemStatus[0] = 'default';
$.each(Init.systemStatus, function(status, statusData){
systemStatus[statusData.id] = statusData.label;
});
// default system status -> first status entry
let defaultSystemStatus = 0;
let statusData = [{id: 0, text: 'auto'}];
// get current map data ---------------------------------------------------------------------------------------
let mapData = mapContainer.getMapDataFromClient({forceData: true});
@@ -1416,7 +1409,10 @@ define([
// dialog data ------------------------------------------------------------------------------------------------
let data = {
id: config.systemDialogId,
selectClass: config.systemDialogSelectClass
select2Class: Util.config.select2Class,
selectClass: config.systemDialogSelectClass,
statusSelectId: config.systemDialogStatusSelectId,
statusData: statusData
};
// set current position as "default" system to add ------------------------------------------------------------
@@ -1438,6 +1434,7 @@ define([
let systemDialog = bootbox.dialog({
title: 'Add new system',
message: content,
show: false,
buttons: {
close: {
label: 'cancel',
@@ -1517,9 +1514,21 @@ define([
}
});
// init dialog
systemDialog.on('shown.bs.modal', function(e) {
systemDialog.on('show.bs.modal', function(e) {
let modalContent = $('#' + config.systemDialogId);
// init "status" select2
for (let [statusName, data] of Object.entries(Init.systemStatus)){
statusData.push({id: data.id, text: data.label, class: data.class});
}
modalContent.find('#' + config.systemDialogStatusSelectId).initStatusSelect({
data: statusData,
iconClass: 'fa-tag'
});
});
systemDialog.on('shown.bs.modal', function(e) {
let modalContent = $('#' + config.systemDialogId);
// init system select live search - some delay until modal transition has finished
@@ -1530,18 +1539,8 @@ define([
});
});
// init system status select
let modalFields = $('.bootbox .modal-dialog').find('.pf-editable-system-status');
modalFields.editable({
mode: 'inline',
emptytext: 'unknown',
onblur: 'submit',
showbuttons: false,
source: systemStatus,
value: defaultSystemStatus,
inputclass: config.systemDialogSelectClass
});
// show dialog
systemDialog.modal('show');
});
};

View File

@@ -15,7 +15,7 @@ define([
* @param data
* @returns {*}
*/
let formatCategoryTypeResultData = (data) => {
let formatCategoryTypeResultData = data => {
if(data.loading) return data.text;
if(data.placeholder) return data.placeholder;
@@ -82,6 +82,52 @@ define([
);
};
/**
* init a sselect element as "select2" for "status" selection
* @param options
* @returns {*}
*/
$.fn.initStatusSelect = function(options){
let config = {
minimumResultsForSearch: -1,
width: '100%',
iconClass: 'fa-circle'
};
config = $.extend({}, config, options);
let formatStatusSelectionData = state => {
let markup = '<span>';
markup += '<i class="fas ' + config.iconClass + ' ' + state.class + '"></i>&nbsp;&nbsp;&nbsp;' + state.text;
markup += '</span>';
return $(markup);
};
let formatStatusResultData = data => {
if(data.loading) return data.text;
if(data.placeholder) return data.placeholder;
let markup = '<div class="clearfix">';
markup += '<div class="col-xs-2 text-center">';
markup += '<i class="fas ' + config.iconClass + ' ' + data.class + '"></i>';
markup += '</div>';
markup += '<div class="col-xs-10">' + data.text + '</div>';
markup += '</div>';
return $(markup);
};
config.templateSelection = formatStatusSelectionData;
config.templateResult = formatStatusResultData;
return this.each(function(){
let selectElement = $(this);
selectElement.select2(config);
});
};
/**
* init a select element as an ajax based "select2" object for system search
* @param options
@@ -304,7 +350,6 @@ define([
// after init finish
});
});
};
/**

View File

@@ -242,14 +242,16 @@ define([
let structureStatusData = Util.getObjVal(Init, 'structureStatus');
let structureTypeData = Util.getObjVal(Init, 'structureStatus');
let statusData = Object.keys(structureStatusData).map((k) => {
let data = structureStatusData[k];
data.selected = data.id === Util.getObjVal(structureData, 'status.id');
return data;
});
let data = {
id: config.structureDialogId,
structureData: structureData,
structureStatus: Object.keys(structureStatusData).map((k) => {
let data = structureStatusData[k];
data.selected = data.id === Util.getObjVal(structureData, 'status.id');
return data;
}),
structureStatus: statusData,
statusSelectId: config.statusSelectId,
typeSelectId: config.typeSelectId,
corporationSelectId: config.corporationSelectId,
@@ -264,7 +266,7 @@ define([
let structureDialog = bootbox.dialog({
title: 'Structure',
message: content,
show: true,
show: false,
buttons: {
close: {
label: 'cancel',
@@ -304,9 +306,11 @@ define([
}
});
structureDialog.on('shown.bs.modal', function(e) {
structureDialog.on('show.bs.modal', function(e) {
let modalContent = $('#' + config.structureDialogId);
// init type select live search
let selectElementType = $(this).find('#' + config.typeSelectId);
let selectElementType = modalContent.find('#' + config.typeSelectId);
selectElementType.initUniverseTypeSelect({
categoryIds: [65],
maxSelectionLength: 1,
@@ -314,19 +318,20 @@ define([
});
// init corporation select live search
let selectElementCorporation = $(this).find('#' + config.corporationSelectId);
let selectElementCorporation = modalContent.find('#' + config.corporationSelectId);
selectElementCorporation.initUniverseSearch({
categoryNames: ['corporation'],
maxSelectionLength: 1
});
$(this).find('#' + config.statusSelectId).select2({
minimumResultsForSearch: -1
// init status select2
modalContent.find('#' + config.statusSelectId).initStatusSelect({
data: statusData
});
// init character counter
let textarea = $(this).find('#' + config.descriptionTextareaId);
let charCounter = $(this).find('.' + config.descriptionTextareaCharCounter);
let textarea = modalContent.find('#' + config.descriptionTextareaId);
let charCounter = modalContent.find('.' + config.descriptionTextareaCharCounter);
Util.updateCounter(textarea, charCounter, maxDescriptionLength);
textarea.on('keyup', function(){
@@ -334,9 +339,11 @@ define([
});
// set form validator (after select2 init finish)
$(this).find('form').initFormValidation();
modalContent.find('form').initFormValidation();
});
// show dialog
structureDialog.modal('show');
});
};

View File

@@ -58,6 +58,7 @@ define([
// dialogs
systemDialogId: 'pf-system-dialog', // id for system dialog
systemDialogSelectClass: 'pf-system-dialog-select', // class for system select Element
systemDialogStatusSelectId: 'pf-system-dialog-status-select', // id for "status" select
// system security classes
systemSec: 'pf-system-sec'
@@ -1378,21 +1379,13 @@ define([
* @param map
* @param options
*/
let showNewSystemDialog = function(map, options){
let showNewSystemDialog = (map, options) => {
let mapContainer = $(map.getContainer());
// format system status for form select -----------------------------------------------------------------------
let systemStatus = {};
// "default" selection (id = 0) prevents status from being overwritten
// -> e.g. keep status information if system was just inactive (active = 0)
systemStatus[0] = 'default';
$.each(Init.systemStatus, function(status, statusData){
systemStatus[statusData.id] = statusData.label;
});
// default system status -> first status entry
let defaultSystemStatus = 0;
let statusData = [{id: 0, text: 'auto'}];
// get current map data ---------------------------------------------------------------------------------------
let mapData = mapContainer.getMapDataFromClient({forceData: true});
@@ -1416,7 +1409,10 @@ define([
// dialog data ------------------------------------------------------------------------------------------------
let data = {
id: config.systemDialogId,
selectClass: config.systemDialogSelectClass
select2Class: Util.config.select2Class,
selectClass: config.systemDialogSelectClass,
statusSelectId: config.systemDialogStatusSelectId,
statusData: statusData
};
// set current position as "default" system to add ------------------------------------------------------------
@@ -1438,6 +1434,7 @@ define([
let systemDialog = bootbox.dialog({
title: 'Add new system',
message: content,
show: false,
buttons: {
close: {
label: 'cancel',
@@ -1517,9 +1514,21 @@ define([
}
});
// init dialog
systemDialog.on('shown.bs.modal', function(e) {
systemDialog.on('show.bs.modal', function(e) {
let modalContent = $('#' + config.systemDialogId);
// init "status" select2
for (let [statusName, data] of Object.entries(Init.systemStatus)){
statusData.push({id: data.id, text: data.label, class: data.class});
}
modalContent.find('#' + config.systemDialogStatusSelectId).initStatusSelect({
data: statusData,
iconClass: 'fa-tag'
});
});
systemDialog.on('shown.bs.modal', function(e) {
let modalContent = $('#' + config.systemDialogId);
// init system select live search - some delay until modal transition has finished
@@ -1530,18 +1539,8 @@ define([
});
});
// init system status select
let modalFields = $('.bootbox .modal-dialog').find('.pf-editable-system-status');
modalFields.editable({
mode: 'inline',
emptytext: 'unknown',
onblur: 'submit',
showbuttons: false,
source: systemStatus,
value: defaultSystemStatus,
inputclass: config.systemDialogSelectClass
});
// show dialog
systemDialog.modal('show');
});
};

View File

@@ -15,7 +15,7 @@ define([
* @param data
* @returns {*}
*/
let formatCategoryTypeResultData = (data) => {
let formatCategoryTypeResultData = data => {
if(data.loading) return data.text;
if(data.placeholder) return data.placeholder;
@@ -82,6 +82,52 @@ define([
);
};
/**
* init a sselect element as "select2" for "status" selection
* @param options
* @returns {*}
*/
$.fn.initStatusSelect = function(options){
let config = {
minimumResultsForSearch: -1,
width: '100%',
iconClass: 'fa-circle'
};
config = $.extend({}, config, options);
let formatStatusSelectionData = state => {
let markup = '<span>';
markup += '<i class="fas ' + config.iconClass + ' ' + state.class + '"></i>&nbsp;&nbsp;&nbsp;' + state.text;
markup += '</span>';
return $(markup);
};
let formatStatusResultData = data => {
if(data.loading) return data.text;
if(data.placeholder) return data.placeholder;
let markup = '<div class="clearfix">';
markup += '<div class="col-xs-2 text-center">';
markup += '<i class="fas ' + config.iconClass + ' ' + data.class + '"></i>';
markup += '</div>';
markup += '<div class="col-xs-10">' + data.text + '</div>';
markup += '</div>';
return $(markup);
};
config.templateSelection = formatStatusSelectionData;
config.templateResult = formatStatusResultData;
return this.each(function(){
let selectElement = $(this);
selectElement.select2(config);
});
};
/**
* init a select element as an ajax based "select2" object for system search
* @param options
@@ -304,7 +350,6 @@ define([
// after init finish
});
});
};
/**

View File

@@ -242,14 +242,16 @@ define([
let structureStatusData = Util.getObjVal(Init, 'structureStatus');
let structureTypeData = Util.getObjVal(Init, 'structureStatus');
let statusData = Object.keys(structureStatusData).map((k) => {
let data = structureStatusData[k];
data.selected = data.id === Util.getObjVal(structureData, 'status.id');
return data;
});
let data = {
id: config.structureDialogId,
structureData: structureData,
structureStatus: Object.keys(structureStatusData).map((k) => {
let data = structureStatusData[k];
data.selected = data.id === Util.getObjVal(structureData, 'status.id');
return data;
}),
structureStatus: statusData,
statusSelectId: config.statusSelectId,
typeSelectId: config.typeSelectId,
corporationSelectId: config.corporationSelectId,
@@ -264,7 +266,7 @@ define([
let structureDialog = bootbox.dialog({
title: 'Structure',
message: content,
show: true,
show: false,
buttons: {
close: {
label: 'cancel',
@@ -304,9 +306,11 @@ define([
}
});
structureDialog.on('shown.bs.modal', function(e) {
structureDialog.on('show.bs.modal', function(e) {
let modalContent = $('#' + config.structureDialogId);
// init type select live search
let selectElementType = $(this).find('#' + config.typeSelectId);
let selectElementType = modalContent.find('#' + config.typeSelectId);
selectElementType.initUniverseTypeSelect({
categoryIds: [65],
maxSelectionLength: 1,
@@ -314,19 +318,20 @@ define([
});
// init corporation select live search
let selectElementCorporation = $(this).find('#' + config.corporationSelectId);
let selectElementCorporation = modalContent.find('#' + config.corporationSelectId);
selectElementCorporation.initUniverseSearch({
categoryNames: ['corporation'],
maxSelectionLength: 1
});
$(this).find('#' + config.statusSelectId).select2({
minimumResultsForSearch: -1
// init status select2
modalContent.find('#' + config.statusSelectId).initStatusSelect({
data: statusData
});
// init character counter
let textarea = $(this).find('#' + config.descriptionTextareaId);
let charCounter = $(this).find('.' + config.descriptionTextareaCharCounter);
let textarea = modalContent.find('#' + config.descriptionTextareaId);
let charCounter = modalContent.find('.' + config.descriptionTextareaCharCounter);
Util.updateCounter(textarea, charCounter, maxDescriptionLength);
textarea.on('keyup', function(){
@@ -334,9 +339,11 @@ define([
});
// set form validator (after select2 init finish)
$(this).find('form').initFormValidation();
modalContent.find('form').initFormValidation();
});
// show dialog
structureDialog.modal('show');
});
};

View File

@@ -46,9 +46,13 @@
<div class="row">
<div class="col-xs-8">
<div class="form-group">
<label class="col-sm-2 control-label" for="form_status">Status</label>
<div class="col-sm-6" style="min-height: 32px;">
<a class="pf-editable pf-editable-system-status" href="#" id="form_status" data-type="select" data-name="statusId"></a>
<label class="col-sm-2 control-label" for="{{statusSelectId}}">Status</label>
<div class="col-sm-6">
<select name="statusId" id="{{statusSelectId}}" class="form-control {{select2Class}}">
{{#statusData}}
<option value="{{id}}">{{text}}</option>
{{/statusData}}
</select>
</div>
</div>
</div>