Add statuses validation on FE

This commit is contained in:
Urban Rotnik 2019-11-10 22:36:17 +01:00
parent b9d50263c4
commit 9bf948c3a8
7 changed files with 164 additions and 24 deletions

View file

@ -73,13 +73,14 @@ var RepositoryListColumnType = (function() {
function refreshCounter(number) {
var $manageModal = $(manageModal);
$manageModal.find('.list-items-count').html(number);
$manageModal.find('.list-items-count').attr('data-count', number);
if (number > GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN) {
if (number >= GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN) {
$manageModal.find('.limit-counter-container').addClass('error-to-many-items');
$manageModal.find('button[data-action="save"]').prop('disabled', true);
$manageModal.find('button[data-action="save"]').addClass('disabled');
} else {
$manageModal.find('.limit-counter-container').removeClass('error-to-many-items');
$manageModal.find('button[data-action="save"]').prop('disabled', false);
$manageModal.find('button[data-action="save"]').removeClass('disabled');
}
}
@ -114,7 +115,7 @@ var RepositoryListColumnType = (function() {
refreshPreviewDropdownList();
});
$manageModal.off('columnModal::partialLoaded').on('columnModal::partialLoaded', function() {
$manageModal.off('columnModal::partialLoadedForLists').on('columnModal::partialLoadedForLists', function() {
refreshPreviewDropdownList();
});
@ -126,6 +127,11 @@ var RepositoryListColumnType = (function() {
return {
init: () => {
initDropdownItemsTextArea();
},
checkValidation: () => {
var $manageModal = $(manageModal);
var count = $manageModal.find('.list-items-count').attr('data-count');
return count < GLOBAL_CONSTANTS.REPOSITORY_LIST_ITEMS_PER_COLUMN;
}
};
}());

View file

@ -1,4 +1,4 @@
/* global I18n */
/* global GLOBAL_CONSTANTS I18n */
/* eslint-disable no-unused-vars */
var RepositoryStatusColumnType = (function() {
var manageModal = '#manageRepositoryColumn';
@ -13,24 +13,57 @@ var RepositoryStatusColumnType = (function() {
<span class="status-item-icon-trash fas fa-trash"></span>
</div>
<div class="emojis-picker">
<span data-emoji-code="128540">&#128540;</span>
<span data-emoji-code="128520">&#128520;</span>
<span data-emoji-code="128526">&#128526;</span>
<span data-emoji-code="128531">&#128531;</span>
<span data-emoji-code="128535">&#128535;</span>
<span data-emoji-code="128536">&#128536;</span>
<span data-emoji-code="&#128540;">&#128540;</span>
<span data-emoji-code="&#128520;">&#128520;</span>
<span data-emoji-code="&#128526;">&#128526;</span>
<span data-emoji-code="&#128531;">&#128531;</span>
<span data-emoji-code="&#128535;">&#128535;</span>
<span data-emoji-code="&#128536;">&#128536;</span>
</div>`;
}
function validateForm() {
var $manageModal = $(manageModal);
var $statusRows = $manageModal.find('.status-item-container:not([data-removed])');
$.each($statusRows, (index, statusRow) => {
var $row = $(statusRow);
var $statusField = $row.find('input.status-item-field');
var $icon = $row.find('.status-item-icon');
var stringLength = $statusField.val().length;
if (stringLength < GLOBAL_CONSTANTS.NAME_MIN_LENGTH
|| stringLength > GLOBAL_CONSTANTS.NAME_MAX_LENGTH
|| !$icon.attr('data-icon')) {
$row.addClass('error');
} else {
$row.removeClass('error');
}
});
if ($manageModal.find('.error').length > 0) {
$manageModal.find('button[data-action="save"]').addClass('disabled');
} else {
$manageModal.find('button[data-action="save"]').removeClass('disabled');
}
}
function highlightErrors() {
$(manageModal).find('.error').addClass('error-highlight');
}
function initActions() {
var $manageModal = $(manageModal);
var addStatusOptionBtn = '.add-status';
var deleteStatusOptionBtn = '.status-item-icon-trash';
var icon = '.status-item-icon';
var emojis = '.emojis-picker>span';
var statusInput = 'input.status-item-field';
var buttonWrapper = '.button-wrapper';
$manageModal.off('click', addStatusOptionBtn).on('click', addStatusOptionBtn, function() {
var newStatusRow = $(statusTemplate()).insertBefore($(this));
validateForm();
setTimeout(function() {
newStatusRow.css('height', '34px');
}, 0);
@ -55,9 +88,12 @@ var RepositoryStatusColumnType = (function() {
setTimeout(function() {
if (isNewRow) {
$statusRow.remove();
validateForm();
} else {
$statusRow.attr('data-removed', 'true');
$statusRow.removeClass('loading');
$statusRow.removeClass('error');
validateForm();
}
$emojis.remove();
}, 300);
@ -70,14 +106,37 @@ var RepositoryStatusColumnType = (function() {
$manageModal.off('click', emojis).on('click', emojis, function() {
var $clickedEmoji = $(this);
var $iconField = $clickedEmoji.parent().prev().find('.status-item-icon');
$clickedEmoji.parent().hide();
$clickedEmoji.parent().prev().find('.status-item-icon').html($clickedEmoji.html());
$iconField.html($clickedEmoji.data('emoji-code'));
$iconField.attr('data-icon', $clickedEmoji.data('emoji-code'));
$iconField.trigger('data-attribute-changed', $iconField);
});
$manageModal.off('keyup change', statusInput).on('keyup change', statusInput, function() {
validateForm();
});
$manageModal.off('data-attribute-changed').on('data-attribute-changed', function() {
validateForm();
});
$manageModal.off('columnModal::partialLoadedForStatuses').on('columnModal::partialLoadedForStatuses', function() {
validateForm();
});
$manageModal.off('click', buttonWrapper).on('click', buttonWrapper, function() {
highlightErrors();
});
}
return {
init: () => {
initActions();
},
checkValidation: () => {
highlightErrors();
return !($(manageModal).find('.error').length > 0);
}
};
}());

View file

@ -40,6 +40,15 @@ var RepositoryColumns = (function() {
});
}
function checkData() {
var validators = {
RepositoryListValue: 'RepositoryListColumnType',
RepositoryStatusValue: 'RepositoryStatusColumnType'
};
var currentPartial = $('#repository-column-data-type').find(':selected').val();
return eval(validators[currentPartial]).checkValidation();
}
function insertNewListItem(column) {
var attributes = column.attributes;
@ -80,11 +89,38 @@ var RepositoryColumns = (function() {
$('li[data-id=' + column.id + ']').find('span').first().text(name);
}
function loadSpecificParams(type, params) {
function loadSpecificParams(type, params, modal) {
var $modal = modal;
var newParams = params;
var $statusItems;
if (type === 'RepositoryListValue') {
newParams.repository_column.repository_list_items_attributes = JSON.parse($('#dropdown_options').val());
newParams.repository_column.delimiter = $('select#delimiter').data('used-delimiter');
} else if (type === 'RepositoryStatusValue') {
$statusItems = $modal.find('.status-item-container');
// Load all new items
// Load all existing items, delete flag included
newParams.repository_column.repository_status_items_attributes = [];
$.each($statusItems, function(index, value) {
var $item = $(value);
var id = $item.data('id');
var removed = $item.data('removed');
var icon = $item.find('.status-item-icon').data('icon');
var status = $item.find('input.status-item-field').val();
if (removed && id) { // flag as item for removing
newParams.repository_column.repository_status_items_attributes
.push({ id: id, _destroy: true });
} else if (id) { // existing element, maybe values needs to be updated
newParams.repository_column.repository_status_items_attributes
.push({ id: id, icon: icon, status: status });
} else { // new element
newParams.repository_column.repository_status_items_attributes
.push({ icon: icon, status: status });
}
});
}
return newParams;
}
@ -95,7 +131,9 @@ var RepositoryColumns = (function() {
var url = $('#repository-column-data-type').find(':selected').data('create-url');
var params = { repository_column: { name: $('#repository-column-name').val() } };
var selectedType = $('#repository-column-data-type').find(':selected').val();
params = loadSpecificParams(selectedType, params);
params = loadSpecificParams(selectedType, params, $manageModal);
// if (checkData() === false) return;
$.ajax({
url: url,
type: 'POST',
@ -120,7 +158,9 @@ var RepositoryColumns = (function() {
var url = $('#repository-column-data-type').find(':selected').data('edit-url');
var params = { repository_column: { name: $('#repository-column-name').val() } };
var selectedType = $('#repository-column-data-type').find(':selected').val();
params = loadSpecificParams(selectedType, params);
params = loadSpecificParams(selectedType, params, $manageModal);
if (checkData() !== true) return;
$.ajax({
url: url,
type: 'PUT',
@ -150,7 +190,8 @@ var RepositoryColumns = (function() {
$manageModal.modal('show').find('.modal-content').html(data.html)
.find('#repository-column-name')
.focus();
$manageModal.trigger('columnModal::partialLoaded');
$manageModal.trigger('columnModal::partialLoadedForLists');
$manageModal.trigger('columnModal::partialLoadedForStatuses');
if (button.data('action') === 'new') {
$('[data-column-type="RepositoryTextValue"]').show();

View file

@ -1,6 +1,7 @@
const GLOBAL_CONSTANTS = {
NAME_TRUNCATION_LENGTH: <%= Constants::NAME_TRUNCATION_LENGTH %>,
NAME_MAX_LENGTH: <%= Constants::NAME_MAX_LENGTH %>,
NAME_MIN_LENGTH: <%= Constants::NAME_MIN_LENGTH %>,
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),

View file

@ -337,6 +337,20 @@
&[data-removed="true"] {
display: none;
}
&.error.error-highlight {
.status-item-icon {
border-bottom: 1px solid $brand-danger;
border-left: 1px solid $brand-danger;
border-top: 1px solid $brand-danger;
}
.status-item-field {
border-bottom: 1px solid $brand-danger;
border-right: 1px solid $brand-danger;
border-top: 1px solid $brand-danger;
}
}
}
.status-item-field {
@ -352,11 +366,19 @@
.status-item-icon {
border: 1px solid #ccc;
border-radius: 4px 0 0 4px;
cursor: pointer;
display: inline-block;
height: 34px;
line-height: 32px;
text-align: center;
width: 34px;
&:not([data-icon])::before {
content: "\f06a";
font-family: "Font Awesome 5 Free";
font-weight: 900;
}
}
.status-item-icon-trash {

View file

@ -11,7 +11,7 @@ module RepositoryColumns
def call
return self unless valid?
if @column.update(@params)
if @column.update(column_attributes)
log_activity(:edit_column_inventory)
else
errors[:repository_column] = @column.errors.messages
@ -19,5 +19,16 @@ module RepositoryColumns
self
end
private
def column_attributes
@params[:repository_status_items_attributes]&.map do |m|
# assign for new records only
m.merge!(repository_id: @repository.id, created_by_id: @user.id, last_modified_by_id: @user.id) unless m[:id]
end
@params
end
end
end

View file

@ -5,7 +5,7 @@
<div class="col-sm-9">
<% column.repository_status_items.each do |item| %>
<div class="status-item-container" data-id="<%= item.id %>">
<div class="status-item-icon"><%= item.icon %></div>
<div class="status-item-icon" data-icon="<%= item.icon %>"><span><%= item.icon %></span></div>
<input placeholder="<%= t('libraries.manange_modal_column.name_placeholder') %>"
class="status-item-field"
type="text"
@ -13,12 +13,12 @@
<span class="status-item-icon-trash fas fa-trash"></span>
</div>
<div class="emojis-picker">
<span data-emoji-code="128540">&#128540;</span>
<span data-emoji-code="128520">&#128520;</span>
<span data-emoji-code="128526">&#128526;</span>
<span data-emoji-code="128531">&#128531;</span>
<span data-emoji-code="128535">&#128535;</span>
<span data-emoji-code="128536">&#128536;</span>
<span data-emoji-code="&#128540;">&#128540;</span>
<span data-emoji-code="&#128520;">&#128520;</span>
<span data-emoji-code="&#128526;">&#128526;</span>
<span data-emoji-code="&#128531;">&#128531;</span>
<span data-emoji-code="&#128535;">&#128535;</span>
<span data-emoji-code="&#128536;">&#128536;</span>
</div>
<% end %>
<div class="add-status">