Refactor inventory edit mode [SCI-4070]

This commit is contained in:
Oleksii Kriuchykhin 2019-11-18 14:04:53 +01:00 committed by Urban Rotnik
parent e8fdfe7111
commit 338b710d81
13 changed files with 764 additions and 897 deletions

View file

@ -44,6 +44,7 @@
//= require protocols/header
//= require marvinjslauncher
//= require_directory ./repositories/renderers
//= require_directory ./repositories/validators
//= require turbolinks

View file

@ -1,396 +1,260 @@
/* global Promise _ ActiveStorage RepositoryItemEditForm */
//= require sugar.min
//= require jquerymy-1.2.14.min
(function(global) {
'use strict';
/**
* RepositoryItemEditForm generates the html inputs for
* repository item and returns the form data object
*
* @param {Object} itemData - repository item data fetched from the API
* @param {Object} repositoryItemElement - row node in the table
*/
global.RepositoryItemEditForm = function(itemData, repositoryItemElement) {
this.itemData = itemData;
this.repositoryItemElement = repositoryItemElement;
this.formData = this.composeFormData(itemData);
}
/**
* Generates the input fields
*
* @param {Object} table - datatable.js object
* @returns {undefinded}
*/
RepositoryItemEditForm.prototype.renderForm = function(table) {
var colIndex = getColumnIndex(table, '#row-name');
var cells = this.itemData.repository_row.repository_cells;
var listColumns = this.itemData.repository_row.repository_column_items;
var formData = this.formData;
if (colIndex) {
$(this.repositoryItemElement).children('td').eq(colIndex)
.html(changeToInputField('repository_row',
'name',
this.itemData.repository_row.name,
'rowName'));
}
$(this.repositoryItemElement).children('td').each(function(i) {
var td = $(this);
var rawIndex = table.column.index('fromVisible', i);
var colHeader = table.column(rawIndex).header();
if ($(colHeader).hasClass('repository-column')) {
var type = $(colHeader).attr('data-type');
var colHeaderId = $(colHeader).attr('id');
var cell = cells[colHeaderId] || '';
td.html(changeToFormField('repository_cell',
colHeaderId,
type,
cell,
listColumns));
addSelectedFile(type, colHeaderId);
appendNewElementToFormData(cell, colHeaderId, formData);
}
});
initializeDataBinding(this.repositoryItemElement, formData);
}
/**
* Parse received data in to a from object
*
* @param {Object} itemData - json representations of repository item
*
* @returns {Object}
*/
RepositoryItemEditForm.prototype.composeFormData = function(itemData) {
var formBindingsData = {};
formBindingsData['rowName'] = itemData.repository_row.name;
$.each(itemData.repository_row.repository_cells, function(i, cell) {
var tableCellId = 'colId-' + cell.cell_column_id;
if(cell.type === 'RepositoryAssetValue') {
formBindingsData[tableCellId] = new File([""], cell.value.file_name);
} else {
formBindingsData[tableCellId] = cell.value;
}
});
return formBindingsData;
}
/**
* Handles select picker default value
*
* @param {Object} node
* @returns {undefinded}
*/
RepositoryItemEditForm.prototype.initializeSelectpickerValues = function(node) {
$($(node).find('.bootstrap-select')).each(function(_, dropdown) {
var selectedValue = $($(dropdown).find('select')[0]).data('selected-value');
var selectPicker = $($(dropdown).find('select')[0]);
var value = '-1'
$(dropdown).find('option').each(function(_, option) {
$(option).removeAttr('selected');
if($(option).val() === selectedValue.toString()) {
$(option).attr('selected', true);
value = $(option).attr('value');
}
});
$(dropdown).parent().attr("list_item_id", value);
selectPicker.val(value);
selectPicker.selectpicker('refresh');
});
}
/**
* Creates a FormData object with the repository row data ready to be
* sended on the server
*
* @param {Object} tableID
* @param {Object} selectedRecord
*
* @returns (Object)
*/
RepositoryItemEditForm.prototype.parseToFormObject = function(tableID, selectedRecord) {
var formData = this.formData;
var formDataObj = new FormData();
var removeFileColumns = [];
var filesToUploadCntr = 0;
var filesUploadedCntr = 0;
const directUploadUrl = $(tableID).data('directUploadUrl');
formDataObj.append('request_url', $(tableID).data('current-uri'));
formDataObj.append('repository_row_id', $(selectedRecord).attr('id'));
return new Promise((resolve, reject) => {
$(_.keys(this.formData)).each(function(_, element) {
var value = formData[element];
if (element === 'rowName') {
formDataObj.append('repository_row_name', value);
} else {
let colId = element.replace('colId-', '');
let $el = $('#' + element);
// don't save anything if element is not visible
if ($el.length === 0) {
return;
}
if ($el.attr('type') === 'file') {
// handle deleting of element
if ($el.attr('remove') === 'true') {
removeFileColumns.push(colId);
formDataObj.append('repository_cells[' + colId + ']', null);
} else if ($el[0].files.length > 0) {
filesToUploadCntr += 1;
}
} else if (value.length >= 0) {
formDataObj.append('repository_cells[' + colId + ']', value);
}
}
});
formDataObj.append('remove_file_columns', JSON.stringify(removeFileColumns));
// No files for upload, so return earlier
if (filesToUploadCntr === 0) {
resolve(formDataObj);
return;
}
// Second run, just for files
$(_.keys(this.formData)).each(function(_, element) {
let $el = $('#' + element);
let colId = element.replace('colId-', '');
if ($el.attr('type') === 'file' && $el.attr('remove') !== 'true') {
let upload = new ActiveStorage.DirectUpload($el[0].files[0], directUploadUrl);
upload.create(function(error, blob) {
if (error) {
reject(error);
} else {
formDataObj.append('repository_cells[' + colId + ']', blob.signed_id);
filesUploadedCntr += 1;
if (filesUploadedCntr === filesToUploadCntr) {
resolve(formDataObj);
}
}
});
}
});
});
};
/**
* |-----------------|
* | Private methods |
* |-----------------|
*/
/**
* Takes object and surrounds it with input
*
* @param {Object} object
* @param {String} name
* @param {String} value
* @param {String} id
*
* @returns (String)
*/
function changeToInputField(object, name, value, id) {
return "<div class='form-group'><input class='form-control' data-object='"
+ object + "' name='" + name + "' value='" + value + "' id='" + id + "'></input></div>";
}
/**
* Takes object and creates an input file field, contains a hidden
* input field which is triggered on button click and we get the uploaded
* file from there.
*
* @param {Object} object
* @param {String} name
* @param {String} value
* @param {String} id
*
* @returns (String)
*/
function changeToInputFileField(object, name, value, id) {
var fileName = (value.file_name) ? value.file_name : I18n.t('general.file.no_file_chosen');
var buttonLabel = I18n.t('general.file.choose');
var html = "<div class='repository-input-file-field'>" +
"<div class='form-group'><div><input type='file' name='" + name + "' id='" +
id + "' style='display:none' /><button class='btn btn-default' " +
"data-object='" + object + "' name='" + name + "' value='" + value +
"' data-id='" + id + "'>" + buttonLabel +
"</button></div><div><p class='file-name-label'>" + truncateLongString(fileName, 20) +
"</p></div>";
if(value.file_name) {
html += "<div><a data-action='removeAsset' ";
html += "onClick='clearFileInput(this)'><i class='fas fa-times'></i></a>";
} else {
html += "<div><a data-action='removeAsset' onClick='clearFileInput(this)' ";
html += "style='display:none'><i class='fas fa-times'></i></a>";
}
html += "</div></div></div>";
return html;
}
/**
* Returns the colum index
*
* @param {Object} table
* @param {String} id
*
* @returns (Boolean | Number)
*/
function getColumnIndex(table, id) {
if(id < 0)
return false;
return table.column(id).index('visible');
}
/**
* Genrates list items dropdown element
*
* @param {Array} options
* @param {String} current_value
* @param {Number} columnId
* @param {String} id
*
* @returns (String)
*/
function _listItemDropdown(options, current_value, columnId, id) {
var val = undefined;
var html = '<select id="' + id + '" class="form-control selectpicker repository-dropdown" ';
html += 'data-selected-value="" data-abs-min-length="2" data-live-search="true" ';
html += 'data-container="body" column_id="' + columnId +'">';
html += '<option value="-1"></option>';
$.each(options, function(index, value) {
var selected = '';
if (current_value === value[1]) {
selected = 'selected';
val = value[0];
}
html += '<option value="' + value[0] + '" ' + selected + '>';
html += value[1] + '</option>';
});
html += '</select>';
return (val) ? $(html).attr('data-selected-value', val)[0] : html;
}
/**
* Takes an object and creates custom html element
*
* @param {String} object
* @param {String} name
* @param {String} column_type
* @param {Object} cell
* @param {Object} listColumns
*
* @returns (String)
*/
function changeToFormField(object, name, column_type, cell, listColumns) {
var cellId = generateInputFieldReference(name);
var value = cell.value || '';
if (column_type === 'RepositoryListValue') {
var column = _.findWhere(listColumns,
{ column_id: parseInt(name, 10) });
var list_items = column.list_items || cell.list_items;
return _listItemDropdown(list_items, value, parseInt(name, 10), cellId);
} else if (column_type === 'RepositoryAssetValue') {
return changeToInputFileField('repository_cell_file', name, value, cellId);
} else {
return changeToInputField(object, name, value, cellId);
}
}
/**
* Append the change listener to file field
*
* @param {String} type
* @param {String} name
*
* @returns {undefined}
*/
function addSelectedFile(type, name) {
var button = $('button[data-id="' +
generateInputFieldReference(name) +
'"]');
if (type === 'RepositoryAssetValue') {
var fileInput = $(button.parent().find('input[type="file"]')[0]);
button.on('click', function(ev) {
ev.preventDefault();
ev.stopPropagation();
fileInput.trigger('click');
initFileHandler(fileInput);
});
}
}
/**
* Handle extraction of file from the input field
*
* @param {Object} $inputField
*
* @returns {undefined}
*/
function initFileHandler($inputField) {
$inputField.on('change', function() {
var input = $(this);
var $label = $($(this).closest('.repository-input-file-field')
.find('.file-name-label')[0]);
var file = this.files[0];
if (file) {
$label.text(truncateLongString(file.name, 20));
input.attr('remove', false);
$($label.closest('.repository-input-file-field')
.find('[data-action="removeAsset"]')[0]).show();
}
})
}
/**
* Initializes the data binding for form object
*
* @param {Object} rowNode
* @param {Object} data
*
* @returns {undefined}
*/
function initializeDataBinding(rowNode, data) {
var uiBindings = {};
$.each(_.keys(data), function(i, element) {
uiBindings['#' + element] = element;
})
$(rowNode).my({ui: uiBindings}, data);
}
/**
* Generates the input tag id that will be used in the formData object
*
* @param {String} columnId
*
* @returns {String}
*/
function generateInputFieldReference(columnId) {
return 'colId-' + columnId;
}
/**
* Appends aditional fields to form data object
* @param {Object} cell
* @param {String} columnId
* @param {Object} formData
*
* @returns {undefined}
*/
function appendNewElementToFormData(cell, columnId, formData) {
if (!cell.repository_cell_id) {
formData[generateInputFieldReference(columnId)] = undefined;
}
}
}(window));
// /* global Promise _ ActiveStorage RepositoryItemEditForm */
//
// //= require sugar.min
// //= require jquerymy-1.2.14.min
//
// (function(global) {
// 'use strict';
//
// /**
// * Creates a FormData object with the repository row data ready to be
// * sended on the server
// *
// * @param {Object} tableID
// * @param {Object} selectedRecord
// *
// * @returns (Object)
// */
// RepositoryItemEditForm.prototype.parseToFormObject = function(tableID, selectedRecord) {
// var formData = this.formData;
// var formDataObj = new FormData();
// var removeFileColumns = [];
// var filesToUploadCntr = 0;
// var filesUploadedCntr = 0;
// const directUploadUrl = $(tableID).data('directUploadUrl');
//
// formDataObj.append('request_url', $(tableID).data('current-uri'));
// formDataObj.append('repository_row_id', $(selectedRecord).attr('id'));
//
// return new Promise((resolve, reject) => {
// $(_.keys(this.formData)).each(function(_, element) {
// var value = formData[element];
// if (element === 'rowName') {
// formDataObj.append('repository_row_name', value);
// } else {
// let colId = element.replace('colId-', '');
// let $el = $('#' + element);
// // don't save anything if element is not visible
// if ($el.length === 0) {
// return;
// }
// if ($el.attr('type') === 'file') {
// // handle deleting of element
// if ($el.attr('remove') === 'true') {
// removeFileColumns.push(colId);
// formDataObj.append('repository_cells[' + colId + ']', null);
// } else if ($el[0].files.length > 0) {
// filesToUploadCntr += 1;
// }
// } else if (value.length >= 0) {
// formDataObj.append('repository_cells[' + colId + ']', value);
// }
// }
// });
//
// formDataObj.append('remove_file_columns', JSON.stringify(removeFileColumns));
//
// // No files for upload, so return earlier
// if (filesToUploadCntr === 0) {
// resolve(formDataObj);
// return;
// }
//
// // Second run, just for files
// $(_.keys(this.formData)).each(function(_, element) {
// let $el = $('#' + element);
// let colId = element.replace('colId-', '');
//
// if ($el.attr('type') === 'file' && $el.attr('remove') !== 'true') {
// let upload = new ActiveStorage.DirectUpload($el[0].files[0], directUploadUrl);
//
// upload.create(function(error, blob) {
// if (error) {
// reject(error);
// } else {
// formDataObj.append('repository_cells[' + colId + ']', blob.signed_id);
// filesUploadedCntr += 1;
//
// if (filesUploadedCntr === filesToUploadCntr) {
// resolve(formDataObj);
// }
// }
// });
// }
// });
// });
// };
//
// /**
// * Takes object and creates an input file field, contains a hidden
// * input field which is triggered on button click and we get the uploaded
// * file from there.
// *
// * @param {Object} object
// * @param {String} name
// * @param {String} value
// * @param {String} id
// *
// * @returns (String)
// */
// function changeToInputFileField(object, name, value, id) {
// var fileName = (value.file_name) ? value.file_name : I18n.t('general.file.no_file_chosen');
// var buttonLabel = I18n.t('general.file.choose');
// var html = "<div class='repository-input-file-field'>" +
// "<div class='form-group'><div><input type='file' name='" + name + "' id='" +
// id + "' style='display:none' /><button class='btn btn-default' " +
// "data-object='" + object + "' name='" + name + "' value='" + value +
// "' data-id='" + id + "'>" + buttonLabel +
// "</button></div><div><p class='file-name-label'>" + truncateLongString(fileName, 20) +
// "</p></div>";
// if(value.file_name) {
// html += "<div><a data-action='removeAsset' ";
// html += "onClick='clearFileInput(this)'><i class='fas fa-times'></i></a>";
// } else {
// html += "<div><a data-action='removeAsset' onClick='clearFileInput(this)' ";
// html += "style='display:none'><i class='fas fa-times'></i></a>";
// }
// html += "</div></div></div>";
//
// return html;
// }
//
// /**
// * Returns the colum index
// *
// * @param {Object} table
// * @param {String} id
// *
// * @returns (Boolean | Number)
// */
// function getColumnIndex(table, id) {
// if(id < 0)
// return false;
// return table.column(id).index('visible');
// }
//
// /**
// * Genrates list items dropdown element
// *
// * @param {Array} options
// * @param {String} current_value
// * @param {Number} columnId
// * @param {String} id
// *
// * @returns (String)
// */
// function _listItemDropdown(options, current_value, columnId, id) {
// var val = undefined;
// var html = '<select id="' + id + '" class="form-control selectpicker repository-dropdown" ';
// html += 'data-selected-value="" data-abs-min-length="2" data-live-search="true" ';
// html += 'data-container="body" column_id="' + columnId +'">';
// html += '<option value="-1"></option>';
// $.each(options, function(index, value) {
// var selected = '';
// if (current_value === value[1]) {
// selected = 'selected';
// val = value[0];
// }
// html += '<option value="' + value[0] + '" ' + selected + '>';
// html += value[1] + '</option>';
// });
// html += '</select>';
// return (val) ? $(html).attr('data-selected-value', val)[0] : html;
// }
//
// /**
// * Takes an object and creates custom html element
// *
// * @param {String} object
// * @param {String} name
// * @param {String} column_type
// * @param {Object} cell
// * @param {Object} listColumns
// *
// * @returns (String)
// */
// function changeToFormField(object, name, column_type, cell, listColumns) {
// var cellId = generateInputFieldReference(name);
// var value = cell.value || '';
// if (column_type === 'RepositoryListValue') {
// var column = _.findWhere(listColumns,
// { column_id: parseInt(name, 10) });
// var list_items = column.list_items || cell.list_items;
// return _listItemDropdown(list_items, value, parseInt(name, 10), cellId);
// } else if (column_type === 'RepositoryAssetValue') {
// return changeToInputFileField('repository_cell_file', name, value, cellId);
// } else {
// return changeToInputField(object, name, value, cellId);
// }
// }
//
// /**
// * Append the change listener to file field
// *
// * @param {String} type
// * @param {String} name
// *
// * @returns {undefined}
// */
// function addSelectedFile(type, name) {
// var button = $('button[data-id="' +
// generateInputFieldReference(name) +
// '"]');
// if (type === 'RepositoryAssetValue') {
// var fileInput = $(button.parent().find('input[type="file"]')[0]);
// button.on('click', function(ev) {
// ev.preventDefault();
// ev.stopPropagation();
// fileInput.trigger('click');
// initFileHandler(fileInput);
// });
// }
// }
//
// /**
// * Handle extraction of file from the input field
// *
// * @param {Object} $inputField
// *
// * @returns {undefined}
// */
// function initFileHandler($inputField) {
// $inputField.on('change', function() {
// var input = $(this);
// var $label = $($(this).closest('.repository-input-file-field')
// .find('.file-name-label')[0]);
// var file = this.files[0];
// if (file) {
// $label.text(truncateLongString(file.name, 20));
// input.attr('remove', false);
// $($label.closest('.repository-input-file-field')
// .find('[data-action="removeAsset"]')[0]).show();
// }
// })
// }
//
// /**
// * Generates the input tag id that will be used in the formData object
// *
// * @param {String} columnId
// *
// * @returns {String}
// */
// function generateInputFieldReference(columnId) {
// return 'colId-' + columnId;
// }
//
// /**
// * Appends aditional fields to form data object
// * @param {Object} cell
// * @param {String} columnId
// * @param {Object} formData
// *
// * @returns {undefined}
// */
// function appendNewElementToFormData(cell, columnId, formData) {
// if (!cell.repository_cell_id) {
// formData[generateInputFieldReference(columnId)] = undefined;
// }
// }
// }(window));

