Add stock filters [SCI-6547] (#3938)

Co-authored-by: Anton <anton@scinote.net>
This commit is contained in:
aignatov-bio 2022-03-23 10:05:07 +01:00 committed by GitHub
parent 97c15eaac0
commit 004aeda601
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 183 additions and 2 deletions

View file

@ -173,11 +173,16 @@
}
}
&.stock-filter-attributes {
grid-template-columns: min-content auto 100px;
}
.number-range-selector {
align-items: center;
display: grid;
gap: .5em;
grid-auto-flow: column;
height: 2em;
}
.dropdown-selector-container {

View file

@ -35,6 +35,7 @@
import RepositoryStatusValue from 'vue/repository_filter/filters/repositoryStatusValue.vue'
import RepositoryChecklistValue from 'vue/repository_filter/filters/repositoryChecklistValue.vue'
import RepositoryUserValue from 'vue/repository_filter/filters/repositoryUserValue.vue'
import RepositoryStockValue from 'vue/repository_filter/filters/repositoryStockValue.vue'
import DropdownSelector from 'vue/shared/dropdown_selector.vue'
@ -61,7 +62,8 @@
RepositoryStatusValue,
RepositoryChecklistValue,
RepositoryListValue,
RepositoryUserValue
RepositoryUserValue,
RepositoryStockValue
},
methods: {
updateFilter(value) {

View file

@ -0,0 +1,127 @@
<template>
<div class="filter-attributes stock-filter-attributes">
<div class="operator-selector">
<DropdownSelector
:disableSearch="true"
:options="this.operators"
:selectedValue="this.operator"
:selectorId="`OperatorSelector${this.filter.id}`"
@dropdown:changed="updateOperator"
/>
</div>
<div v-if="operator !== 'between'" class="sci-input-container">
<input
class="sci-input-field"
type="text"
name="value"
v-model="value"
:placeholder= "this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.input_placeholder')"
/>
</div>
<div v-else class="number-range-selector">
<div class="sci-input-container">
<input
class="sci-input-field"
type="text"
name="from"
v-model="from"
:placeholder= "this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.from_placeholder')"
/>
</div>
<span class="between-delimiter"></span>
<div class="sci-input-container">
<input
class="sci-input-field"
type="text"
name="to"
v-model="to"
:placeholder= "this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.to_placeholder')"
/>
</div>
</div>
<div class="stock-unit-filter-dropdown">
<DropdownSelector
:singleSelect="true"
:closeOnSelect="true"
:selectedValue="this.stock_unit"
:options="this.prepareUnitOptions()"
:selectorId="`StockUnitSelector${this.filter.id}`"
@dropdown:changed="updateStockUnit"
/>
</div>
</div>
</template>
<script>
import FilterMixin from 'vue/repository_filter/mixins/filter.js'
import DropdownSelector from 'vue/shared/dropdown_selector.vue'
export default {
name: 'RepositoryStockValue',
mixins: [FilterMixin],
data() {
return {
operators: [
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to')},
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
],
operator: 'equal_to',
value: '',
from: '',
to: '',
stock_unit: 'all'
}
},
components: {
DropdownSelector
},
methods: {
validateNumber(number) {
return number.replace(/[^0-9.]/g, '').match(/^\d*(\.\d{0,10})?/)[0]
},
prepareUnitOptions() {
return [
{ label: this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.all_units'), value: 'all'},
{ label: this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.no_unit'), value: 'none'}
].concat(this.filter.column.items);
},
updateStockUnit(value) {
this.stock_unit = value
}
},
watch: {
stock_unit() {
this.parameters.stock_unit = this.stock_unit
this.updateFilter();
},
value() {
this.value = this.validateNumber(this.value)
this.parameters = { value: this.value, stock_unit: this.stock_unit }
this.updateFilter();
},
to() {
this.to = this.validateNumber(this.to)
this.parameters = {from: this.from, to: this.to, stock_unit: this.stock_unit}
this.updateFilter();
},
from() {
this.from = this.validateNumber(this.from)
this.parameters = {from: this.from, to: this.to, stock_unit: this.stock_unit}
this.updateFilter();
}
},
computed: {
isBlank(){
return (!this.value && this.operator != 'between') ||
((!this.to || !this.from) && this.operator == 'between');
}
}
}
</script>

View file

@ -123,6 +123,7 @@ class RepositoryColumn < ApplicationRecord
def items
items_method_name = "#{data_type.chomp('Value').underscore}_items"
items_method_name = 'repository_stock_unit_items' if data_type == 'RepositoryStockValue'
__send__(items_method_name) if respond_to?(items_method_name, true)
end

View file

@ -14,7 +14,6 @@ class RepositoryStockUnitItem < ApplicationRecord
validates :data, presence: true,
uniqueness: { scope: :repository_column_id },
length: { maximum: Constants::NAME_MAX_LENGTH }
private
def validate_per_column_limit

View file

@ -24,6 +24,44 @@ class RepositoryStockValue < ApplicationRecord
amount <= low_stock_threshold
end
def self.add_filter_condition(repository_rows, join_alias, filter_element)
parameters = filter_element.parameters
if filter_element.operator == 'between'
return repository_rows if parameters['from'].blank? || parameters['to'].blank?
elsif parameters['value'].blank?
return repository_rows
end
repository_rows = case parameters['stock_unit']
when 'all'
repository_rows.where("#{join_alias}.repository_stock_unit_item_id IS NOT NULL")
when 'none'
repository_rows.where("#{join_alias}.repository_stock_unit_item_id IS NULL")
else
repository_rows.where("#{join_alias}.repository_stock_unit_item_id = ?", parameters['stock_unit'])
end
case filter_element.operator
when 'equal_to'
repository_rows.where("#{join_alias}.amount = ?", parameters['value'].to_d)
when 'unequal_to'
repository_rows.where.not("#{join_alias}.amount = ?", parameters['value'].to_d)
when 'greater_than'
repository_rows.where("#{join_alias}.amount > ?", parameters['value'].to_d)
when 'greater_than_or_equal_to'
repository_rows.where("#{join_alias}.amount >= ?", parameters['value'].to_d)
when 'less_than'
repository_rows.where("#{join_alias}.amount < ?", parameters['value'].to_d)
when 'less_than_or_equal_to'
repository_rows.where("#{join_alias}.amount <= ?", parameters['value'].to_d)
when 'between'
repository_rows
.where("#{join_alias}.amount > ? AND #{join_alias}.amount < ?", parameters['from'].to_d, parameters['to'].to_d)
else
raise ArgumentError, 'Wrong operator for RepositoryStockValue!'
end
end
def data_changed?(new_data)
BigDecimal(new_data.to_s) != data
end

View file

@ -117,6 +117,8 @@ class Extends
table_name: :repository_date_time_values
}, RepositoryTimeRangeValue: {
table_name: :repository_date_time_range_values
}, RepositoryStockValue: {
table_name: :repository_stock_values
}
}
@ -139,6 +141,7 @@ class Extends
RepositoryDateRangeValue
RepositoryTimeValue
RepositoryTimeRangeValue
RepositoryStockValue
)
# Array of preload relations used in search query for repository rows

View file

@ -1504,6 +1504,12 @@ en:
select_placeholder: "Select user"
multiple_selected: "users selected"
all_selected: "All users selected"
RepositoryStockValue:
input_placeholder: "Enter numeric value"
to_placeholder: "To"
from_placeholder: "From"
all_units: "All units"
no_unit: "No unit"
bmt_search:
bmt_filter: "Biomolecule filter"
save_filters: "Save filters"