diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb
index fefa13e71..10312b0dc 100644
--- a/app/assets/javascripts/application.js.erb
+++ b/app/assets/javascripts/application.js.erb
@@ -44,6 +44,7 @@
//= require protocols/header
//= require marvinjslauncher
//= require_directory ./repositories/renderers
+//= require_directory ./repositories/validators
//= require turbolinks
diff --git a/app/assets/javascripts/repositories/forms/repository_item_edit.js b/app/assets/javascripts/repositories/forms/repository_item_edit.js
index 004b31e42..a398437f8 100644
--- a/app/assets/javascripts/repositories/forms/repository_item_edit.js
+++ b/app/assets/javascripts/repositories/forms/repository_item_edit.js
@@ -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 "
";
- }
-
- /**
- * 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 = "
" +
- "
" + truncateLongString(fileName, 20) +
- "
";
- if(value.file_name) {
- html += "
";
- } else {
- html += "
";
- }
- html += "
";
-
- 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 = '';
- 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 = "
" +
+// "
" + truncateLongString(fileName, 20) +
+// "
";
+// if(value.file_name) {
+// html += "
";
+// } else {
+// html += "
";
+// }
+// html += "
";
+//
+// 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 = '';
+// 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));
diff --git a/app/assets/javascripts/repositories/renderers/edit_renderers.js b/app/assets/javascripts/repositories/renderers/edit_renderers.js
new file mode 100644
index 000000000..250e656a9
--- /dev/null
+++ b/app/assets/javascripts/repositories/renderers/edit_renderers.js
@@ -0,0 +1,32 @@
+$.fn.dataTable.render.editRowName = function(formId, cell) {
+ let $cell = $(cell.node());
+ let text = $cell.find('a').first().text();
+
+ $cell.html(`
+
+
+
+ `);
+};
+
+$.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 '';
+};
diff --git a/app/assets/javascripts/repositories/renderers/new_renderers.js b/app/assets/javascripts/repositories/renderers/new_renderers.js
new file mode 100644
index 000000000..121daec70
--- /dev/null
+++ b/app/assets/javascripts/repositories/renderers/new_renderers.js
@@ -0,0 +1,24 @@
+$.fn.dataTable.render.newRowName = function(formId) {
+ return ``;
+};
+
+$.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 '';
+};
diff --git a/app/assets/javascripts/repositories/repository_datatable.js b/app/assets/javascripts/repositories/repository_datatable.js
index 5b2660628..bc987bf3e 100644
--- a/app/assets/javascripts/repositories/repository_datatable.js
+++ b/app/assets/javascripts/repositories/repository_datatable.js
@@ -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 "";
- }
+ // function changeToInputField(object, name, value) {
+ // return "";
+ // }
// 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 = '' + 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("" + defaultFields.name + ' ');
- }
- }
- }
-
- // 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("" + message + ' ');
- } else {
- input.closest('.form-group').addClass('has-error');
- input.parent().append("" + message + ' ');
- }
- }
- });
- });
- }
- }
- });
- }
-
- 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("" + defaultFields.name + ' ');
+ // }
+ // }
+ // }
+ //
+ // // 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("" + message + ' ');
+ // } else {
+ // input.closest('.form-group').addClass('has-error');
+ // input.parent().append("" + message + ' ');
+ // }
+ // }
+ // });
+ // });
+ // }
+ // }
+ // });
+ // }
// 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();
}
diff --git a/app/assets/javascripts/repositories/row_editor.js b/app/assets/javascripts/repositories/row_editor.js
new file mode 100644
index 000000000..60ed2493c
--- /dev/null
+++ b/app/assets/javascripts/repositories/row_editor.js
@@ -0,0 +1,146 @@
+/*
+ globals HelperModule animateSpinner SmartAnnotation
+*/
+
+var RepositoryDatatableRowEditor = (function() {
+ const NAME_COLUMN_ID = 'row-name';
+ const TABLE_ROW = '
';
+ const TABLE_CELL = '
';
+
+ 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 = $(`
+
+ `);
+
+ $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 = $(`
+
+ `);
+
+ $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
+ });
+}());
diff --git a/app/assets/javascripts/repositories/validators/base_validator.js b/app/assets/javascripts/repositories/validators/base_validator.js
new file mode 100644
index 000000000..ebed78115
--- /dev/null
+++ b/app/assets/javascripts/repositories/validators/base_validator.js
@@ -0,0 +1,5 @@
+/* global GLOBAL_CONSTANTS textValidator */
+
+$.fn.dataTable.render.RowNameValidator = function($input) {
+ return textValidator(undefined, $input, 1, GLOBAL_CONSTANTS.NAME_MAX_LENGTH);
+};
diff --git a/app/assets/stylesheets/themes/repositories.scss b/app/assets/stylesheets/themes/repositories.scss
index 26d5d388d..04ddc7230 100644
--- a/app/assets/stylesheets/themes/repositories.scss
+++ b/app/assets/stylesheets/themes/repositories.scss
@@ -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;
diff --git a/app/helpers/repository_datatable_helper.rb b/app/helpers/repository_datatable_helper.rb
index 6ced02dcb..8f653f59c 100644
--- a/app/helpers/repository_datatable_helper.rb
+++ b/app/helpers/repository_datatable_helper.rb
@@ -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]
diff --git a/app/services/repository_table_state_service.rb b/app/services/repository_table_state_service.rb
index da1ee0350..a0254bbe9 100644
--- a/app/services/repository_table_state_service.rb
+++ b/app/services/repository_table_state_service.rb
@@ -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] =
diff --git a/app/views/repositories/_repository_table.html.erb b/app/views/repositories/_repository_table.html.erb
index 7af68aadf..2607f8004 100644
--- a/app/views/repositories/_repository_table.html.erb
+++ b/app/views/repositories/_repository_table.html.erb
@@ -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) %>">