View file

@ -0,0 +1,32 @@
$.fn.dataTable.render.editRowName = function(formId, cell) {
let $cell = $(cell.node());
let text = $cell.find('a').first().text();
$cell.html(`
<div class="form-group">
<input class="form-control editing"
form="${formId}"
type="text"
name="repository_row_name"
value="${text}"
data-type="RowName">
</div>
`);
};
$.fn.dataTable.render.editRepositoryAssetValue = function(formId, columnId, cell) {
return '';
};
$.fn.dataTable.render.editRepositoryTextValue = function(formId, columnId, cell) {
SmartAnnotation.init(cell);
return '';
};
$.fn.dataTable.render.editRepositoryListValue = function(formId, columnId, cell) {
return '';
};
$.fn.dataTable.render.editRepositoryStatusValue = function(formId, columnId, cell) {
return '';
};

View file

@ -0,0 +1,24 @@
$.fn.dataTable.render.newRowName = function(formId) {
return `<input form="${formId}"
type="text"
name="repository_row_name"
class="editing"
value=""
data-type="RowName">`;
};
$.fn.dataTable.render.newRepositoryAssetValue = function(formId, columnId) {
return '';
};
$.fn.dataTable.render.newRepositoryTextValue = function(formId, columnId) {
return '';
};
$.fn.dataTable.render.newRepositoryListValue = function(formId, columnId) {
return '';
};
$.fn.dataTable.render.newRepositoryStatusValue = function(formId, columnId) {
return '';
};

View file

@ -1,31 +1,27 @@
/*
globals I18n _ SmartAnnotation FilePreviewModal truncateLongString animateSpinner Promise
ActiveStorage HelperModule animateLoading RepositoryItemEditForm onClickCancel
hideAssignUnasignModal
HelperModule animateLoading onClickCancel
hideAssignUnasignModal RepositoryDatatableRowEditor
*/
//= require jquery-ui/widgets/sortable
//= require repositories/forms/repository_item_edit.js
//= require repositories/row_editor.js
var RepositoryDatatable = (function(global) {
'use strict';
var TABLE_ID = '';
var TABLE_WRAPPER = $('.repository-table');
var TABLE = null;
var SCINOTE_REPOSITORY_EDITED_ROWS = []; // an array of edited rows
var EDITABLE = false;
var rowsSelected = [];
// Tells whether we're currently viewing or editing table
var currentMode = 'viewMode';
// Tells what action will execute by pressing on save button (update/create)
var saveAction = 'update';
var selectedRecord;
// Helps saving correct table state
var myData;
var loadFirstTime = true;
// var selectedRecord;
// Tells whether to filter only assigned repository records
var viewAssigned;
@ -50,22 +46,11 @@ var RepositoryDatatable = (function(global) {
return value;
});
function changeToViewMode() {
currentMode = 'viewMode';
// Table specific stuff
TABLE.button(0).enable(true);
FilePreviewModal.init();
}
function changeToEditMode() {
currentMode = 'editMode';
// Table specific stuff
TABLE.button(0).enable(false);
}
// Enable/disable edit button
function updateButtons() {
if (currentMode === 'viewMode') {
$('#saveCancel').hide();
$('#editDeleteCopy').show();
$('#addRepositoryRecord').removeClass('disabled').prop('disabled', false);
$('.dataTables_length select').prop('disabled', false);
$('#repository-acitons-dropdown').removeClass('disabled').prop('disabled', false);
@ -108,6 +93,8 @@ var RepositoryDatatable = (function(global) {
$('#unassignRepositoryRecords').removeClass('disabled').prop('disabled', false);
}
} else if (currentMode === 'editMode') {
$('#editDeleteCopy').hide();
$('#saveCancel').show();
$('#repository-acitons-dropdown').addClass('disabled').prop('disabled', true);
$('.dataTables_length select').prop('disabled', true);
$('#addRepositoryRecord').addClass('disabled').prop('disabled', true);
@ -124,6 +111,28 @@ var RepositoryDatatable = (function(global) {
}
}
function clearRowSelection() {
$('.dt-body-center .repository-row-selector').prop('checked', false);
$('.dt-body-center .repository-row-selector').closest('tr').removeClass('selected');
rowsSelected = [];
}
function changeToViewMode() {
currentMode = 'viewMode';
// Table specific stuff
TABLE.button(0).enable(true);
FilePreviewModal.init();
updateButtons();
}
function changeToEditMode() {
currentMode = 'editMode';
// Table specific stuff
TABLE.button(0).enable(false);
clearRowSelection();
updateButtons();
}
// Updates "Select all" control in a data table
function updateDataTableSelectAllCtrl() {
var $table = TABLE.table().node();
@ -170,23 +179,9 @@ var RepositoryDatatable = (function(global) {
return html;
}
function addSelectedFile(type, cell, input) {
if (type === 'RepositoryAssetValue') {
if (cell.value != null) {
const dT = new ClipboardEvent('').clipboardData // Firefox workaround exploiting https://bugzilla.mozilla.org/show_bug.cgi?id=1422655
|| new DataTransfer(); // specs compliant (as of March 2018 only Chrome)
dT.items.add(new File([_], cell.value.file_name));
input.files = dT.files;
}
$(input).on('change', function() {
this.dataset.changed = 'true';
});
}
}
function initRowSelection() {
// Handle clicks on checkbox
$('.dt-body-center .repository-row-selector').change(function(e) {
$('.dt-body-center .repository-row-selector').change(function(ev) {
var $row;
var data;
var rowId;
@ -219,21 +214,77 @@ var RepositoryDatatable = (function(global) {
updateDataTableSelectAllCtrl();
e.stopPropagation();
ev.stopPropagation();
updateButtons();
// Update number of selected records info
$('#selected_info').html(' (' + rowsSelected.length + ' entries selected)');
});
// Handle click on "Select all" control
$('.dataTables_scrollHead input[name="select_all"]').change(function(e) {
$('.dataTables_scrollHead input[name="select_all"]').change(function(ev) {
if (this.checked) {
$('.repository-row-selector:not(:checked)').trigger('click');
} else {
$('.repository-row-selector:checked').trigger('click');
}
// Prevent click event from propagating to parent
e.stopPropagation();
ev.stopPropagation();
});
}
function checkAvailableColumns() {
$.ajax({
url: $(TABLE_ID).data('available-columns'),
type: 'GET',
dataType: 'json',
success: function(data) {
var columnsIds = data.columns;
var presentColumns = $(TABLE_ID).data('repository-columns-ids');
if (!_.isEqual(columnsIds.sort(), presentColumns.sort())) {
alert($(TABLE_ID).data('columns-changed'));
animateSpinner();
location.reload();
}
},
error: function() {
location.reload();
}
});
}
function initItemEditIcon() {
$(TABLE_ID).on('click', '.repository-row-edit-icon', function(ev) {
let rowId = $(ev.target).closest('tr').attr('id');
let row = TABLE.row(`#${rowId}`);
$(row.node()).find('.repository-row-selector').trigger('click');
checkAvailableColumns();
$(TABLE_ID).find('.repository-row-edit-icon').remove();
RepositoryDatatableRowEditor.switchRowToEditMode(row);
changeToEditMode();
});
}
function initSaveButton() {
TABLE_WRAPPER.on('click', '#saveRecord', function() {
$(TABLE_ID).find('.repository-row-edit-form').submit();
});
}
function initCancelButton() {
TABLE_WRAPPER.on('click', '#cancelSave', function() {
if ($('#assigned').text().length === 0) {
TABLE.column(1).visible(false);
}
TABLE.ajax.reload(function() {
initRowSelection();
}, false);
changeToViewMode();
SmartAnnotation.closePopup();
animateSpinner(null, false);
});
}
@ -244,10 +295,10 @@ var RepositoryDatatable = (function(global) {
}
// Takes object and surrounds it with input
function changeToInputField(object, name, value) {
return "<div class='form-group'><input class='form-control' data-object='"
+ object + "' name='" + name + "' value='" + value + "'></input></div>";
}
// function changeToInputField(object, name, value) {
// return "<div class='form-group'><input class='form-control' data-object='"
// + object + "' name='" + name + "' value='" + value + "'></input></div>";
// }
// Takes object and surrounds it with input
function changeToInputFileField(object, name, value) {
@ -357,13 +408,6 @@ var RepositoryDatatable = (function(global) {
});
}
// Return td element with content
function createTdElement(content) {
var td = document.createElement('td');
td.innerHTML = content;
return td;
}
function initialListItemsRequest(columnId) {
var massageResponse = [];
$.ajax({
@ -443,22 +487,16 @@ var RepositoryDatatable = (function(global) {
}
// Clear all has-error tags
function clearAllErrors() {
// Remove any validation errors
$(selectedRecord)
.find('.has-error')
.removeClass('has-error')
.find('span')
.remove();
// Remove any alerts
$('#alert-container').find('div').remove();
}
function clearRowSelection() {
$('.dt-body-center .repository-row-selector').prop('checked', false);
$('.dt-body-center .repository-row-selector').closest('tr').removeClass('selected');
rowsSelected = [];
}
// function clearAllErrors() {
// // Remove any validation errors
// $(selectedRecord)
// .find('.has-error')
// .removeClass('has-error')
// .find('span')
// .remove();
// // Remove any alerts
// $('#alert-container').find('div').remove();
// }
function dataTableInit() {
viewAssigned = 'assigned';
@ -471,6 +509,7 @@ var RepositoryDatatable = (function(global) {
sScrollXInner: '100%',
scrollY: '64vh',
scrollCollapse: true,
order: [[2, 'asc']],
colReorder: {
fixedColumnsLeft: 2,
realtime: false
@ -499,7 +538,14 @@ var RepositoryDatatable = (function(global) {
targets: 1,
searchable: false,
orderable: true,
sWidth: '1%'
sWidth: '1%',
render: function(data) {
let content = data;
if (EDITABLE) {
content = '<i class="repository-row-edit-icon fas fa-pencil-alt"></i>' + content;
}
return content;
}
}, {
// Name column is clickable
targets: 3,
@ -522,21 +568,16 @@ var RepositoryDatatable = (function(global) {
},
rowCallback: function(row, data) {
// Get row ID
var rowId = data.DT_RowId;
let rowId = data.DT_RowId;
// If row ID is in the list of selected row IDs
if ($.inArray(rowId, rowsSelected) !== -1) {
$(row).find('input[type="checkbox"]').prop('checked', true);
$(row).addClass('selected');
}
},
order: $(TABLE_ID).data('default-order'),
columns: (function() {
var columns = $(TABLE_ID).data('default-table-columns');
var customColumns = $(TABLE_ID).find('thead th[data-type]');
for (let i = 0; i < columns.length; i += 1) {
columns[i].data = String(i);
columns[i].defaultContent = '';
}
customColumns.each((i, column) => {
columns.push({
visible: true,
@ -547,10 +588,9 @@ var RepositoryDatatable = (function(global) {
});
return columns;
}()),
fnDrawCallback: function() {
drawCallback: function() {
animateSpinner(this, false);
changeToViewMode();
updateButtons();
updateDataTableSelectAllCtrl();
FilePreviewModal.init();
// Prevent row toggling when selecting user smart annotation link
@ -565,101 +605,66 @@ var RepositoryDatatable = (function(global) {
animateSpinner(this);
$('.record-info-link').off('click');
},
stateLoadCallback: function() {
// Send an Ajax request to the server to get the data. Note that
// this is a synchronous request since the data is expected back from the
// function
stateLoadCallback: function(settings, callback) {
var repositoryId = $(TABLE_ID).data('repository-id');
$.ajax({
url: '/repositories/' + repositoryId + '/state_load',
data: {},
async: false,
dataType: 'json',
type: 'POST',
success: function(json) {
myData = json.state;
// Fix the order - convert it from index-keyed JS object that
// is returned from the server state into true JS array;
// e.g. {0: [2, 'asc'], 1: [3, 'desc']}
// is converted into [[2, 'asc'], [3, 'desc']]
myData.order = _.toArray(myData.order);
callback(json.state);
}
});
return myData;
},
stateSaveCallback: function(settings, data) {
var stateData = data;
// Send an Ajax request to the server with the state object
var repositoryId = $(TABLE_ID).data('repository-id');
// Save correct data
if (loadFirstTime === true) {
stateData = myData;
}
let repositoryId = $(TABLE_ID).data('repository-id');
$.ajax({
url: '/repositories/' + repositoryId + '/state_save',
data: { state: stateData },
contentType: 'application/json',
data: JSON.stringify({ state: data }),
dataType: 'json',
type: 'POST'
});
loadFirstTime = false;
},
fnInitComplete: function(oSettings) {
// First two columns are fixed
TABLE.column(0).visible(true);
TABLE.column(1).visible(true);
// Reload correct column order and visibility (if you refresh page)
for (let i = 2; i < TABLE.columns()[0].length; i += 1) {
let visibility = false;
if (myData.columns && myData.columns[i]) {
visibility = myData.columns[i].visible;
}
if (typeof (visibility) === 'string') {
visibility = (visibility === 'true');
}
TABLE.column(i).visible(visibility);
TABLE.setColumnSearchable(i, visibility);
}
// Re-order table as per loaded state
if (myData.order) {
TABLE.order(myData.order);
TABLE.draw();
}
// Datatables triggers this action about 3 times
// sometimes on the first iteration the oSettings._colReorder is null
// and the fnOrder rises an error that breaks the table
// here I added a null guard for that case.
// @todo we need to find out why the tables are loaded multiple times
if (oSettings._colReorder) {
oSettings._colReorder.fnOrder(myData.ColReorder);
}
fnInitComplete: function() {
initRowSelection();
bindExportActions();
disableCheckboxToggleOnAssetDownload();
FilePreviewModal.init();
initHeaderTooltip();
// Append button to inner toolbar in table
$('div.toolbarButtonsDatatable').appendTo('div.toolbar');
$('div.toolbarButtonsDatatable').show();
// Append buttons for task inventory
$('div.toolbarButtons').appendTo('div.toolbar');
$('div.toolbarButtons').show();
if (EDITABLE) {
RepositoryDatatableRowEditor.initFormSubmitAction(TABLE);
initItemEditIcon();
initSaveButton();
initCancelButton();
}
}
});
// hack to replace filter placeholder
$('.dataTables_filter .form-control').attr('placeholder', $('.dataTables_filter label').text())
$('.dataTables_filter label').contents().filter(function(){
$('.dataTables_filter .form-control').attr('placeholder', $('.dataTables_filter label').text());
$('.dataTables_filter label').contents().filter(function() {
return this.nodeType === 3;
}).remove();
// Handle click on table cells with checkboxes
$(TABLE_ID).on('click', 'tbody td', function(e) {
if ($(e.target).is('.repository-row-selector')) {
// Skip if clicking on selector checkbox
return;
}
if (!$(e.target).is('.record-info-link')) {
// Skip if clicking on samples info link
$(this).parent().find('.repository-row-selector').trigger('click');
}
$(TABLE_ID).on('click', 'tbody td', function(ev) {
// Skip if clicking on selector checkbox, edit icon or link
if ($(ev.target).is('.repository-row-selector, .repository-row-edit-icon, a')) return;
$(this).parent().find('.repository-row-selector').trigger('click');
});
TABLE.on('column-reorder', function() {
@ -681,107 +686,15 @@ var RepositoryDatatable = (function(global) {
// Append buttons for task inventory
$('div.toolbarButtons').appendTo('div.toolbar');
$('div.toolbarButtons').show();
}, 10);
}, 100);
return TABLE;
}
function checkAvailableColumns() {
$.ajax({
url: $(TABLE_ID).data('available-columns'),
type: 'GET',
dataType: 'json',
success: function(data) {
var columnsIds = data.columns;
var presentColumns = $(TABLE_ID).data('repository-columns-ids');
if (!_.isEqual(columnsIds.sort(), presentColumns.sort())) {
alert($(TABLE_ID).data('columns-changed'));
animateSpinner();
location.reload();
}
},
error: function() {
location.reload();
}
});
}
// Restore previous table
global.onClickCancel = function() {
if ($('#assigned').text().length === 0) {
TABLE.column(1).visible(false);
}
TABLE.ajax.reload(function() {
initRowSelection();
}, false);
changeToViewMode();
updateButtons();
SmartAnnotation.closePopup();
SCINOTE_REPOSITORY_EDITED_ROWS = [];
animateSpinner(null, false);
};
global.onClickAddRecord = function() {
var tr = document.createElement('tr');
checkAvailableColumns();
RepositoryDatatableRowEditor.addNewRow(TABLE);
changeToEditMode();
updateButtons();
saveAction = 'create';
if (TABLE.column(1).visible() === false) {
TABLE.column(1).visible(true);
}
$('table' + TABLE_ID + ' thead tr').children('th').each(function() {
var th = $(this);
var td;
var input;
if ($(th).attr('id') === 'checkbox') {
td = createTdElement('');
$(td).html($('#saveRecord').clone());
tr.appendChild(td);
} else if ($(th).attr('id') === 'assigned') {
td = createTdElement('');
$(td).html($('#cancelSave').clone());
tr.appendChild(td);
} else if ($(th).attr('id') === 'row-name') {
input = changeToInputField('repository_row', 'name', '');
tr.appendChild(createTdElement(input));
} else if ($(th).hasClass('repository-column')
&& $(th).attr('data-type') === 'RepositoryTextValue') {
input = changeToInputField('repository_cell', th.attr('id'), '');
tr.appendChild(createTdElement(input));
} else if ($(th).hasClass('repository-column')
&& $(th).attr('data-type') === 'RepositoryListValue') {
input = initialListItemsRequest($(th).attr('id'));
tr.appendChild(createTdElement(input));
} else if ($(th).hasClass('repository-column')
&& $(th).attr('data-type') === 'RepositoryAssetValue') {
input = changeToInputFileField('repository_cell_file', th.attr('id'), '');
td = createTdElement(input);
tr.appendChild(td);
addSelectedFile($(th).attr('data-type'), '', $(td).find('input')[0]);
} else {
// Column we don't care for, just add empty td
tr.appendChild(createTdElement(''));
}
});
$('table' + TABLE_ID).prepend(tr);
selectedRecord = tr;
// initialize smart annotation
_.each($('[data-object="repository_cell"]'), function(el) {
if (_.isUndefined($(el).data('atwho'))) {
SmartAnnotation.init(el);
}
});
// Init selectpicker
initSelectPicker();
// Adjust columns width in table header
adjustTableHeader();
};
global.onClickToggleAssignedRecords = function() {
@ -903,10 +816,10 @@ var RepositoryDatatable = (function(global) {
rowsSelected = [];
onClickCancel();
},
error: function(e) {
if (e.status === 403) {
error: function(ev) {
if (ev.status === 403) {
HelperModule.flashAlertMsg(
I18n.t('repositories.js.permission_error'), e.responseJSON.style
I18n.t('repositories.js.permission_error'), ev.responseJSON.style
);
}
}
@ -925,10 +838,10 @@ var RepositoryDatatable = (function(global) {
rowsSelected = [];
onClickCancel();
},
error: function(e) {
if (e.status === 403) {
error: function(ev) {
if (ev.status === 403) {
HelperModule.flashAlertMsg(
I18n.t('repositories.js.permission_error'), e.responseJSON.style
I18n.t('repositories.js.permission_error'), ev.responseJSON.style
);
}
}
@ -937,232 +850,96 @@ var RepositoryDatatable = (function(global) {
// Edit record
global.onClickEdit = function() {
var row;
var node;
var rowData;
checkAvailableColumns();
if (rowsSelected.length !== 1) {
return;
}
row = TABLE.row('#' + rowsSelected[0]);
node = row.node();
rowData = row.data();
let row = TABLE.row('#' + rowsSelected[0]);
$(node).find('td input').trigger('click');
selectedRecord = node;
$(TABLE_ID).find('.repository-row-edit-icon').remove();
clearAllErrors();
changeToEditMode();
updateButtons();
saveAction = 'update';
$.ajax({
url: rowData.recordEditUrl,
type: 'GET',
dataType: 'json',
success: function(data) {
var editForm = new RepositoryItemEditForm(data, node);
if (TABLE.column(1).visible() === false) {
TABLE.column(1).visible(true);
}
// Show save and cancel buttons in first two columns
$(node).children('td').eq(0).html($('#saveRecord').clone());
$(node).children('td').eq(1).html($('#cancelSave').clone());
editForm.renderForm(TABLE);
initSelectPicker();
editForm.initializeSelectpickerValues(node);
// initialize smart annotation
_.each($('[data-object="repository_cell"]'), function(el) {
if (_.isUndefined($(el).data('atwho'))) {
SmartAnnotation.init(el);
}
});
// Adjust columns width in table header
adjustTableHeader();
updateButtons();
SCINOTE_REPOSITORY_EDITED_ROWS.push(editForm);
},
error: function(e) {
if (e.status === 403) {
HelperModule.flashAlertMsg(
I18n.t('repositories.js.permission_error'), e.responseJSON.style
);
changeToViewMode();
updateButtons();
}
}
});
RepositoryDatatableRowEditor.switchRowToEditMode(row);
adjustTableHeader();
};
function submitForm(url, formData) {
var type;
if (saveAction === 'update') {
type = 'PUT';
} else {
type = 'POST';
}
$.ajax({
url: url,
type: type,
dataType: 'json',
data: formData,
processData: false,
contentType: false,
success: function(data) {
HelperModule.flashAlertMsg(data.flash, 'success');
SmartAnnotation.closePopup();
SCINOTE_REPOSITORY_EDITED_ROWS = [];
onClickCancel();
animateSpinner(null, false);
},
error: function(e) {
var data = e.responseJSON;
animateSpinner(null, false);
SmartAnnotation.closePopup();
clearAllErrors();
if (e.status === 404) {
HelperModule.flashAlertMsg(
I18n.t('repositories.js.not_found_error'), 'danger'
);
changeToViewMode();
updateButtons();
} else if (e.status === 403) {
HelperModule.flashAlertMsg(
I18n.t('repositories.js.permission_error'), 'danger'
);
changeToViewMode();
updateButtons();
} else if (e.status === 400) {
if (data.default_fields) {
let defaultFields = data.default_fields;
// Validate record name
if (defaultFields.name) {
let input = $(selectedRecord).find('input[name = name]');
if (input) {
input.closest('.form-group').addClass('has-error');
input.parent().append("<span class='help-block'>" + defaultFields.name + '<br /></span>');
}
}
}
// Validate custom cells
$.each(data.repository_cells || [], function(_, val) {
$.each(val, function(key, val2) {
let input = $(selectedRecord).find('input[name=' + key + ']');
if (input) {
let message = Array.isArray(val2.data) ? val2.data[0] : val2.data;
// handle custom input field
if (input.attr('type') === 'file') {
let container = input.closest('.repository-input-file-field');
$(container.find('.form-group')[0]).addClass('has-error');
container.addClass('has-error');
container.append("<span class='help-block'>" + message + '<br /></span>');
} else {
input.closest('.form-group').addClass('has-error');
input.parent().append("<span class='help-block'>" + message + '<br /></span>');
}
}
});
});
}
}
});
}
function buildNewFormData() {
return new Promise((resolve, reject) => {
var node = selectedRecord;
var formData = new FormData();
var filesToUploadCntr = 0;
var filesUploadedCntr = 0;
const directUploadUrl = $(TABLE_ID).data('directUploadUrl');
// First fetch all the data in input fields
formData.append('request_url', $(TABLE_ID).data('current-uri'));
formData.append('repository_row_id', $(selectedRecord).attr('id'));
// Direct record attributes
// Record name
formData.append('repository_row_name', $('td input[data-object = repository_row]').val());
// Custom cells text type
$(node).find('td input[data-object = repository_cell]').each(function() {
// Send data only and only if cell is not empty
if ($(this).val().trim()) {
formData.append('repository_cells[' + $(this).attr('name') + ']', $(this).val());
}
});
// Custom cells list type
$(node).find('td[column_id]').each(function(index, el) {
var value = $(el).attr('list_item_id');
formData.append('repository_cells[' + $(el).attr('column_id') + ']', value);
});
// Custom cells file type, first run, count files ready for upload
$(node).find('td input[data-object = repository_cell_file]').each(function() {
// Send data only and only if cell is not empty
if ($(this)[0].files.length === 1) {
filesToUploadCntr += 1;
}
});
// No files for upload, so return earlier
if (filesToUploadCntr === 0) {
resolve(formData);
return;
}
// Custom cells file type, second run, upload files
$(node).find('td input[data-object = repository_cell_file]').each(function() {
// Send data only and only if cell is not empty
if ($(this)[0].files.length === 1) {
let upload = new ActiveStorage.DirectUpload($(this)[0].files[0], directUploadUrl);
let colId = $(this).attr('name');
upload.create(function(error, blob) {
if (error) {
reject(error);
} else {
formData.append('repository_cells[' + colId + ']', blob.signed_id);
filesUploadedCntr += 1;
if (filesUploadedCntr === filesToUploadCntr) {
resolve(formData);
}
}
});
}
});
});
}
// Save record
global.onClickSave = function() {
animateSpinner(null, true);
if (saveAction === 'update') {
let row = TABLE.row(selectedRecord);
let rowData = row.data();
SCINOTE_REPOSITORY_EDITED_ROWS[0].parseToFormObject(
TABLE_ID, selectedRecord
).then(formData => {
submitForm(rowData.recordUpdateUrl, formData);
});
} else if (saveAction === 'create') {
buildNewFormData().then(formData => {
submitForm($('table' + TABLE_ID).data('create-record'), formData);
});
}
};
// function submitForm(url, formData) {
// var type;
// if (saveAction === 'update') {
// type = 'PUT';
// } else {
// type = 'POST';
// }
// $.ajax({
// url: url,
// type: type,
// dataType: 'json',
// data: formData,
// processData: false,
// contentType: false,
// success: function(data) {
// HelperModule.flashAlertMsg(data.flash, 'success');
// SmartAnnotation.closePopup();
// SCINOTE_REPOSITORY_EDITED_ROWS = [];
// onClickCancel();
// animateSpinner(null, false);
// },
// error: function(ev) {
// var data = ev.responseJSON;
// animateSpinner(null, false);
// SmartAnnotation.closePopup();
// clearAllErrors();
//
// if (ev.status === 404) {
// HelperModule.flashAlertMsg(
// I18n.t('repositories.js.not_found_error'), 'danger'
// );
// changeToViewMode();
// } else if (ev.status === 403) {
// HelperModule.flashAlertMsg(
// I18n.t('repositories.js.permission_error'), 'danger'
// );
// changeToViewMode();
// } else if (ev.status === 400) {
// if (data.default_fields) {
// let defaultFields = data.default_fields;
//
// // Validate record name
// if (defaultFields.name) {
// let input = $(selectedRecord).find('input[name = name]');
//
// if (input) {
// input.closest('.form-group').addClass('has-error');
// input.parent().append("<span class='help-block'>" + defaultFields.name + '<br /></span>');
// }
// }
// }
//
// // Validate custom cells
// $.each(data.repository_cells || [], function(_, val) {
// $.each(val, function(key, val2) {
// let input = $(selectedRecord).find('input[name=' + key + ']');
// if (input) {
// let message = Array.isArray(val2.data) ? val2.data[0] : val2.data;
// // handle custom input field
// if (input.attr('type') === 'file') {
// let container = input.closest('.repository-input-file-field');
// $(container.find('.form-group')[0]).addClass('has-error');
// container.addClass('has-error');
// container.append("<span class='help-block'>" + message + '<br /></span>');
// } else {
// input.closest('.form-group').addClass('has-error');
// input.parent().append("<span class='help-block'>" + message + '<br /></span>');
// }
// }
// });
// });
// }
// }
// });
// }
// Delete record
global.onClickDelete = function() {
@ -1343,6 +1120,7 @@ var RepositoryDatatable = (function(global) {
function init(id) {
TABLE_ID = id;
EDITABLE = $(TABLE_ID).data('editable');
TABLE = dataTableInit();
initDropdown();
}

View file

@ -0,0 +1,146 @@
/*
globals HelperModule animateSpinner SmartAnnotation
*/
var RepositoryDatatableRowEditor = (function() {
const NAME_COLUMN_ID = 'row-name';
const TABLE_ROW = '<tr></tr>';
const TABLE_CELL = '<td></td>';
var TABLE;
// Initialize SmartAnnotation
function initSmartAnnotation($row) {
$row.find('[data-object="repository_cell"]').each(function(el) {
if (el.data('atwho')) {
SmartAnnotation.init(el);
}
});
}
function initFormSubmitAction(table) {
TABLE = table;
let $table = $(TABLE.table().node());
$table.on('ajax:beforeSend', '.repository-row-edit-form', function() {
let $row = $(this).closest('tr');
let valid = true;
$row.find('.has-error').removeClass('has-error').find('span').remove();
$row.find('input').each(function() {
let dataType = $(this).data('type');
if (!dataType) return;
valid = $.fn.dataTable.render[dataType + 'Validator']($(this));
});
if (!valid) return false;
animateSpinner(null, true);
});
$table.on('ajax:success', '.repository-row-edit-form', function(ev, data) {
TABLE.ajax.reload();
HelperModule.flashAlertMsg(data.flash, 'success');
});
$table.on('ajax:error', '.repository-row-edit-form', function(ev, data) {
HelperModule.flashAlertMsg(data.responseJSON.flash, 'danger');
});
$table.on('ajax:complete', '.repository-row-edit-form', function() {
animateSpinner(null, false);
});
}
function addNewRow(table) {
TABLE = table;
let $row = $(TABLE_ROW);
const formId = 'repositoryNewRowForm';
let actionUrl = $(TABLE.table().node()).data('createRecord');
let rowForm = $(`
<form id="${formId}"
class="repository-row-edit-form"
action="${actionUrl}"
method="post"
data-remote="true">
</form>
`);
$row.prepend(rowForm);
// First two columns are always present and visible
$row.append($(TABLE_CELL));
$row.append($(TABLE_CELL));
table.columns().every(function() {
let column = this;
let $header = $(column.header());
if (column.index() < 2) return;
if (!column.visible()) return;
let columnId = $header.attr('id');
if (columnId === NAME_COLUMN_ID) {
$row.append($(TABLE_CELL).html($.fn.dataTable.render.newRowName(formId)));
} else {
let dataType = $header.data('type');
if (dataType) {
$row.append($(TABLE_CELL).html($.fn.dataTable.render['new' + dataType](formId, columnId)));
} else {
$row.append($(TABLE_CELL));
}
}
});
$(TABLE.table().node()).find('tbody').prepend($row);
initSmartAnnotation($row);
TABLE.columns.adjust();
}
function switchRowToEditMode(row) {
let $row = $(row.node());
let itemId = row.id();
let formId = `repositoryRowForm${itemId}`;
let rowForm = $(`
<form id="${formId}"
class="repository-row-edit-form"
action="${row.data().recordUpdateUrl}"
method="patch"
data-remote="true"
data-row-id="${itemId}">
<input name="id" type="hidden" value="${itemId}" />
</form>
`);
$row.prepend(rowForm);
TABLE.cells(row.index(), row.columns().eq(0)).every(function() {
let columnId = $(TABLE.columns(this.index().column).header()).attr('id');
let cell = this;
if (columnId === NAME_COLUMN_ID) {
$.fn.dataTable.render.editRowName(formId, cell);
} else {
let dataType = $(this.column().header()).data('type');
if (dataType) $.fn.dataTable.render['edit' + dataType](formId, columnId, cell);
}
return true;
});
initSmartAnnotation($row);
TABLE.columns.adjust();
}
return Object.freeze({
initFormSubmitAction: initFormSubmitAction,
switchRowToEditMode: switchRowToEditMode,
addNewRow: addNewRow
});
}());

View file

@ -0,0 +1,5 @@
/* global GLOBAL_CONSTANTS textValidator */
$.fn.dataTable.render.RowNameValidator = function($input) {
return textValidator(undefined, $input, 1, GLOBAL_CONSTANTS.NAME_MAX_LENGTH);
};

View file

@ -83,6 +83,28 @@
}
}
.repository-table {
tbody {
tr:hover {
background-color: $color-concrete;
}
.editing {
border: 1px solid;
}
.repository-row-edit-icon {
opacity: 0;
padding-right: 10px;
}
tr:hover .repository-row-edit-icon {
cursor: pointer;
opacity: 1;
}
}
}
.new-input-file-field-div {
display: flex;
flex-direction: row;

View file

@ -56,14 +56,6 @@ module RepositoryDatatableHelper
can_manage_repository_rows?(repository)
end
# The order must be converted from Ruby Hash into a JS array -
# because arrays in JS are in truth regular JS objects with indexes as keys
def default_table_order_as_js_array
Constants::REPOSITORY_TABLE_DEFAULT_STATE[:order].keys.sort.map do |k|
Constants::REPOSITORY_TABLE_DEFAULT_STATE[:order][k]
end.to_s
end
def default_table_columns
Constants::REPOSITORY_TABLE_DEFAULT_STATE[:columns].keys.sort.map do |k|
col = Constants::REPOSITORY_TABLE_DEFAULT_STATE[:columns][k]

View file

@ -18,14 +18,17 @@ class RepositoryTableStateService
loaded.state['order'] &&
loaded.state['columns'] &&
loaded.state['ColReorder'] &&
loaded.state.dig('columns', '1', 'visible') == 'true' &&
loaded.state.dig('columns', '3', 'visible') == 'true'
loaded.state.dig('columns', 1, 'visible') == true &&
loaded.state.dig('columns', 3, 'visible') == true
loaded
end
def update_state(state)
saved_state = load_state
state[:order][0] = [3, 'asc'] if state.dig(:order, 0, 0).to_i < 2
return if saved_state.state.except('time') == state.except('time')
saved_state.update(state: state)
end
@ -33,7 +36,7 @@ class RepositoryTableStateService
# Destroy any state object before recreating a new one
RepositoryTableState.where(user: @user, repository: @repository).destroy_all
return RepositoryTableState.create(
RepositoryTableState.create(
user: @user,
repository: @repository,
state: generate_default_state
@ -47,9 +50,7 @@ class RepositoryTableStateService
Constants::REPOSITORY_TABLE_DEFAULT_STATE[:length]
# This state should be strings-only
state = HashUtil.deep_stringify_keys_and_values(
Constants::REPOSITORY_TABLE_DEFAULT_STATE
)
state = Constants::REPOSITORY_TABLE_DEFAULT_STATE.with_indifferent_access
repository.repository_columns.each_with_index do |_, index|
real_index = default_columns_num + index
state['columns'][real_index.to_s] =

View file

@ -17,9 +17,9 @@
data-columns-delete-text="<%= I18n.t('repositories.columns_delete') %>"
data-available-columns="<%= repository_available_columns_path(repository) %>"
data-columns-changed="<%= I18n.t('repositories.columns_changed') %>"
data-default-order="<%= default_table_order_as_js_array %>"
data-default-table-columns="<%= default_table_columns %>"
data-list-items-path="<%= Rails.application.routes.url_helpers.repository_list_items_path %>">
data-list-items-path="<%= Rails.application.routes.url_helpers.repository_list_items_path %>"
data-editable="<%= can_manage_repository_rows?(repository) %>">
<thead>
<tr>
<th id="checkbox"><input name="select_all" value="1" type="checkbox"></th>

View file

@ -103,17 +103,6 @@
</div>
</div>
<div class="btn-group inline" id="saveCancel" data-toggle="buttons" style="display:none">
<button type="button" class="btn btn-success" id="saveRecord" onclick="onClickSave()">
<span class="fas fa-save"></span>
<%= t("repositories.save_record") %>
</button>
<button type="button" class="btn btn-default" id="cancelSave" onclick="onClickCancel()">
<span class="fas fa-times visible-xs-inline"></span>
<span class="hidden-xs"><%= t("repositories.cancel_save") %></span>
</button>
</div>
<!-- These buttons are appended to table in javascript, after table initialization -->
<div class="toolbarButtonsDatatable" style="display:none">
@ -128,21 +117,34 @@
<% end %>
<% if can_manage_repository_rows?(@repository) %>
<button type="button" class="btn btn-default editAdd" id="editRepositoryRecord" onclick="onClickEdit()" disabled>
<span class="fas fa-pencil-alt"></span>
<span class="hidden-xs-custom"><%= t("repositories.edit_record") %></span>
</button>
<button type="button" class="btn btn-default"
id="deleteRepositoryRecordsButton" onclick="onClickDelete()" disabled>
<span class="fas fa-trash"></span>
<span class="hidden-xs-custom"><%= t'repositories.delete_record' %></span>
<%= submit_tag I18n.t('repositories.delete_record'), :class => "hidden
delete_repository_records_submit" %>
</button>
<button type="button" class="btn btn-default copyRow" id="copyRepositoryRecords" onclick="onClickCopyRepositoryRecords()" disabled>
<span class="fas fa-copy"></span>
<span class="hidden-xs-custom"><%= t("repositories.copy_record") %></span>
</button>
<span id="editDeleteCopy" data-toggle="buttons" style="display:none">
<button type="button" class="btn btn-default editAdd" id="editRepositoryRecord" onclick="onClickEdit()" disabled>
<span class="fas fa-pencil-alt"></span>
<span class="hidden-xs-custom"><%= t("repositories.edit_record") %></span>
</button>
<button type="button" class="btn btn-default"
id="deleteRepositoryRecordsButton" onclick="onClickDelete()" disabled>
<span class="fas fa-trash"></span>
<span class="hidden-xs-custom"><%= t'repositories.delete_record' %></span>
<%= submit_tag I18n.t('repositories.delete_record'), :class => "hidden
delete_repository_records_submit" %>
</button>
<button type="button" class="btn btn-default copyRow" id="copyRepositoryRecords" onclick="onClickCopyRepositoryRecords()" disabled>
<span class="fas fa-copy"></span>
<span class="hidden-xs-custom"><%= t("repositories.copy_record") %></span>
</button>
</span>
<span id="saveCancel" data-toggle="buttons" style="display:none">
<button type="button" class="btn btn-success" id="saveRecord">
<span class="fas fa-save"></span>
<%= t("repositories.save_record") %>
</button>
<button type="button" class="btn btn-default" id="cancelSave">
<span class="fas fa-times-circle"></span>
<%= t("repositories.cancel_save") %>
</button>
</span>
<% elsif @repository.shared_with?(current_team) %>
<p class="view-only-label"><%= t('repositories.index.view_only_permission_label') %></p>
<% end %>

View file

@ -947,7 +947,7 @@ class Constants
time: 0,
start: 0,
length: 6,
order: { 0 => [2, 'asc'] }, # Default sorting by 'ID' column
order: [[2, 'asc']], # Default sorting by 'ID' column
search: { search: '',
smart: true,
regex: false,