mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-02 09:54:37 +08:00
Merge pull request #2260 from aignatov-bio/ai-sci-4121-add-edit-mode-to-checklist
Add checklist and list edit mode [SCI-4121]
This commit is contained in:
commit
2da7eeaedd
25 changed files with 289 additions and 140 deletions
|
@ -0,0 +1,57 @@
|
|||
/* global dropdownSelector I18n */
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
var ChecklistColumnHelper = (function() {
|
||||
function checklistSelect(select, url, values) {
|
||||
var selectedOptions = '';
|
||||
if (values) {
|
||||
$.each(values, function(i, option) {
|
||||
selectedOptions += `<option value="${option.value}">${option.label}</option>`;
|
||||
});
|
||||
}
|
||||
return $(`<select
|
||||
id="${select}"
|
||||
data-placeholder = "Select options..."
|
||||
data-ajax-url = "${url}"
|
||||
data-combine-tags="true"
|
||||
data-select-multiple-all-selected="${I18n.t('libraries.manange_modal_column.checklist_type.all_options')}"
|
||||
data-select-multiple-name="${I18n.t('libraries.manange_modal_column.checklist_type.multiple_options')}"
|
||||
>${selectedOptions}</select>`);
|
||||
}
|
||||
|
||||
function checklistHiddenField(formId, columnId, values) {
|
||||
var idList = [];
|
||||
if (values) {
|
||||
$.each(values, function(i, option) {
|
||||
idList.push(option.value);
|
||||
});
|
||||
} else {
|
||||
idList = '';
|
||||
}
|
||||
return $(`<input form="${formId}"
|
||||
type="hidden"
|
||||
name="repository_cells[${columnId}]"
|
||||
value="${JSON.stringify(idList)}"
|
||||
data-type="RepositoryChecklistValue">`);
|
||||
}
|
||||
|
||||
function initialChecklistEditMode(formId, columnId, cell, values) {
|
||||
var select = 'checklist-' + columnId;
|
||||
var checklistUrl = $('.repository-column#' + columnId).data('items-url');
|
||||
var $select = checklistSelect(select, checklistUrl, values);
|
||||
var $hiddenField = checklistHiddenField(formId, columnId, values);
|
||||
cell.html($select).append($hiddenField);
|
||||
dropdownSelector.init('#' + select, {
|
||||
noEmptyOption: true,
|
||||
optionClass: 'checkbox-icon',
|
||||
selectAppearance: 'simple',
|
||||
onChange: function() {
|
||||
$hiddenField.val(JSON.stringify(dropdownSelector.getValues('#' + select)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
initialChecklistEditMode: initialChecklistEditMode
|
||||
};
|
||||
}());
|
|
@ -1,52 +1,45 @@
|
|||
/* global dropdownSelector */
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
var List = (function() {
|
||||
function listItemDropdown(options, currentValueId, columnId, formId) {
|
||||
var html = `<select class="form-control selectpicker repository-dropdown"
|
||||
data-abs-min-length="2" data-live-search="true" from="${formId}"
|
||||
data-container="body" column_id="${columnId}">
|
||||
<option value="-1"></option>`;
|
||||
$.each(options, function(index, value) {
|
||||
var selected = (parseInt(currentValueId, 10) === value[0]) ? 'selected' : '';
|
||||
html += `<option value="${value[0]}" ${selected}>${value[1]}</option>`;
|
||||
});
|
||||
html += '</select>';
|
||||
return html;
|
||||
var ListColumnHelper = (function() {
|
||||
function listSelect(select, url, value) {
|
||||
var selectedOption = '';
|
||||
if (value && value.value) {
|
||||
selectedOption = `<option value="${value.value}">${value.label}</option>`;
|
||||
}
|
||||
return $(`<select
|
||||
id="${select}"
|
||||
data-placeholder = "Select option..."
|
||||
data-ajax-url = "${url}"
|
||||
>${selectedOption}</select>`);
|
||||
}
|
||||
|
||||
function initialListItemsRequest(columnId, currentValueId, formId, url) {
|
||||
var massageResponse = [];
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
async: false,
|
||||
data: {
|
||||
column_id: columnId
|
||||
}
|
||||
}).done(function(data) {
|
||||
$.each(data.list_items, function(index, el) {
|
||||
massageResponse.push([el.id, el.data]);
|
||||
});
|
||||
});
|
||||
return listItemDropdown(massageResponse, currentValueId, columnId, formId);
|
||||
function listHiddenField(formId, columnId, value) {
|
||||
var originalValue = value ? value.value : '';
|
||||
return $(`<input form="${formId}"
|
||||
type="hidden"
|
||||
name="repository_cells[${columnId}]"
|
||||
value="${originalValue}"
|
||||
data-type="RepositoryListValue">`);
|
||||
}
|
||||
|
||||
function initSelectPicker($select, $hiddenField) {
|
||||
dropdownSelector.init($select, {
|
||||
noEmptyOption: true,
|
||||
function initialListEditMode(formId, columnId, cell, value = null) {
|
||||
var select = 'list-' + columnId;
|
||||
var listUrl = $('.repository-column#' + columnId).data('items-url');
|
||||
var $select = listSelect(select, listUrl, value);
|
||||
var $hiddenField = listHiddenField(formId, columnId, value);
|
||||
cell.html($select).append($hiddenField);
|
||||
dropdownSelector.init('#' + select, {
|
||||
singleSelect: true,
|
||||
closeOnSelect: true,
|
||||
selectAppearance: 'simple',
|
||||
onChange: function() {
|
||||
$hiddenField.val(dropdownSelector.getValues($select));
|
||||
var values = dropdownSelector.getValues('#' + select);
|
||||
$hiddenField.val(values);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
initialListItemsRequest: initialListItemsRequest,
|
||||
initSelectPicker: initSelectPicker
|
||||
initialListEditMode: initialListEditMode
|
||||
};
|
||||
}());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global List Status SmartAnnotation I18n GLOBAL_CONSTANTS */
|
||||
/* global ListColumnHelper ChecklistColumnHelper Status SmartAnnotation I18n GLOBAL_CONSTANTS */
|
||||
|
||||
$.fn.dataTable.render.editRowName = function(formId, cell) {
|
||||
let $cell = $(cell.node());
|
||||
|
@ -58,19 +58,17 @@ $.fn.dataTable.render.editRepositoryTextValue = function(formId, columnId, cell)
|
|||
};
|
||||
|
||||
$.fn.dataTable.render.editRepositoryListValue = function(formId, columnId, cell) {
|
||||
let $cell = $(cell.node());
|
||||
let currentValueId = $cell.find('.list-label').attr('data-value-id');
|
||||
let url = $cell.closest('table').data('list-items-path');
|
||||
let hiddenField = `
|
||||
<input form="${formId}"
|
||||
type="hidden"
|
||||
name="repository_cells[${columnId}]"
|
||||
value=""
|
||||
data-type="RepositoryListValue">`;
|
||||
var $cell = $(cell.node());
|
||||
var currentElement = $cell.find('.list-label');
|
||||
var currentValue = null;
|
||||
if (currentElement.length) {
|
||||
currentValue = {
|
||||
value: currentElement.attr('data-value-id'),
|
||||
label: currentElement.text()
|
||||
};
|
||||
}
|
||||
|
||||
$cell.html(hiddenField + List.initialListItemsRequest(columnId, currentValueId, formId, url));
|
||||
|
||||
List.initSelectPicker($cell.find('select'), $cell.find(`[name='repository_cells[${columnId}]']`));
|
||||
ListColumnHelper.initialListEditMode(formId, columnId, $cell, currentValue);
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.editRepositoryStatusValue = function(formId, columnId, cell) {
|
||||
|
@ -114,6 +112,8 @@ $.fn.dataTable.render.editRepositoryTimeRangeValue = function(formId, columnId,
|
|||
return '';
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.editRepositoryCheckboxValue = function(formId, columnId, cell) {
|
||||
return '';
|
||||
$.fn.dataTable.render.editRepositoryChecklistValue = function(formId, columnId, cell) {
|
||||
var $cell = $(cell.node());
|
||||
var currentValue = $cell.find('.checklist-options').data('checklist-items');
|
||||
ChecklistColumnHelper.initialChecklistEditMode(formId, columnId, $cell, currentValue);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global List Status SmartAnnotation I18n GLOBAL_CONSTANTS */
|
||||
/* global ListColumnHelper ChecklistColumnHelper Status SmartAnnotation I18n GLOBAL_CONSTANTS */
|
||||
|
||||
$.fn.dataTable.render.newRowName = function(formId, $cell) {
|
||||
$cell.html(`
|
||||
|
@ -48,17 +48,7 @@ $.fn.dataTable.render.newRepositoryTextValue = function(formId, columnId, $cell)
|
|||
};
|
||||
|
||||
$.fn.dataTable.render.newRepositoryListValue = function(formId, columnId, $cell) {
|
||||
let url = $cell.closest('table').data('list-items-path');
|
||||
let hiddenField = `
|
||||
<input form="${formId}"
|
||||
type="hidden"
|
||||
name="repository_cells[${columnId}]"
|
||||
value=""
|
||||
data-type="RepositoryListValue">`;
|
||||
|
||||
$cell.html(hiddenField + List.initialListItemsRequest(columnId, '', formId, url));
|
||||
|
||||
List.initSelectPicker($cell.find('select'), $cell.find(`[name='repository_cells[${columnId}]']`));
|
||||
ListColumnHelper.initialListEditMode(formId, columnId, $cell);
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.newRepositoryStatusValue = function(formId, columnId, $cell) {
|
||||
|
@ -75,6 +65,6 @@ $.fn.dataTable.render.newRepositoryStatusValue = function(formId, columnId, $cel
|
|||
Status.initStatusSelectPicker($cell.find('select'), $cell.find(`[name='repository_cells[${columnId}]']`));
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.newRepositoryCheckboxValue = function(formId, columnId) {
|
||||
return '';
|
||||
$.fn.dataTable.render.newRepositoryChecklistValue = function(formId, columnId, $cell) {
|
||||
ChecklistColumnHelper.initialChecklistEditMode(formId, columnId, $cell);
|
||||
};
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* global I18n */
|
||||
|
||||
$.fn.dataTable.render.RepositoryAssetValue = function(data) {
|
||||
var asset = data.value;
|
||||
return `
|
||||
|
@ -96,10 +98,31 @@ $.fn.dataTable.render.RepositoryDateRangeValue = function(data) {
|
|||
return data.value;
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.RepositoryCheckboxValue = function(data) {
|
||||
return '';
|
||||
$.fn.dataTable.render.RepositoryChecklistValue = function(data) {
|
||||
var render = '—';
|
||||
var options = data.value;
|
||||
var optionsList;
|
||||
if (options.length === 1) {
|
||||
render = `<span class="checklist-options" data-checklist-items='${JSON.stringify(options)}'>
|
||||
${options[0].label}
|
||||
</span>`;
|
||||
} else if (options.length > 1) {
|
||||
optionsList = $(' <ul class="dropdown-menu checklist-dropdown-menu" role="menu"></ul');
|
||||
$.each(options, function(i, option) {
|
||||
$(`<li class="checklist-item">${option.label}</li>`).appendTo(optionsList);
|
||||
});
|
||||
|
||||
render = `
|
||||
<span class="dropdown checklist-dropdown">
|
||||
<span data-toggle="dropdown" class="checklist-options" aria-haspopup="true" data-checklist-items='${JSON.stringify(options)}'>
|
||||
${options.length} ${I18n.t('libraries.manange_modal_column.checklist_type.multiple_options')}
|
||||
</span>
|
||||
${optionsList[0].outerHTML}
|
||||
</span>`;
|
||||
}
|
||||
return render;
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.defaultRepositoryCheckboxValue = function() {
|
||||
return '';
|
||||
$.fn.dataTable.render.defaultRepositoryChecklistValue = function() {
|
||||
return '—';
|
||||
};
|
||||
|
|
|
@ -29,3 +29,7 @@ $.fn.dataTable.render.RepositoryAssetValueValidator = function($input) {
|
|||
$btn.attr('data-error-text', errorMessage);
|
||||
return false;
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.RepositoryChecklistValueValidator = function() {
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -63,7 +63,7 @@ var RepositoryChecklistColumnType = (function() {
|
|||
checkValidation: () => {
|
||||
var $manageModal = $(manageModal);
|
||||
var count = $manageModal.find(previewContainer).find('.items-count').attr('data-count');
|
||||
return count < GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN;
|
||||
return count < GLOBAL_CONSTANTS.REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN;
|
||||
},
|
||||
loadParams: () => {
|
||||
var repositoryColumnParams = {};
|
||||
|
|
|
@ -5,5 +5,6 @@ const GLOBAL_CONSTANTS = {
|
|||
FILENAME_TRUNCATION_LENGTH: <%= Constants::FILENAME_TRUNCATION_LENGTH %>,
|
||||
FILE_MAX_SIZE_MB: <%= Rails.configuration.x.file_max_size_mb %>,
|
||||
IS_SAFARI: /^((?!chrome|android).)*safari/i.test(navigator.userAgent),
|
||||
REPOSITORY_LIST_ITEMS_PER_COLUMN: <%= Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN %>
|
||||
REPOSITORY_LIST_ITEMS_PER_COLUMN: <%= Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN %>,
|
||||
REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN: <%= Constants::REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN %>
|
||||
};
|
||||
|
|
|
@ -55,16 +55,29 @@ var dropdownSelector = (function() {
|
|||
var containerPosition = container[0].getBoundingClientRect().top;
|
||||
var containerHeight = container[0].getBoundingClientRect().height;
|
||||
var containerWidth = container[0].getBoundingClientRect().width;
|
||||
var bottomSpace = windowHeight - containerPosition - containerHeight;
|
||||
if (bottomSpace < 280) {
|
||||
var bottomSpace;
|
||||
var modalContainer = container.closest('.modal-dialog');
|
||||
var modalContainerBottom = 0;
|
||||
var maxHeight = 0;
|
||||
|
||||
if (modalContainer.length) {
|
||||
windowHeight = modalContainer.height() + modalContainer[0].getBoundingClientRect().top;
|
||||
modalContainerBottom = modalContainer[0].getBoundingClientRect().bottom;
|
||||
maxHeight += modalContainerBottom;
|
||||
}
|
||||
|
||||
bottomSpace = windowHeight - containerPosition - containerHeight;
|
||||
|
||||
if ((modalContainerBottom + bottomSpace) < 280) {
|
||||
container.addClass('inverse');
|
||||
container.find('.dropdown-container').css('max-height', `${(containerPosition - 122)}px`)
|
||||
container.find('.dropdown-container').css('max-height', `${(containerPosition - 122 + maxHeight)}px`)
|
||||
.css('margin-bottom', `${(containerPosition * -1)}px`)
|
||||
.css('width', `${containerWidth}px`);
|
||||
} else {
|
||||
container.removeClass('inverse');
|
||||
container.find('.dropdown-container').css('max-height', `${(bottomSpace - 32)}px`)
|
||||
.css('width', '');
|
||||
container.find('.dropdown-container').css('max-height', `${(bottomSpace - 32 + maxHeight)}px`)
|
||||
.css('width', `${containerWidth}px`)
|
||||
.css('margin-top', `${(bottomSpace * -1)}px`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,7 +278,7 @@ var dropdownSelector = (function() {
|
|||
$(`
|
||||
<div class="dropdown-container"></div>
|
||||
<div class="input-field">
|
||||
<input type="text" class="search-field" placeholder="${selectElement.data('placeholder') || ''}"></input>
|
||||
<input type="text" class="search-field" data-options-selected=0 placeholder="${selectElement.data('placeholder') || ''}"></input>
|
||||
${prepareCustomDropdownIcon(config)}
|
||||
</div>
|
||||
<input type="hidden" class="data-field" value="[]">
|
||||
|
@ -368,12 +381,15 @@ var dropdownSelector = (function() {
|
|||
});
|
||||
|
||||
// When user will resize browser we must check dropdown position
|
||||
$(window).resize(function() { updateDropdownDirection(selectElement, dropdownContainer); });
|
||||
$(window).resize(() => { updateDropdownDirection(selectElement, dropdownContainer); });
|
||||
$(window).scroll(() => { updateDropdownDirection(selectElement, dropdownContainer); });
|
||||
|
||||
// When user will click away, we must close dropdown
|
||||
$(window).click(() => {
|
||||
if (dropdownContainer.hasClass('open') && config.onClose) {
|
||||
if (dropdownContainer.hasClass('open')) {
|
||||
dropdownContainer.find('.search-field').val('');
|
||||
}
|
||||
if (dropdownContainer.hasClass('open') && config.onClose) {
|
||||
config.onClose();
|
||||
}
|
||||
dropdownContainer.removeClass('open active');
|
||||
|
@ -544,7 +560,7 @@ var dropdownSelector = (function() {
|
|||
}
|
||||
|
||||
// First we clear search field
|
||||
container.find('.search-field').val('');
|
||||
if (selector.data('config').singleSelect) container.find('.search-field').val('');
|
||||
|
||||
// Now we check all options in dropdown for selection and add them to array
|
||||
$.each(container.find('.dropdown-container .dropdown-option'), function(oi, option) {
|
||||
|
@ -575,8 +591,6 @@ var dropdownSelector = (function() {
|
|||
updateCurrentData(container, selectArray);
|
||||
// Redraw tags
|
||||
updateTags(selector, container, { select: true });
|
||||
// Reload options in option container
|
||||
loadData(selector, container);
|
||||
}
|
||||
|
||||
// Refresh tags in input field
|
||||
|
@ -650,6 +664,7 @@ var dropdownSelector = (function() {
|
|||
// If we have alteast one tag, we need to remove placeholder from search field
|
||||
searchFieldValue.attr('placeholder',
|
||||
selectedOptions.length > 0 ? '' : (selector.data('placeholder') || ''));
|
||||
searchFieldValue.attr('data-options-selected', selectedOptions.length);
|
||||
|
||||
// Add stretch class for visual improvments
|
||||
if (!selector.data('combine-tags')) {
|
||||
|
@ -772,7 +787,6 @@ var dropdownSelector = (function() {
|
|||
values = $.map(getCurrentData($(selector).next()), (v) => {
|
||||
return v.value;
|
||||
});
|
||||
|
||||
if ($(selector).data('config').singleSelect) return values[0];
|
||||
|
||||
return values;
|
||||
|
|
|
@ -94,11 +94,17 @@
|
|||
var mouse = { x: e.clientX, y: e.clientY };
|
||||
$('.popover.tooltip-open').each(function(i, obj) {
|
||||
var tooltipObj = '*[data-tooltip-id="' + obj.dataset.popoverId + '"]';
|
||||
var objHeight = $(tooltipObj)[0].clientHeight;
|
||||
var objWidth = $(tooltipObj)[0].clientWidth;
|
||||
var objLeft = $(tooltipObj)[0].offsetLeft;
|
||||
var objTop = $(tooltipObj)[0].offsetTop;
|
||||
var objCorners = {
|
||||
var objHeight;
|
||||
var objWidth;
|
||||
var objLeft;
|
||||
var objTop;
|
||||
var objCorners;
|
||||
if ($(tooltipObj).length === 0) return;
|
||||
objHeight = $(tooltipObj)[0].clientHeight;
|
||||
objWidth = $(tooltipObj)[0].clientWidth;
|
||||
objLeft = $(tooltipObj)[0].offsetLeft;
|
||||
objTop = $(tooltipObj)[0].offsetTop;
|
||||
objCorners = {
|
||||
tl: { x: objLeft, y: objTop },
|
||||
tr: { x: (objLeft + objWidth), y: objTop },
|
||||
bl: { x: objLeft, y: (objTop + objHeight) },
|
||||
|
|
17
app/assets/stylesheets/repository/repository_table.scss
Normal file
17
app/assets/stylesheets/repository/repository_table.scss
Normal file
|
@ -0,0 +1,17 @@
|
|||
// scss-lint:disable SelectorDepth SelectorFormat QualifyingElement
|
||||
// scss-lint:disable NestingDepth ImportantRule
|
||||
|
||||
@import "constants";
|
||||
|
||||
.repository-table {
|
||||
// Cells
|
||||
|
||||
// Checklists
|
||||
.checklist-dropdown {
|
||||
.dropdown-menu {
|
||||
.checklist-item {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -109,8 +109,8 @@
|
|||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.12), 0 0 4px 0 rgba(0, 0, 0, 0.08);
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: calc(100% - 30px);
|
||||
position: fixed;
|
||||
bottom: calc(100% - 30px);
|
||||
transition: .2s;
|
||||
transition-property: top, bottom, box-shadow;
|
||||
width: 100%;
|
||||
|
@ -167,7 +167,7 @@
|
|||
cursor: pointer;
|
||||
display: flex;
|
||||
min-height: 32px;
|
||||
padding: 0 10px;
|
||||
padding: 3px 10px;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
|
@ -236,9 +236,19 @@
|
|||
bottom: 3px;
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
||||
&[data-options-selected="0"] {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-simple {
|
||||
.tag-label {
|
||||
overflow: hiddens;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fa-times {
|
||||
display: none;
|
||||
}
|
||||
|
@ -263,6 +273,7 @@
|
|||
|
||||
.dropdown-container {
|
||||
border-top: 0;
|
||||
bottom: auto;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.12), 0 0 4px 0 rgba(0, 0, 0, 0.08);
|
||||
display: block;
|
||||
top: 100%;
|
||||
|
@ -289,6 +300,10 @@
|
|||
.search-field {
|
||||
display: block;
|
||||
line-height: 14px;
|
||||
|
||||
&[data-options-selected="0"] {
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.ds-simple {
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
module RepositoryColumns
|
||||
class ChecklistColumnsController < BaseColumnsController
|
||||
before_action :load_column, only: %i(update destroy)
|
||||
before_action :load_column, only: %i(update destroy items)
|
||||
before_action :check_create_permissions, only: :create
|
||||
before_action :check_manage_permissions, only: %i(update destroy)
|
||||
before_action :check_manage_permissions, only: %i(update destroy items)
|
||||
helper_method :delimiters
|
||||
|
||||
def create
|
||||
|
@ -45,8 +45,21 @@ module RepositoryColumns
|
|||
end
|
||||
end
|
||||
|
||||
def items
|
||||
column_checklist_items = @repository_column.repository_checklist_items
|
||||
.where('data ILIKE ?',
|
||||
"%#{search_params[:query]}%")
|
||||
.select(:id, :data)
|
||||
|
||||
render json: column_checklist_items.map { |i| { value: i.id, label: escape_input(i.data) } }, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search_params
|
||||
params.permit(:query, :column_id)
|
||||
end
|
||||
|
||||
def repository_column_params
|
||||
params.require(:repository_column).permit(:name, :delimiter, repository_checklist_items_attributes: %i(data))
|
||||
end
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
module RepositoryColumns
|
||||
class ListColumnsController < BaseColumnsController
|
||||
before_action :load_column, only: %i(update destroy)
|
||||
before_action :load_column, only: %i(update destroy items)
|
||||
before_action :check_create_permissions, only: :create
|
||||
before_action :check_manage_permissions, only: %i(update destroy)
|
||||
before_action :check_manage_permissions, only: %i(update destroy items)
|
||||
helper_method :delimiters
|
||||
|
||||
def create
|
||||
|
@ -45,8 +45,22 @@ module RepositoryColumns
|
|||
end
|
||||
end
|
||||
|
||||
def items
|
||||
column_list_items = @repository_column.repository_list_items
|
||||
.where('data ILIKE ?',
|
||||
"%#{search_params[:query]}%")
|
||||
.limit(Constants::SEARCH_LIMIT)
|
||||
.select(:id, :data)
|
||||
|
||||
render json: column_list_items.map { |i| { value: i.id, label: escape_input(i.data) } }, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search_params
|
||||
params.permit(:query, :column_id)
|
||||
end
|
||||
|
||||
def repository_column_params
|
||||
params.require(:repository_column).permit(:name, :delimiter, repository_list_items_attributes: %i(data))
|
||||
end
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
class RepositoryListItemsController < ApplicationController
|
||||
before_action :load_vars, only: :search
|
||||
|
||||
def search
|
||||
column_list_items = @repository_column.repository_list_items
|
||||
.where('data ILIKE ?',
|
||||
"%#{search_params[:q]}%")
|
||||
.limit(Constants::SEARCH_LIMIT)
|
||||
.select(:id, :data)
|
||||
|
||||
render json: { list_items: column_list_items }, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search_params
|
||||
params.permit(:q, :column_id)
|
||||
end
|
||||
|
||||
def load_vars
|
||||
@repository_column = RepositoryColumn.find_by_id(search_params[:column_id])
|
||||
repository = @repository_column.repository if @repository_column
|
||||
unless @repository_column&.data_type == 'RepositoryListValue'
|
||||
render_404 and return
|
||||
end
|
||||
render_403 unless can_manage_repository_rows?(repository)
|
||||
end
|
||||
end
|
|
@ -18,5 +18,22 @@ class RepositoryChecklistValue < ApplicationRecord
|
|||
def data
|
||||
repository_cell.repository_column.repository_checklist_items
|
||||
.where(id: repository_checklist_items).select(:id, :data)
|
||||
.map { |i| { value: i.id, label: i.data } }
|
||||
end
|
||||
|
||||
def data_changed?(new_data)
|
||||
JSON.parse(new_data) != repository_checklist_items
|
||||
end
|
||||
|
||||
def update_data!(new_data, user)
|
||||
self.repository_checklist_items = JSON.parse(new_data)
|
||||
self.last_modified_by = user
|
||||
save!
|
||||
end
|
||||
|
||||
def self.new_with_payload(payload, attributes)
|
||||
value = new(attributes)
|
||||
value.repository_checklist_items = JSON.parse(payload)
|
||||
value
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RepositoryDatatable
|
||||
class RepositoryCheckboxValueSerializer < ActiveModel::Serializer
|
||||
class RepositoryChecklistValueSerializer < ActiveModel::Serializer
|
||||
attributes :value, :value_type
|
||||
|
||||
def value
|
||||
object.repository_checkbox_value.data
|
||||
object.repository_checklist_value.data
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,12 +14,12 @@ module RepositoryColumns
|
|||
@column.lock!
|
||||
|
||||
updating_items_names = @params[:repository_checklist_items_attributes].to_a.map { |e| e[:data] }
|
||||
existing_items_names = @column.repository_checkbox_items.pluck(:data)
|
||||
existing_items_names = @column.repository_checklist_items.pluck(:data)
|
||||
to_be_deleted = existing_items_names - updating_items_names
|
||||
to_be_created = updating_items_names - existing_items_names
|
||||
|
||||
if @column.repository_list_items.size - to_be_deleted.size + to_be_created.size >=
|
||||
Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN
|
||||
Constants::REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN
|
||||
|
||||
@errors[:repository_column] = { repository_checklist_items: 'too many items' }
|
||||
end
|
||||
|
@ -30,7 +30,7 @@ module RepositoryColumns
|
|||
|
||||
ActiveRecord::Base.transaction do
|
||||
to_be_deleted.each do |item|
|
||||
@column.repository_checkbox_items.find_by(data: item).destroy!.id
|
||||
@column.repository_checklist_items.find_by(data: item).destroy!.id
|
||||
end
|
||||
|
||||
to_be_created.each do |item|
|
||||
|
|
|
@ -25,7 +25,7 @@ module RepositoryRows
|
|||
|
||||
params[:repository_cells]&.each do |column_id, value|
|
||||
column = @repository.repository_columns.find_by(id: column_id)
|
||||
next unless column
|
||||
next if !column || value.empty?
|
||||
|
||||
RepositoryCell.create_with_value!(@repository_row, column, value, @user)
|
||||
end
|
||||
|
|
|
@ -31,15 +31,12 @@ module RepositoryRows
|
|||
next
|
||||
end
|
||||
|
||||
unless cell
|
||||
RepositoryCell.create_with_value!(@repository_row, column, value, @user)
|
||||
@record_updated = true
|
||||
next
|
||||
end
|
||||
|
||||
if cell.value.data_changed?(value)
|
||||
if cell&.value&.data_changed?(value)
|
||||
cell.value.update_data!(value, @user)
|
||||
@record_updated = true
|
||||
elsif !value.empty?
|
||||
RepositoryCell.create_with_value!(@repository_row, column, value, @user)
|
||||
@record_updated = true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
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-checklist-items-path="<%= Rails.application.routes.url_helpers.repository_list_items_path %>"
|
||||
data-status-items-path="<%= Rails.application.routes.url_helpers.repository_status_items_path %>"
|
||||
data-editable="<%= can_manage_repository_rows?(repository) %>">
|
||||
<thead>
|
||||
|
@ -31,7 +31,13 @@
|
|||
<th id="added-on"><%= t("repositories.table.added_on") %></th>
|
||||
<th id="added-by"><%= t("repositories.table.added_by") %></th>
|
||||
<% repository.repository_columns.order(:id).each do |column| %>
|
||||
<th class="repository-column" id="<%= column.id %>" data-type="<%= column.data_type %>">
|
||||
<th
|
||||
class="repository-column"
|
||||
id="<%= column.id %>"
|
||||
data-type="<%= column.data_type %>"
|
||||
<%= "data-items-url=#{items_repository_repository_columns_checklist_column_path(repository, column)}" if column.data_type == 'RepositoryChecklistValue' %>
|
||||
<%= "data-items-url=#{items_repository_repository_columns_list_column_path(repository, column)}" if column.data_type == 'RepositoryListValue' %>
|
||||
>
|
||||
<%= display_tooltip(column.name) %>
|
||||
</th>
|
||||
<% end %>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</select>
|
||||
<div class="limit-counter-container">
|
||||
<span class="items-count"></span>
|
||||
<span class="items-limit">/<%= Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN %> </span>
|
||||
<span class="items-limit">/<%= Constants::REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN %> </span>
|
||||
<span class="items-label"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -976,6 +976,7 @@ class Constants
|
|||
EXPORTABLE_ZIP_EXPIRATION_DAYS = 7
|
||||
|
||||
REPOSITORY_LIST_ITEMS_PER_COLUMN = 500
|
||||
REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN = 50
|
||||
REPOSITORY_NUMBER_TYPE_MAX_DECIMALS = 11
|
||||
|
||||
REPOSITORY_LIST_ITEMS_DELIMITERS_MAP = {
|
||||
|
|
|
@ -581,12 +581,20 @@ Rails.application.routes.draw do
|
|||
namespace :repository_columns do
|
||||
resources :text_columns, only: %i(create update destroy)
|
||||
resources :number_columns, only: %i(create update destroy)
|
||||
resources :list_columns, only: %i(create update destroy)
|
||||
resources :list_columns, only: %i(create update destroy) do
|
||||
member do
|
||||
get 'items'
|
||||
end
|
||||
end
|
||||
resources :asset_columns, only: %i(create update destroy)
|
||||
resources :date_columns, only: %i(create update destroy)
|
||||
resources :status_columns, only: %i(create update destroy)
|
||||
resources :date_time_columns, only: %i(create update destroy)
|
||||
resources :checklist_columns, only: %i(create update destroy)
|
||||
resources :checklist_columns, only: %i(create update destroy) do
|
||||
member do
|
||||
get 'items'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7123,3 +7123,4 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||
('20191210103004');
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue