mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-20 14:45:56 +08:00
Add new stock management modal and edit itemm card stock fields [SCI-9415] (#6474)
* Add edit fetaure to item card stock field [SCI-9415] * Replace manage stock modal [SCI-9415] * Fix issue displaying item card [SCI-9415] * Minor improvements [SCI-9415] * Enable stock modal in assigned inventories [SCI-9415] * Use toggleable reminder value [SCI-9415]
This commit is contained in:
parent
7c54509c58
commit
c22d1e226b
|
@ -210,17 +210,18 @@ $.fn.dataTable.render.RepositoryStockValue = function(data) {
|
|||
if (data) {
|
||||
if (data.value) {
|
||||
if (data.stock_managable) {
|
||||
return `<a class="manage-repository-stock-value-link stock-value-view-render stock-${data.stock_status}">
|
||||
return `<a class="manage-repository-stock-value-link stock-value-view-render stock-${data.stock_status}"
|
||||
data-manage-stock-url=${data.value.stock_url}>
|
||||
${data.value.stock_formatted}
|
||||
</a>`;
|
||||
}
|
||||
return `<span class="stock-value-view-render
|
||||
return `<span class="stock-value-view-render data-manage-stock-url=${data.value.stock_url}
|
||||
${data.displayWarnings ? `stock-${data.stock_status}` : ''}">
|
||||
${data.value.stock_formatted}
|
||||
</span>`;
|
||||
}
|
||||
if (data.stock_managable) {
|
||||
return `<a class="manage-repository-stock-value-link not-assigned-stock">
|
||||
return `<a class="manage-repository-stock-value-link not-assigned-stock" data-manage-stock-url=${data.stock_url}>
|
||||
<i class="fas fa-box-open"></i>
|
||||
${I18n.t('libraries.manange_modal_column.stock_type.add_stock')}
|
||||
</a>`;
|
||||
|
|
|
@ -1,231 +0,0 @@
|
|||
/* global dropdownSelector GLOBAL_CONSTANTS I18n SmartAnnotation formatDecimalValue Decimal */
|
||||
|
||||
var RepositoryStockValues = (function() {
|
||||
const UNIT_SELECTOR = '#repository-stock-value-units';
|
||||
|
||||
function updateChangeAmount($element) {
|
||||
if (!$element.val()) {
|
||||
$('.stock-final-container .value').text('-');
|
||||
return;
|
||||
}
|
||||
if (!($element.val() >= 0)) return;
|
||||
|
||||
let currentAmount = new Decimal($element.data('currentAmount') || 0);
|
||||
let inputAmount = new Decimal($element.val());
|
||||
let newAmount;
|
||||
|
||||
switch ($element.data('operator')) {
|
||||
case 'set':
|
||||
newAmount = inputAmount;
|
||||
break;
|
||||
case 'add':
|
||||
newAmount = currentAmount.plus(inputAmount);
|
||||
break;
|
||||
case 'remove':
|
||||
newAmount = currentAmount.minus(inputAmount);
|
||||
break;
|
||||
default:
|
||||
newAmount = currentAmount;
|
||||
break;
|
||||
}
|
||||
$('#change_amount').val(inputAmount);
|
||||
|
||||
$('#repository_stock_value_amount').val(newAmount);
|
||||
$('.stock-final-container').toggleClass('negative', newAmount < 0);
|
||||
$('.stock-final-container .value').text(
|
||||
formatDecimalValue(String(newAmount), $('#stock-input-amount').data('decimals'))
|
||||
);
|
||||
}
|
||||
|
||||
function initManageAction() {
|
||||
let amountChanged = false;
|
||||
|
||||
$('.repository-show').on('click', '.manage-repository-stock-value-link', function() {
|
||||
let colIndex = this.parentNode.cellIndex;
|
||||
let rowIndex = this.parentNode.parentNode.rowIndex;
|
||||
|
||||
$.ajax({
|
||||
url: $(this).closest('tr').data('manage-stock-url'),
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: (result) => {
|
||||
var $manageModal = $('#manage-repository-stock-value-modal');
|
||||
$manageModal.find('.modal-content').html(result.html);
|
||||
|
||||
dropdownSelector.init(UNIT_SELECTOR, {
|
||||
singleSelect: true,
|
||||
closeOnSelect: true,
|
||||
noEmptyOption: true,
|
||||
selectAppearance: 'simple',
|
||||
onChange: function() {
|
||||
let unit = '';
|
||||
if (dropdownSelector.getValues(UNIT_SELECTOR) > 0) {
|
||||
unit = dropdownSelector.getLabels(UNIT_SELECTOR);
|
||||
}
|
||||
$('.stock-final-container .units').text(unit);
|
||||
$('.repository-stock-reminder-value .units').text(
|
||||
I18n.t('repository_stock_values.manage_modal.units_remaining', {
|
||||
unit: unit
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$manageModal.find(`
|
||||
.dropdown-selector-container .input-field,
|
||||
.dropdown-selector-container .search-field
|
||||
`).attr('tabindex', 2);
|
||||
|
||||
$manageModal.find('form').on('ajax:success', function(_, data) {
|
||||
$manageModal.modal('hide');
|
||||
let $cell = $('.dataTable').find(
|
||||
`tr:nth-child(${rowIndex}) td:nth-child(${colIndex + 1})`
|
||||
);
|
||||
$cell.parent().data('manage-stock-url', data.manageStockUrl);
|
||||
$cell.html(
|
||||
$.fn.dataTable.render.RepositoryStockValue(data)
|
||||
);
|
||||
});
|
||||
|
||||
$('.stock-operator-option').click(function() {
|
||||
var $stockInput = $('#stock-input-amount');
|
||||
$('.stock-operator-option').removeClass('btn-primary').addClass('btn-secondary');
|
||||
$(this).removeClass('btn-secondary').addClass('btn-primary');
|
||||
$stockInput.data('operator', $(this).data('operator'));
|
||||
|
||||
dropdownSelector.selectValues(UNIT_SELECTOR, $('#initial_units').val());
|
||||
$('#operator').val($(this).data('operator'));
|
||||
switch ($(this).data('operator')) {
|
||||
case 'set':
|
||||
dropdownSelector.enableSelector(UNIT_SELECTOR);
|
||||
if (!amountChanged) { $stockInput.val($stockInput.data('currentAmount')); }
|
||||
break;
|
||||
case 'add':
|
||||
if (!amountChanged) { $stockInput.val(''); }
|
||||
dropdownSelector.disableSelector(UNIT_SELECTOR);
|
||||
break;
|
||||
case 'remove':
|
||||
if (!amountChanged) { $stockInput.val(''); }
|
||||
dropdownSelector.disableSelector(UNIT_SELECTOR);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
updateChangeAmount($('#stock-input-amount'));
|
||||
});
|
||||
|
||||
$('#stock-input-amount, #low_stock_threshold').on('input focus', function() {
|
||||
let decimals = $(this).data('decimals');
|
||||
this.value = formatDecimalValue(this.value, decimals);
|
||||
});
|
||||
|
||||
SmartAnnotation.init($('#repository-stock-value-comment')[0], false);
|
||||
|
||||
$('#repository-stock-value-comment').on('input', function() {
|
||||
$(this).closest('.sci-input-container').toggleClass(
|
||||
'error',
|
||||
this.value.length > GLOBAL_CONSTANTS.NAME_MAX_LENGTH
|
||||
);
|
||||
$('.update-repository-stock').toggleClass(
|
||||
'disabled',
|
||||
this.value.length > GLOBAL_CONSTANTS.NAME_MAX_LENGTH
|
||||
);
|
||||
});
|
||||
|
||||
$('#reminder-selector-checkbox').on('change', function() {
|
||||
let valueContainer = $('.repository-stock-reminder-value');
|
||||
valueContainer.toggleClass('hidden', !this.checked);
|
||||
if (!this.checked) {
|
||||
$(this).data('reminder-value', valueContainer.find('input').val());
|
||||
valueContainer.find('input').val(null);
|
||||
} else {
|
||||
valueContainer.find('input').val($(this).data('reminder-value'));
|
||||
valueContainer.find('input').focus();
|
||||
}
|
||||
});
|
||||
|
||||
$('.update-repository-stock').on('click', function() {
|
||||
let reminderError = $('#reminder-selector-checkbox')[0].checked
|
||||
&& $('.repository-stock-reminder-value').find('input').val() === '';
|
||||
$('.repository-stock-reminder-value').find('.sci-input-container').toggleClass('error', reminderError);
|
||||
});
|
||||
|
||||
$('#stock-input-amount').on('input', function() {
|
||||
amountChanged = true;
|
||||
updateChangeAmount($(this));
|
||||
});
|
||||
|
||||
$manageModal.on('ajax:beforeSend', 'form', function() {
|
||||
let status = true;
|
||||
if (!(dropdownSelector.getValues(UNIT_SELECTOR) > 0)) {
|
||||
dropdownSelector.showError(UNIT_SELECTOR, I18n.t('repository_stock_values.manage_modal.unit_error'));
|
||||
status = false;
|
||||
} else {
|
||||
dropdownSelector.hideError(UNIT_SELECTOR);
|
||||
}
|
||||
let stockInput = $('#stock-input-amount');
|
||||
if (stockInput.val().length && stockInput.val() >= 0) {
|
||||
stockInput.parent().removeClass('error');
|
||||
} else {
|
||||
stockInput.parent().addClass('error');
|
||||
if (stockInput.val().length === 0) {
|
||||
stockInput.parent()
|
||||
.attr(
|
||||
'data-error-text',
|
||||
I18n.t('repository_stock_values.manage_modal.amount_error')
|
||||
);
|
||||
} else {
|
||||
stockInput.parent()
|
||||
.attr(
|
||||
'data-error-text',
|
||||
I18n.t('repository_stock_values.manage_modal.negative_error')
|
||||
);
|
||||
}
|
||||
status = false;
|
||||
}
|
||||
|
||||
let reminderInput = $('.repository-stock-reminder-value input');
|
||||
if ($('#reminder-selector-checkbox')[0].checked) {
|
||||
if (reminderInput.val().length && reminderInput.val() >= 0) {
|
||||
reminderInput.parent().removeClass('error');
|
||||
} else {
|
||||
reminderInput.parent().addClass('error');
|
||||
if (reminderInput.val().length === 0) {
|
||||
reminderInput.parent()
|
||||
.attr(
|
||||
'data-error-text',
|
||||
I18n.t('repository_stock_values.manage_modal.amount_error')
|
||||
);
|
||||
} else {
|
||||
reminderInput.parent()
|
||||
.attr(
|
||||
'data-error-text',
|
||||
I18n.t('repository_stock_values.manage_modal.negative_error')
|
||||
);
|
||||
}
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
});
|
||||
|
||||
$manageModal.modal('show');
|
||||
amountChanged = false;
|
||||
$('#stock-input-amount').focus();
|
||||
$('#stock-input-amount')[0].selectionStart = $('#stock-input-amount')[0].value.length;
|
||||
$('#stock-input-amount')[0].selectionEnd = $('#stock-input-amount')[0].value.length;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: () => {
|
||||
initManageAction();
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
RepositoryStockValues.init();
|
|
@ -69,4 +69,24 @@
|
|||
$('#modal-info-repository-row').modal('hide');
|
||||
}
|
||||
});
|
||||
$(document).on('click', '.manage-repository-stock-value-link', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
window.initManageStockValueModalComponent();
|
||||
if (window.manageStockModalComponent) {
|
||||
const $link = $(e.target).parents('a')[0] ? $(e.target).parents('a') : $(e.target);
|
||||
const stockValueUrl = $link.data('manage-stock-url');
|
||||
let updateCallback;
|
||||
if (stockValueUrl) {
|
||||
updateCallback = (data) => {
|
||||
if (!data?.value) return;
|
||||
// reload dataTable
|
||||
if ($('.dataTable')[0]) $('.dataTable').DataTable().ajax.reload();
|
||||
// update item card stock column
|
||||
window.manageStockCallback && window.manageStockCallback(data.value)
|
||||
};
|
||||
window.manageStockModalComponent.showModal(stockValueUrl, updateCallback);
|
||||
}
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
|
|
@ -43,6 +43,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-color: var(--sn-delete-red);
|
||||
}
|
||||
|
||||
.sn-select__options {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
/* Hide arrows on number type input field */
|
||||
@layer utilities {
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.sci-btn-group {
|
||||
@apply inline-flex items-center gap-2 relative;
|
||||
|
|
|
@ -6,33 +6,9 @@ class RepositoryStockValuesController < ApplicationController
|
|||
before_action :load_vars
|
||||
before_action :check_manage_permissions
|
||||
|
||||
def new
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
partial: 'repository_stock_values/manage_modal_content',
|
||||
locals: {
|
||||
repository_row: @repository_row,
|
||||
repository_stock_column: @repository_column,
|
||||
unit_items: @repository_column.repository_stock_unit_items,
|
||||
repository_stock_value: RepositoryStockValue.new
|
||||
}
|
||||
)
|
||||
}
|
||||
end
|
||||
def new; end
|
||||
|
||||
def edit
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
partial: 'repository_stock_values/manage_modal_content',
|
||||
locals: {
|
||||
repository_row: @repository_row,
|
||||
repository_stock_column: @repository_column,
|
||||
unit_items: @repository_column.repository_stock_unit_items,
|
||||
repository_stock_value: @repository_stock_value
|
||||
}
|
||||
)
|
||||
}
|
||||
end
|
||||
def edit; end
|
||||
|
||||
def create_or_update
|
||||
ActiveRecord::Base.transaction do
|
||||
|
@ -50,8 +26,12 @@ class RepositoryStockValuesController < ApplicationController
|
|||
render json: {
|
||||
stock_managable: true,
|
||||
stock_status: @repository_stock_value.status,
|
||||
manageStockUrl: edit_repository_stock_repository_repository_row_url(@repository, @repository_row)
|
||||
}.merge(serialize_repository_cell_value(@repository_stock_value.repository_cell, current_team, @repository))
|
||||
}.merge(
|
||||
serialize_repository_cell_value(
|
||||
@repository_stock_value.repository_cell, current_team, @repository,
|
||||
reminders_enabled: Repository.reminders_enabled?
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
module RepositoryDatatableHelper
|
||||
include InputSanitizeHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {})
|
||||
has_stock_management = repository.has_stock_management?
|
||||
|
@ -47,20 +48,6 @@ module RepositoryDatatableHelper
|
|||
end
|
||||
|
||||
if has_stock_management
|
||||
row['manageStockUrl'] = if record.has_stock?
|
||||
Rails.application.routes.url_helpers
|
||||
.edit_repository_stock_repository_repository_row_url(
|
||||
repository,
|
||||
record
|
||||
)
|
||||
else
|
||||
Rails.application.routes.url_helpers
|
||||
.new_repository_stock_repository_repository_row_url(
|
||||
repository,
|
||||
record
|
||||
)
|
||||
end
|
||||
|
||||
stock_cell = record.repository_cells.find { |cell| cell.value_type == 'RepositoryStockValue' }
|
||||
|
||||
# always add stock cell, even if empty
|
||||
|
@ -68,7 +55,7 @@ module RepositoryDatatableHelper
|
|||
if stock_cell.present?
|
||||
serialize_repository_cell_value(record.repository_stock_cell, team, repository)
|
||||
else
|
||||
{}
|
||||
{ stock_url: new_repository_stock_repository_repository_row_url(repository, record) }
|
||||
end
|
||||
row['stock'][:stock_managable] = stock_managable
|
||||
row['stock']['displayWarnings'] = display_stock_warnings?(repository)
|
||||
|
|
24
app/javascript/packs/vue/manage_stock_value_modal.js
Normal file
24
app/javascript/packs/vue/manage_stock_value_modal.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import TurbolinksAdapter from 'vue-turbolinks';
|
||||
import Vue from 'vue/dist/vue.esm';
|
||||
import PerfectScrollbar from 'vue2-perfect-scrollbar';
|
||||
import 'vue2-perfect-scrollbar/dist/vue2-perfect-scrollbar.css';
|
||||
import ManageStockValueModal from '../../vue/repository_row/manage_stock_value_modal.vue';
|
||||
|
||||
Vue.use(PerfectScrollbar);
|
||||
Vue.use(TurbolinksAdapter);
|
||||
Vue.prototype.i18n = window.I18n;
|
||||
|
||||
window.initManageStockValueModalComponent = () => {
|
||||
if (window.manageStockModalComponent) return;
|
||||
|
||||
if (notTurbolinksPreview()) {
|
||||
new Vue({
|
||||
el: '#manageStockValueModal',
|
||||
components: {
|
||||
'manage-stock-value-modal': ManageStockValueModal,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
initManageStockValueModalComponent();
|
|
@ -1,12 +1,10 @@
|
|||
<template>
|
||||
<div v-if="customColumns?.length > 0" class="flex flex-col gap-4 w-[350px] h-auto">
|
||||
<div v-for="(column, index) in customColumns" :key="column.id" class="flex flex-col gap-4 w-[350px] h-auto relative">
|
||||
<span class="absolute right-2 top-6" v-if="column?.value?.reminder === true">
|
||||
<Reminder :value="column?.value" :valueType="column?.value_type" />
|
||||
</span>
|
||||
<component
|
||||
:is="column.data_type"
|
||||
:key="index"
|
||||
:actions="actions"
|
||||
:data_type="column.data_type"
|
||||
:colId="column.id"
|
||||
:colName="column.name"
|
||||
|
@ -18,7 +16,6 @@
|
|||
:optionsPath="column.options_path"
|
||||
:inArchivedRepositoryRow="inArchivedRepositoryRow"
|
||||
:editingField="editingField"
|
||||
:actions="actions"
|
||||
@setEditingField="editingField = $event"
|
||||
@update="update"
|
||||
/>
|
||||
|
@ -31,7 +28,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Reminder from './reminder.vue'
|
||||
import RepositoryStockValue from './repository_values/RepositoryStockValue.vue';
|
||||
import RepositoryTextValue from './repository_values/RepositoryTextValue.vue';
|
||||
import RepositoryNumberValue from './repository_values/RepositoryNumberValue.vue';
|
||||
|
@ -49,7 +45,6 @@
|
|||
export default {
|
||||
name: 'CustomColumns',
|
||||
components: {
|
||||
Reminder,
|
||||
RepositoryStockValue,
|
||||
RepositoryTextValue,
|
||||
RepositoryNumberValue,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<template v-if="value.reminder === true">
|
||||
<template v-if="value?.reminder === true">
|
||||
<div class="inline-block float-right cursor-pointer relative" :title="reminderTitle"
|
||||
tabindex='-1'>
|
||||
<i class="sn-icon sn-icon-notifications row-reminders-icon"></i>
|
||||
|
@ -14,14 +14,14 @@
|
|||
},
|
||||
computed: {
|
||||
reminderColor() {
|
||||
if (this.value.reminder && (this.value.stock_amount > 0 || this.value.days_left > 0)) {
|
||||
if (this.value?.reminder && (this.value?.stock_amount > 0 || this.value?.days_left > 0)) {
|
||||
return 'bg-sn-alert-brittlebush';
|
||||
}
|
||||
return 'bg-sn-alert-passion';
|
||||
},
|
||||
reminderTitle() {
|
||||
let title = this.value.reminder_text
|
||||
if (this.value.reminder_message) title = `${title}\n${this.value.reminder_message}`;
|
||||
let title = this.value?.reminder_text
|
||||
if (this.value?.reminder_message) title = `${title}\n${this.value?.reminder_message}`;
|
||||
|
||||
return title;
|
||||
}
|
||||
|
|
|
@ -8,23 +8,49 @@
|
|||
{{ i18n.t('repositories.item_card.stock_export') }}
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="colVal?.stock_formatted" class="text-sn-dark-grey font-inter text-sm font-normal leading-5">
|
||||
{{ colVal?.stock_formatted }}
|
||||
<a style="text-decoration: none;"
|
||||
class="text-sn-dark-grey font-inter text-sm font-normal leading-5 w-full rounded relative block"
|
||||
:class="editableClassName"
|
||||
@click="enableEditing"
|
||||
:data-manage-stock-url="stockValueUrl"
|
||||
:data-repository-row-id="repositoryId"
|
||||
>
|
||||
<div v-if="values?.stock_formatted" class="text-sn-dark-grey font-inter text-sm font-normal leading-5 stock-value">
|
||||
{{ values.stock_formatted }}
|
||||
</div>
|
||||
<div v-else class="text-sn-dark-grey font-inter text-sm font-normal leading-5">
|
||||
<div v-else class="text-sn-dark-grey font-inter text-sm font-normal leading-5 stock-value">
|
||||
{{ i18n.t('repositories.item_card.repository_stock_value.no_stock') }}
|
||||
</div>
|
||||
<span class="absolute right-2 reminder" :class="{ 'top-1.5': permissions?.can_manage, 'top-0': !permissions?.can_manage, hidden: !values?.reminder }">
|
||||
<Reminder :value="values" />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Reminder from '../reminder.vue';
|
||||
export default {
|
||||
name: 'RepositoryStockValue',
|
||||
components: {
|
||||
Reminder
|
||||
},
|
||||
computed: {
|
||||
editableClassName() {
|
||||
const className = 'border-solid border-[1px] p-2 manage-repository-stock-value-link sci-cursor-edit'
|
||||
if (this.permissions.can_manage && this.isEditing) return `${className} border-sn-science-blue`;
|
||||
if (this.permissions.can_manage) return `${className} border-sn-light-grey hover:border-sn-sleepy-grey`;
|
||||
return ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stock_formatted: null,
|
||||
stock_amount: null,
|
||||
low_stock_threshold: null
|
||||
low_stock_threshold: null,
|
||||
isEditing: null,
|
||||
values: null,
|
||||
stockValueUrl: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
@ -34,14 +60,29 @@ export default {
|
|||
colVal: Object,
|
||||
repositoryId: Number,
|
||||
repositoryRowId: null,
|
||||
permissions: null
|
||||
permissions: null,
|
||||
actions: null,
|
||||
},
|
||||
created() {
|
||||
if (!this.colVal) return
|
||||
|
||||
this.stock_formatted = this.colVal.stock_formatted
|
||||
this.stock_amount = this.colVal.stock_amount
|
||||
this.low_stock_threshold = this.colVal.low_stock_threshold
|
||||
mounted() {
|
||||
this.values = this.colVal;
|
||||
this.stockValueUrl = this.actions.stock.stock_value_url
|
||||
window.manageStockCallback = this.submitCallback;
|
||||
},
|
||||
unmounted(){
|
||||
delete window.manageStockCallback
|
||||
},
|
||||
methods: {
|
||||
enableEditing(){
|
||||
this.isEditing = true
|
||||
const $this = this;
|
||||
// disable edit
|
||||
$('#manageStockValueModal').on('hide.bs.modal', function() {
|
||||
$this.isEditing = false;
|
||||
})
|
||||
},
|
||||
submitCallback(values) {
|
||||
if (values) this.values = values;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
278
app/javascript/vue/repository_row/manage_stock_value_modal.vue
Normal file
278
app/javascript/vue/repository_row/manage_stock_value_modal.vue
Normal file
|
@ -0,0 +1,278 @@
|
|||
<template>
|
||||
<div
|
||||
ref="modal"
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
aria-labelledby="manage-stock-value"
|
||||
>
|
||||
<div class="modal-dialog" role="document" v-if="stockValue">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" :aria-label="i18n.t('general.close')">
|
||||
<i class="sn-icon sn-icon-close"></i>
|
||||
</button>
|
||||
<h4 class="modal-title">
|
||||
<template v-if="!!stockValue?.id">
|
||||
{{ i18n.t('repository_stock_values.manage_modal.title', { item: repositoryRowName }) }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ i18n.t('repository_stock_values.manage_modal.edit_title', { item: repositoryRowName }) }}
|
||||
</template>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="text-sm pb-6"> {{ i18n.t('repository_stock_values.manage_modal.enter_amount') }}</p>
|
||||
<form class="flex flex-col gap-6" @submit.prevent novalidate>
|
||||
<fieldset class="w-full flex justify-between">
|
||||
<div class="flex flex-col w-40">
|
||||
<label class="text-sn-grey text-sm font-normal" for="operations">{{ i18n.t('repository_stock_values.manage_modal.operation') }}</label>
|
||||
<Select
|
||||
:disabled="!stockValue?.id"
|
||||
:value="operation"
|
||||
:options="operations"
|
||||
@change="setOperation"
|
||||
></Select>
|
||||
</div>
|
||||
<div class="flex flex-col w-40">
|
||||
<Input
|
||||
@input="amount = $event"
|
||||
name="stock_amount"
|
||||
id="stock-amount"
|
||||
:inputClass="`sci-input-container-v2 ${errors.amount ? 'error' : ''}`"
|
||||
:labelClass="`text-sm font-normal ${errors.amount ? 'text-sn-delete-red' : 'text-sn-grey'}`"
|
||||
type="number"
|
||||
:value="amount"
|
||||
:decimals="stockValue.decimals"
|
||||
:placeholder="i18n.t('repository_stock_values.manage_modal.amount_placeholder_new')"
|
||||
required
|
||||
:label="i18n.t('repository_stock_values.manage_modal.amount')"
|
||||
showLabel
|
||||
autoFocus
|
||||
:error="errors.amount"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col w-40">
|
||||
<label :class="`text-sm font-normal ${errors.unit ? 'text-sn-delete-red' : 'text-sn-grey'}`" for="stock-unit">
|
||||
{{ i18n.t('repository_stock_values.manage_modal.unit') }}
|
||||
</label>
|
||||
<Select
|
||||
:disabled="[2, 3].includes(operation)"
|
||||
:value="unit"
|
||||
:options="units"
|
||||
:placeholder="i18n.t('repository_stock_values.manage_modal.unit_prompt')"
|
||||
@change="unit = $event"
|
||||
:className="`${errors.unit ? 'error' : ''}`"
|
||||
></Select>
|
||||
<div class="text-sn-delete-red text-xs" :class="{ visible: errors.unit, invisible: !errors.unit }">
|
||||
{{ errors.unit }}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<template v-if="stockValue?.id">
|
||||
<div class="flex justify-between w-full items-center">
|
||||
<div class="flex flex-col w-[220px] h-24 border-rounded bg-sn-super-light-grey justify-between text-center">
|
||||
<span class="text-sm text-sn-grey leading-5">{{ i18n.t('repository_stock_values.manage_modal.current_stock') }}</span>
|
||||
<span class="text-2xl text-sn-black font-semibold leading-8" :class="{ 'text-sn-delete-red': stockValue.amount < 0 }">{{ stockValue.amount }}</span>
|
||||
<span class="text-sm text0sn-black leading-5">{{ initUnitLabel }}</span>
|
||||
</div>
|
||||
<i class="sn-icon sn-icon-arrow-right"></i>
|
||||
<div class="flex flex-col w-[220px] h-24 border-rounded bg-sn-super-light-grey justify-between text-center">
|
||||
<span class="text-sm text-sn-grey leading-5">{{ i18n.t('repository_stock_values.manage_modal.new_stock') }}</span>
|
||||
<span class="text-2xl text-sn-black font-semibold leading-8" :class="{ 'text-sn-delete-red': newAmount < 0 }">
|
||||
{{ (newAmount || newAmount === 0) ? newAmount : '-' }}
|
||||
</span>
|
||||
<span class="text-sm text0sn-black leading-5">{{ unitLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="repository-stock-reminder-selector">
|
||||
<div class="sci-checkbox-container">
|
||||
<input type="checkbox" name="reminder-enabled" tabindex="4" class="sci-checkbox" id="reminder-selector-checkbox" :checked="reminderEnabled" @change="reminderEnabled = $event.target.checked"/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</div>
|
||||
<span class="ml-2">{{ i18n.t('repository_stock_values.manage_modal.create_reminder') }}</span>
|
||||
</div>
|
||||
<div v-if="reminderEnabled" class="stock-reminder-value flex gap-2 items-center">
|
||||
<Input
|
||||
@input="lowStockTreshold = $event"
|
||||
name="treshold_amount"
|
||||
id="treshold-amount"
|
||||
fieldClass="flex gap-2"
|
||||
inputClass="sci-input-container-v2 w-40"
|
||||
labelClass="text-sm font-normal flex items-center"
|
||||
type="number"
|
||||
:value="lowStockTreshold"
|
||||
:decimals="stockValue.decimals"
|
||||
:placeholder="i18n.t('repository_stock_values.manage_modal.amount_placeholder_new')"
|
||||
required
|
||||
:label="i18n.t('repository_stock_values.manage_modal.reminder_at')"
|
||||
showLabel
|
||||
:error="errors.tresholdAmount"
|
||||
/>
|
||||
<span class="text-sm font-normal">
|
||||
{{ unitLabel }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="sci-input-container flex flex-col" :data-error-text="i18n.t('repository_stock_values.manage_modal.comment_limit')">
|
||||
<label class="text-sn-grey text-sm font-normal" for="stock-value-comment">{{ i18n.t('repository_stock_values.manage_modal.comment') }}</label>
|
||||
<input class="sci-input-field"
|
||||
@input="comment = e.target.value"
|
||||
type="text"
|
||||
name="comment"
|
||||
id="stock-value-comment"
|
||||
:placeholder="i18n.t('repository_stock_values.manage_modal.comment_placeholder')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type='button' class='btn btn-secondary' data-dismiss='modal'>
|
||||
{{ i18n.t('general.cancel') }}
|
||||
</button>
|
||||
<button class="btn btn-primary" @click="validateAndsaveStockValue">
|
||||
{{ i18n.t('repository_stock_values.manage_modal.save_stock') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Select from './../shared/select.vue';
|
||||
import Input from './../shared/input.vue';
|
||||
|
||||
export default {
|
||||
name: 'ManageStockValueModal',
|
||||
components: {
|
||||
Select,
|
||||
Input
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
operation: null,
|
||||
operations: [],
|
||||
stockValue: null,
|
||||
amount: 0,
|
||||
repositoryRowName: null,
|
||||
stockUrl: null,
|
||||
units: null,
|
||||
unit: null,
|
||||
reminderEnabled: false,
|
||||
lowStockTreshold: null,
|
||||
comment: null,
|
||||
errors: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
unitLabel: function() {
|
||||
const currentUnit = this.units?.find(option => option[0] === this.unit);
|
||||
return currentUnit ? currentUnit[1] : ''
|
||||
},
|
||||
initUnitLabel: function() {
|
||||
const unit = this.units?.find(option => option[0] === this.stockValue?.unit);
|
||||
return unit ? unit[1] : ''
|
||||
},
|
||||
newAmount: function() {
|
||||
switch (this.operation) {
|
||||
case 2:
|
||||
if (this.amount) return parseFloat(this.stockValue.amount) + this.amount
|
||||
case 3:
|
||||
if(this.amount) return parseFloat(this.stockValue.amount) - this.amount
|
||||
default:
|
||||
return this.amount
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.manageStockModalComponent = this;
|
||||
},
|
||||
beforeDestroy() {
|
||||
delete window.manageStockModalComponent;
|
||||
},
|
||||
mounted() {
|
||||
// Focus stock amount input field
|
||||
$(this.$refs.modal).on('show.bs.modal', function() {
|
||||
setTimeout(() => {
|
||||
$('#stock-amount')[0]?.focus()
|
||||
}, 500)
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
setOperation($event) {
|
||||
this.operation = $event;
|
||||
if ([2, 3].includes($event)) {
|
||||
this.unit = this.stockValue.unit;
|
||||
}
|
||||
},
|
||||
fetchStockValueData(stockValueUrl) {
|
||||
if (!stockValueUrl) return;
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: stockValueUrl,
|
||||
dataType: 'json',
|
||||
success: (result) => {
|
||||
this.repositoryRowName = result.repository_row_name
|
||||
this.stockValue = result.stock_value
|
||||
this.amount = parseFloat(result.stock_value.amount)
|
||||
this.units = result.stock_value.units
|
||||
this.unit = result.stock_value.unit
|
||||
this.reminderEnabled = result.stock_value.reminder_enabled
|
||||
this.lowStockTreshold = result.stock_value.low_stock_treshold
|
||||
this.operation = 1;
|
||||
this.stockUrl = result.stock_url;
|
||||
this.operations = [[1, 'set'], [2, 'add'], [3, 'remove']];
|
||||
this.errors = {};
|
||||
}
|
||||
});
|
||||
},
|
||||
closeModal() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
showModal(stockValueUrl, closeCallback) {
|
||||
$(this.$refs.modal).modal('show');
|
||||
this.fetchStockValueData(stockValueUrl);
|
||||
this.closeCallback = closeCallback;
|
||||
},
|
||||
validateAndsaveStockValue() {
|
||||
let newErrors = {};
|
||||
this.errors = newErrors;
|
||||
if (!this.unit)
|
||||
newErrors['unit'] = I18n.t('repository_stock_values.manage_modal.unit_error');
|
||||
if (!this.amount)
|
||||
newErrors['amount'] = I18n.t('repository_stock_values.manage_modal.amount_error');
|
||||
if (parseFloat(this.amount) && this.amount < 0)
|
||||
newErrors['amount'] = I18n.t('repository_stock_values.manage_modal.negative_error');
|
||||
if (this.reminderEnabled && !this.lowStockTreshold)
|
||||
newErrors['tresholdAmount'] = I18n.t('repository_stock_values.manage_modal.amount_error');
|
||||
|
||||
this.errors = newErrors;
|
||||
|
||||
if (!$.isEmptyObject(newErrors)) return;
|
||||
|
||||
const $this = this
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: this.stockUrl,
|
||||
dataType: 'json',
|
||||
data: {
|
||||
repository_stock_value: {
|
||||
unit_item_id: this.unit,
|
||||
amount: this.newAmount,
|
||||
comment: this.comment,
|
||||
low_stock_threshold: this.reminderEnabled ? this.lowStockTreshold : null
|
||||
},
|
||||
operator: this.operations.find(operation => operation[0] = this.operation)?.[1],
|
||||
change_amount: Math.abs(this.amount),
|
||||
},
|
||||
success: function(result) {
|
||||
$this.stockValue = null;
|
||||
$this.closeModal();
|
||||
$this.closeCallback && $this.closeCallback(result);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
73
app/javascript/vue/shared/input.vue
Normal file
73
app/javascript/vue/shared/input.vue
Normal file
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div class="relative" :class="fieldClass">
|
||||
<label v-if="showLabel" :class="labelClass" :for="id">{{ label }}</label>
|
||||
<div :class="inputClass">
|
||||
<input ref="input"
|
||||
:id="id"
|
||||
:type="type"
|
||||
:name="name"
|
||||
:value="value"
|
||||
:class="`${error ? 'error' : ''}`"
|
||||
:placeholder="placeholder"
|
||||
:required="required"
|
||||
@input="updateValue"
|
||||
/>
|
||||
<div
|
||||
class="mt-2 text-sn-delete-red whitespace-nowrap truncate text-xs font-normal absolute bottom-[-1rem] w-full"
|
||||
:title="error"
|
||||
:class="{ visible: error, invisible: !error}"
|
||||
>
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Input',
|
||||
props: {
|
||||
id: { type: String, required: false },
|
||||
fieldClass: { type: String, default: '' },
|
||||
inputClass: { type: String, default: '' },
|
||||
labelClass: { type: String, default: '' },
|
||||
type: { type: String, default: 'text' },
|
||||
name: { type: String, required: true },
|
||||
value: { type: [String, Number], required: false },
|
||||
decimals: { type: [Number, String], default: 0 },
|
||||
placeholder: { type: String, default: '' },
|
||||
required: { type: Boolean, default: false },
|
||||
showLabel: { type: Boolean, default: false },
|
||||
label: { type: String, required: false },
|
||||
autoFocus: { type: Boolean, default: false },
|
||||
error: { type: String, required: false }
|
||||
},
|
||||
watch: {
|
||||
value: function(newVal) {
|
||||
this.inputValue = newVal;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue($event) {
|
||||
switch (this.type) {
|
||||
case 'text':
|
||||
this.$emit('input', $event.target.value);
|
||||
break;
|
||||
case 'number':
|
||||
const newValue = this.formatDecimalValue($event.target.value);
|
||||
this.$emit('input', parseFloat(newValue));
|
||||
break
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
formatDecimalValue(value) {
|
||||
let decimalValue = value.replace(/[^-0-9.]/g, '');
|
||||
if (this.decimals === 0) {
|
||||
return decimalValue.split('.')[0];
|
||||
}
|
||||
return decimalValue.match(new RegExp(`^-?\\d*(\\.\\d{0,${this.decimals}})?`))[0];
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -8,7 +8,8 @@
|
|||
'sn-select--blank': !valueLabel,
|
||||
'disabled cursor-default': disabled,
|
||||
'cursor-pointer': !withEditCursor,
|
||||
'sci-cursor-edit hover:border-sn-sleepy-grey': !disabled && !isOpen && withEditCursor
|
||||
'sci-cursor-edit hover:border-sn-sleepy-grey': !disabled && !isOpen && withEditCursor,
|
||||
[className]: true
|
||||
}">
|
||||
<slot>
|
||||
<button ref="focusElement" class="sn-select__value">
|
||||
|
@ -66,6 +67,7 @@
|
|||
initialValue: { type: [String, Number] },
|
||||
placeholder: { type: String },
|
||||
noOptionsPlaceholder: { type: String },
|
||||
className: { type: String, default: '' },
|
||||
disabled: { type: Boolean, default: false }
|
||||
},
|
||||
directives: {
|
||||
|
|
|
@ -3,27 +3,37 @@
|
|||
module RepositoryDatatable
|
||||
class RepositoryStockValueSerializer < RepositoryBaseValueSerializer
|
||||
include Canaid::Helpers::PermissionsHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
def value
|
||||
data = {
|
||||
stock_formatted: value_object.formatted,
|
||||
stock_amount: value_object.data,
|
||||
low_stock_threshold: value_object.low_stock_threshold
|
||||
low_stock_threshold: value_object.low_stock_threshold,
|
||||
stock_url: edit_repository_stock_repository_repository_row_url(scope[:repository],
|
||||
value_object.repository_row)
|
||||
}
|
||||
data.merge(reminder_values)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reminder_values
|
||||
data = {}
|
||||
if scope.dig(:options, :reminders_enabled) &&
|
||||
!scope[:repository].is_a?(RepositorySnapshot) &&
|
||||
value_object.data.present? &&
|
||||
value_object.low_stock_threshold.present?
|
||||
data[:reminder] = value_object.low_stock_threshold > value_object.data
|
||||
if data[:reminder] && (data[:stock_amount]).positive?
|
||||
if data[:reminder] && value_object.data&.positive?
|
||||
data[:reminder_text] =
|
||||
I18n.t('repositories.item_card.reminders.stock_low', stock_formated: data[:stock_formatted])
|
||||
I18n.t('repositories.item_card.reminders.stock_low', stock_formated: value_object.formatted)
|
||||
elsif data[:reminder]
|
||||
data[:reminder_text] = I18n.t('repositories.item_card.reminders.stock_empty')
|
||||
end
|
||||
end
|
||||
|
||||
if data[:stock_amount].zero?
|
||||
if value_object.data && value_object.data <= 0
|
||||
data[:reminder] = true
|
||||
data[:reminder_text] = I18n.t('repositories.item_card.reminders.stock_empty')
|
||||
end
|
||||
|
|
|
@ -152,8 +152,13 @@
|
|||
<!-- Delete file modal -->
|
||||
<%= render partial: 'assets/asset_delete_modal' %>
|
||||
|
||||
<!-- Manage Stock Modal -->
|
||||
<%= render partial: 'shared/manage_stock_value_modal' %>
|
||||
|
||||
<!-- Consume Stock Modal -->
|
||||
<%= render partial: 'my_modules/repositories/consume_stock_modal'%>
|
||||
|
||||
<%= javascript_include_tag 'inputmask' %>
|
||||
<%= stylesheet_link_tag 'datatables' %>
|
||||
<%= javascript_include_tag "handsontable.full" %>
|
||||
<%= render partial: "shared/formulas_libraries" %>
|
||||
|
|
|
@ -73,12 +73,12 @@
|
|||
locals: { repository: @repository } %>
|
||||
|
||||
<%= render partial: 'repository_columns/manage_column_modal', locals: { my_module_page: false } %>
|
||||
<%= render partial: "repository_stock_values/manage_modal" %>
|
||||
<%= render partial: "toolbar_buttons" %>
|
||||
<%= render partial: "assign_items_to_task_modal" %>
|
||||
|
||||
<%= render partial: 'repository_filters' %>
|
||||
<%= render partial: 'save_repository_filter_modal' %>
|
||||
<%= render partial: 'shared/manage_stock_value_modal' %>
|
||||
|
||||
|
||||
<%= javascript_include_tag 'vue_components_action_toolbar' %>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.id @repository_row.id
|
||||
json.repository do
|
||||
json.id @repository.id
|
||||
json.name @repository.name
|
||||
|
@ -20,6 +21,13 @@ json.actions do
|
|||
end
|
||||
end
|
||||
json.direct_file_upload_path rails_direct_uploads_url
|
||||
json.stock do
|
||||
if @repository_row.has_stock?
|
||||
json.stock_value_url edit_repository_stock_repository_repository_row_url(@repository, @repository_row)
|
||||
elsif @repository.has_stock_management?
|
||||
json.stock_value_url new_repository_stock_repository_repository_row_url(@repository, @repository_row)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
json.default_columns do
|
||||
|
@ -58,8 +66,10 @@ json.custom_columns do
|
|||
end
|
||||
|
||||
if repository_cell
|
||||
json.merge! **serialize_repository_cell_value(repository_cell, @repository.team, @repository, reminders_enabled: @reminders_present).merge(
|
||||
**repository_cell.repository_column.as_json(only: %i(id name data_type))
|
||||
json.merge! serialize_repository_cell_value(
|
||||
repository_cell, @repository.team, @repository, reminders_enabled: @reminders_present
|
||||
).merge(
|
||||
repository_cell.repository_column.as_json(only: %i(id name data_type))
|
||||
).merge(options)
|
||||
else
|
||||
json.merge! repository_column.as_json(only: %i(id name data_type)).merge(options)
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<div class="modal repository-stock-modal"
|
||||
id="manage-repository-stock-value-modal"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
aria-labelledby="manageRepositoryStockValueLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%= javascript_include_tag 'repositories/stock' %>
|
|
@ -1,143 +0,0 @@
|
|||
<%= form_tag update_repository_stock_repository_repository_row_path(@repository, @repository_row), method: :post, remote: true, novalidate: true do %>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="<%= t('general.close') %>">
|
||||
<i class="sn-icon sn-icon-close"></i>
|
||||
</button>
|
||||
<h4 class="modal-title" id="modal-delete-module-label">
|
||||
<% if repository_stock_value.new_record? %>
|
||||
<%= t('repository_stock_values.manage_modal.title', item: repository_row.name) %>
|
||||
<% else %>
|
||||
<%= t('repository_stock_values.manage_modal.edit_title', item: repository_row.name) %>
|
||||
<% end %>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<%= hidden_field_tag 'repository_stock_value[amount]', repository_stock_value.amount %>
|
||||
<%= hidden_field_tag 'initial_units', repository_stock_value.repository_stock_unit_item_id %>
|
||||
<%= hidden_field_tag 'change_amount' %>
|
||||
<%= hidden_field_tag 'operator', 'set' %>
|
||||
<p><%= t('repository_stock_values.manage_modal.enter_amount') %></p>
|
||||
<div class="row">
|
||||
<div class="col-sm-5 !pr-0">
|
||||
<label><%= t('repository_stock_values.manage_modal.operation') %></label>
|
||||
<% if repository_stock_value.id %>
|
||||
<div class="btn-group" role="group" aria-label="Operator group">
|
||||
<button type="button" data-operator="set" class="btn btn-primary stock-operator-option"><%= t('repository_stock_values.manage_modal.set') %></button>
|
||||
<button type="button" data-operator="add" class="btn btn-secondary stock-operator-option"><%= t('repository_stock_values.manage_modal.add') %></button>
|
||||
<button type="button" data-operator="remove" class="btn btn-secondary stock-operator-option"><%= t('repository_stock_values.manage_modal.remove') %></button>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="btn-group" role="group" aria-label="Operator group">
|
||||
<button type="button" data-operator="set" class="btn btn-primary"><%= t('repository_stock_values.manage_modal.set') %></button>
|
||||
<button type="button" data-operator="add" class="btn btn-secondary disabled"><%= t('repository_stock_values.manage_modal.add') %></button>
|
||||
<button type="button" data-operator="remove" class="btn btn-secondary disabled"><%= t('repository_stock_values.manage_modal.remove') %></button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="sci-input-container" data-error-text="<%= t('repository_stock_values.manage_modal.amount_error') %>">
|
||||
<label><%= t('repository_stock_values.manage_modal.amount') %></label>
|
||||
<input id="stock-input-amount"
|
||||
class="sci-input-field"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
name="input-amount"
|
||||
data-operator="set"
|
||||
tabindex="1"
|
||||
data-current-amount="<%= repository_stock_value.amount %>"
|
||||
data-decimals="<%= repository_stock_column.metadata['decimals'] %>"
|
||||
value="<%= repository_stock_value.formatted_value %>"
|
||||
placeholder="<%= t("repository_stock_values.manage_modal.amount_placeholder_new") %>"
|
||||
required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label><%= t('repository_stock_values.manage_modal.unit') %></label>
|
||||
<select class="form-control" name="repository_stock_value[unit_item_id]" id="repository-stock-value-units" required>
|
||||
<option valus=""><%= t('repository_stock_values.manage_modal.unit_prompt') %></option>
|
||||
<% unit_items.each do |unit_item| %>
|
||||
<option value="<%= unit_item.id %>" <%= 'selected' if repository_stock_value.repository_stock_unit_item == unit_item || unit_items.one?%>>
|
||||
<%= unit_item.data %>
|
||||
</option>
|
||||
<% end %>
|
||||
</select>
|
||||
<div class="input-error-message">
|
||||
<%= t('repository_stock_values.manage_modal.unit_error') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="stock-comment-field sci-input-container" data-error-text="<%= t('repository_stock_values.manage_modal.comment_limit') %>">
|
||||
<label><%= t('repository_stock_values.manage_modal.comment') %></label>
|
||||
<input class="sci-input-field"
|
||||
type="text"
|
||||
tabindex="3"
|
||||
name="repository_stock_value[comment]"
|
||||
id="repository-stock-value-comment"
|
||||
placeholder="<%= t('repository_stock_values.manage_modal.comment_placeholder') %>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="repository-stock-reminder-selector">
|
||||
<div class="sci-checkbox-container">
|
||||
<input type="checkbox" name="reminder-enabled" tabindex="4" class="sci-checkbox" id="reminder-selector-checkbox" <%= "checked" if repository_stock_value.low_stock_threshold.present? %>/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</div>
|
||||
<%= t('repository_stock_values.manage_modal.create_reminder') %>
|
||||
</div>
|
||||
<div class="repository-stock-reminder-value <%= "hidden" if repository_stock_value.low_stock_threshold.blank? %>">
|
||||
<span><%= t('repository_stock_values.manage_modal.reminder_at') %></span>
|
||||
<div class="sci-input-container" data-error-text="<%= t('repository_stock_values.manage_modal.enter_ammount') %>">
|
||||
<input type="text"
|
||||
autocomplete="off"
|
||||
id="low_stock_threshold"
|
||||
name="repository_stock_value[low_stock_threshold]"
|
||||
value="<%= repository_stock_value.formatted_treshold %>"
|
||||
data-decimals="<%= repository_stock_column.metadata['decimals'] %>"
|
||||
class="sci-input-field"
|
||||
tabindex="5"
|
||||
placeholder="Enter amount"/>
|
||||
</div>
|
||||
<span class="units">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% unless repository_stock_value.new_record? %>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="stock-update-view">
|
||||
<div class="stock-initial-container <%= 'negative' if repository_stock_value.amount.negative? %>">
|
||||
<span class="subtitle"><%= t('repository_stock_values.manage_modal.current_stock') %></span>
|
||||
<span class="value"><%= repository_stock_value.formatted_value %></span>
|
||||
<span class="units"><%= repository_stock_value.repository_stock_unit_item&.data %></span>
|
||||
</div>
|
||||
<div class="stock-arrow">
|
||||
<i class="sn-icon sn-icon-arrow-right"></i>
|
||||
</div>
|
||||
<div class="stock-final-container <%= 'negative' if repository_stock_value.amount.negative? %> ">
|
||||
<span class="subtitle"><%= t('repository_stock_values.manage_modal.new_stock') %></span>
|
||||
<span class="value"><%= repository_stock_value.formatted_value %></span>
|
||||
<span class="units"><%= repository_stock_value.repository_stock_unit_item&.data %></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
id="cancel"
|
||||
class="btn btn-secondary"
|
||||
tabindex="7"
|
||||
data-dismiss="modal"><%=t 'general.cancel' %></button>
|
||||
<%= submit_tag t('repository_stock_values.manage_modal.save_stock'), class: "btn btn-primary update-repository-stock", tabindex: "6" %>
|
||||
</div>
|
||||
<% end %>
|
13
app/views/repository_stock_values/edit.json.jbuilder
Normal file
13
app/views/repository_stock_values/edit.json.jbuilder
Normal file
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.stock_url update_repository_stock_repository_repository_row_url(@repository, @repository_row)
|
||||
json.repository_row_name @repository_row.name
|
||||
json.stock_value do
|
||||
json.id @repository_stock_value.id
|
||||
json.amount @repository_stock_value.formatted_value
|
||||
json.decimals @repository_column.metadata['decimals']
|
||||
json.units @repository_column.repository_stock_unit_items.pluck(:id, :data)
|
||||
json.unit @repository_stock_value.repository_stock_unit_item_id
|
||||
json.reminder_enabled @repository_stock_value.low_stock_threshold.present?
|
||||
json.low_stock_treshold @repository_stock_value.formatted_treshold
|
||||
end
|
8
app/views/repository_stock_values/new.json.jbuilder
Normal file
8
app/views/repository_stock_values/new.json.jbuilder
Normal file
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
json.stock_url update_repository_stock_repository_repository_row_url(@repository, @repository_row)
|
||||
json.repository_row_name @repository_row.name
|
||||
json.stock_value do
|
||||
json.decimals @repository_column.metadata['decimals']
|
||||
json.units @repository_column.repository_stock_unit_items.pluck(:id, :data)
|
||||
end
|
5
app/views/shared/_manage_stock_value_modal.erb
Normal file
5
app/views/shared/_manage_stock_value_modal.erb
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div id="manageStockValueModal" data-behaviour="vue">
|
||||
<manage-stock-value-modal />
|
||||
</div>
|
||||
|
||||
<%= javascript_include_tag('vue_components_manage_stock_value_modal') %>
|
|
@ -81,7 +81,6 @@ Rails.application.config.assets.precompile += %w(label_printers/zebra_settings.j
|
|||
Rails.application.config.assets.precompile += %w(repositories/index.js)
|
||||
Rails.application.config.assets.precompile += %w(repositories/share_modal.js)
|
||||
Rails.application.config.assets.precompile += %w(repositories/edit.js)
|
||||
Rails.application.config.assets.precompile += %w(repositories/stock.js)
|
||||
Rails.application.config.assets.precompile += %w(repositories/repository_datatable.js)
|
||||
Rails.application.config.assets.precompile += %w(global_activities/index.js)
|
||||
Rails.application.config.assets.precompile += %w(repositories/show.js)
|
||||
|
|
|
@ -2294,6 +2294,10 @@ en:
|
|||
expand: 'Expand'
|
||||
collapse: 'Collapse'
|
||||
dropdown_placeholder: 'Select'
|
||||
date_time:
|
||||
errors:
|
||||
set_all_or_none: 'Needs to set both or none'
|
||||
not_valid_range: 'Range is not valid.'
|
||||
highlight_component:
|
||||
information_label: 'Information'
|
||||
custom_columns_label: 'Custom columns'
|
||||
|
|
|
@ -43,7 +43,8 @@ const entryList = {
|
|||
vue_components_open_vector_editor: './app/javascript/packs/vue/open_vector_editor.js',
|
||||
vue_navigation_breadcrumbs: './app/javascript/packs/vue/navigation/breadcrumbs.js',
|
||||
vue_protocol_file_import_modal: './app/javascript/packs/vue/protocol_file_import_modal.js',
|
||||
vue_components_export_stock_consumption_modal: './app/javascript/packs/vue/export_stock_consumption_modal.js'
|
||||
vue_components_export_stock_consumption_modal: './app/javascript/packs/vue/export_stock_consumption_modal.js',
|
||||
vue_components_manage_stock_value_modal: './app/javascript/packs/vue/manage_stock_value_modal.js'
|
||||
}
|
||||
|
||||
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949
|
||||
|
|
Loading…
Reference in a new issue