mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-01-04 14:44:26 +08:00
Merge pull request #6907 from aignatov-bio/ai-sci-9939-fix-filter-component
Refactor filter component [SCI-9939]
This commit is contained in:
commit
3194a28106
9 changed files with 181 additions and 154 deletions
|
@ -29,7 +29,7 @@
|
|||
<ag-grid-vue
|
||||
v-if="currentViewRender === 'table'"
|
||||
class="ag-theme-alpine w-full flex-grow h-full z-10"
|
||||
:class="{'opacity-0': initializing, 'tw-hidden': dataLoading}"
|
||||
:class="{'opacity-0': initializing }"
|
||||
:columnDefs="extendedColumnDefs"
|
||||
:rowData="rowData"
|
||||
:defaultColDef="defaultColDef"
|
||||
|
@ -48,8 +48,8 @@
|
|||
:CheckboxSelectionCallback="withCheckboxes"
|
||||
>
|
||||
</ag-grid-vue>
|
||||
<div v-if="dataLoading" class="flex items-center justify-center w-full flex-grow h-full z-10">
|
||||
<img src="/images/medium/loading.svg" alt="Loading" />
|
||||
<div v-if="dataLoading" class="flex absolute top-0 items-center justify-center w-full flex-grow h-full z-10">
|
||||
<img src="/images/medium/loading.svg" alt="Loading" class="p-4 rounded-xl bg-sn-white" />
|
||||
</div>
|
||||
<ActionToolbar
|
||||
v-if="selectedRows.length > 0 && actionsUrl"
|
||||
|
@ -291,14 +291,10 @@ export default {
|
|||
if (this.dataLoading) return;
|
||||
|
||||
this.dataLoading = true;
|
||||
if (this.gridApi) {
|
||||
this.gridApi.setRowData([]);
|
||||
}
|
||||
this.rowData = [];
|
||||
this.page = 1;
|
||||
this.loadData();
|
||||
this.loadData(true);
|
||||
},
|
||||
loadData() {
|
||||
loadData(reload = false) {
|
||||
axios
|
||||
.get(this.dataUrl, {
|
||||
params: {
|
||||
|
@ -311,6 +307,12 @@ export default {
|
|||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (reload) {
|
||||
if (this.gridApi) {
|
||||
this.gridApi.setRowData([]);
|
||||
}
|
||||
this.rowData = [];
|
||||
}
|
||||
this.selectedRows = [];
|
||||
if (this.gridApi) {
|
||||
this.gridApi.setRowData(this.formatData(response.data.data));
|
||||
|
|
|
@ -1,20 +1,34 @@
|
|||
<template>
|
||||
<div class="relative">
|
||||
<div ref="dropdown" class="filter-container dropdown" :class="{ 'filters-applied': appliedDotIsShown }">
|
||||
<button class="btn btn-light icon-btn filter-button" :title="i18n.t('filters_modal.title')" data-toggle="dropdown"><i class="sn-icon sn-icon-filter"></i></button>
|
||||
<div class="dropdown-menu dropdown-menu-right sci-flyout" @click.stop.self>
|
||||
<div class="sci-flyout-header">
|
||||
<div class="sci-flyout-title">{{ i18n.t('filters_modal.title') }}</div>
|
||||
<button @click="toggleDropdown" type="button" class="ml-auto btn btn-light btn-black icon-btn" data-dismiss="modal" aria-label="Close">
|
||||
<GeneralDropdown ref="dropdown" position="right" @close="$emit('applyFilters', filterValues)">
|
||||
<template v-slot:field>
|
||||
<button class="btn btn-light icon-btn"
|
||||
:title="i18n.t('filters_modal.title')">
|
||||
<i class="sn-icon sn-icon-filter"></i>
|
||||
<div
|
||||
v-if="appliedDotIsShown"
|
||||
class="w-1.5 h-1.5 rounded bg-sn-delete-red absolute right-1 top-1"
|
||||
></div>
|
||||
</button>
|
||||
</template>
|
||||
<template v-slot:flyout>
|
||||
<div class="p-3.5 pb-4 flex items-center min-w-[400px]">
|
||||
<h2>{{ i18n.t('filters_modal.title') }}</h2>
|
||||
<button @click="close" type="button"
|
||||
class="ml-auto btn btn-light btn-black icon-btn">
|
||||
<i class="sn-icon sn-icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sci-flyout-body max-h-[400px] relative w-[calc(100%_+_1.125rem)] pr-5">
|
||||
<div v-for="filter in filters" :key="filter.key + resetFilters" class="">
|
||||
<Component :is="`${filter.type}Filter`" :filter="filter" :value="filterValues[filter.key]" @update="updateFilter" />
|
||||
<div class="max-h-[400px] p-3.5 pt-0">
|
||||
<div v-for="filter in filters" :key="filter.key">
|
||||
<Component
|
||||
:is="`${filter.type}Filter`"
|
||||
:filter="filter"
|
||||
:values="filterValues"
|
||||
@update="updateFilter" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="sci-flyout-footer">
|
||||
<div class="p-3.5 pt-0.5 flex items-center justify-end gap-4">
|
||||
<div @click.prevent="clearFilters" class="btn btn-secondary">
|
||||
{{ i18n.t('filters_modal.clear_btn') }}
|
||||
</div>
|
||||
|
@ -22,60 +36,61 @@
|
|||
{{ i18n.t('filters_modal.show_btn.one') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</GeneralDropdown>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import TextFilter from './inputs/text_filter.vue'
|
||||
import SelectFilter from './inputs/select_filter.vue';
|
||||
import DateRangeFilter from './inputs/date_range_filter.vue';
|
||||
import CheckboxFilter from './inputs/checkbox_filter.vue';
|
||||
import TextFilter from './inputs/text_filter.vue';
|
||||
import SelectFilter from './inputs/select_filter.vue';
|
||||
import DateRangeFilter from './inputs/date_range_filter.vue';
|
||||
import CheckboxFilter from './inputs/checkbox_filter.vue';
|
||||
import GeneralDropdown from '../general_dropdown.vue';
|
||||
|
||||
export default {
|
||||
name: 'FilterDropdown',
|
||||
props: {
|
||||
filters: { type: Array, required: true }
|
||||
},
|
||||
components: { TextFilter, SelectFilter, DateRangeFilter, CheckboxFilter },
|
||||
data() {
|
||||
return {
|
||||
filterValues: {},
|
||||
filtersApplied: false,
|
||||
resetFilters: false
|
||||
export default {
|
||||
name: 'FilterDropdown',
|
||||
props: {
|
||||
filters: { type: Array, required: true }
|
||||
},
|
||||
components: {
|
||||
TextFilter,
|
||||
SelectFilter,
|
||||
DateRangeFilter,
|
||||
CheckboxFilter,
|
||||
GeneralDropdown
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filterValues: {},
|
||||
filtersApplied: false,
|
||||
resetFilters: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
appliedDotIsShown() {
|
||||
return Object.keys(this.filterValues).length !== 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateFilter(params) {
|
||||
if (params.value !== '' && params.value !== undefined && params.value !== null) {
|
||||
this.filterValues[params.key] = params.value;
|
||||
} else {
|
||||
delete this.filterValues[params.key];
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
appliedDotIsShown() {
|
||||
return this.filtersApplied && Object.keys(this.filterValues).length !== 0;
|
||||
}
|
||||
applyFilters() {
|
||||
this.$emit('applyFilters', this.filterValues);
|
||||
this.close();
|
||||
},
|
||||
methods: {
|
||||
updateFilter(params) {
|
||||
if (params.value !== '' && params.value !== undefined && params.value !== null) {
|
||||
this.filterValues[params.key] = params.value;
|
||||
} else {
|
||||
delete this.filterValues[params.key];
|
||||
}
|
||||
},
|
||||
applyFilters() {
|
||||
this.filtersApplied = true;
|
||||
this.$emit('applyFilters', this.filterValues);
|
||||
this.toggleDropdown();
|
||||
},
|
||||
clearFilters() {
|
||||
this.filterValues = {};
|
||||
this.filtersApplied = false;
|
||||
|
||||
// This changes filter keys in the v-for, so they get fully reloaded,
|
||||
// which prevents perserved state issues with datepickers
|
||||
this.resetFilters = !this.resetFilters;
|
||||
this.$emit('applyFilters', this.filterValues);
|
||||
this.toggleDropdown();
|
||||
},
|
||||
toggleDropdown() {
|
||||
this.$refs.dropdown.click();
|
||||
}
|
||||
clearFilters() {
|
||||
this.filterValues = {};
|
||||
this.$emit('applyFilters', this.filterValues);
|
||||
this.close();
|
||||
},
|
||||
close() {
|
||||
this.$refs.dropdown.$refs.field.click();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -9,20 +9,21 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CheckboxFilter',
|
||||
props: {
|
||||
filter: { type: Object, required: true }
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
value: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: function() {
|
||||
this.$emit('update', { key: this.filter.key, value: this.value });
|
||||
}
|
||||
},
|
||||
export default {
|
||||
name: 'CheckboxFilter',
|
||||
props: {
|
||||
filter: { type: Object, required: true },
|
||||
values: { type: Object }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: this.values[this.filter.key]
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.$emit('update', { key: this.filter.key, value: this.value || null });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<template>
|
||||
<div class="mb-6">
|
||||
<div @click.stop class="mb-6">
|
||||
<div class="flex flex-col">
|
||||
<label class="sci-label">{{ filter.label }}</label>
|
||||
<div class="w-full mb-2">
|
||||
<DateTimePicker
|
||||
:defaultValue="dateFrom"
|
||||
class="w-full"
|
||||
@change="updateDateFrom"
|
||||
@cleared="updateDateFrom"
|
||||
:clearable="true"
|
||||
:placeholder="i18n.t('From')"
|
||||
:dateOnly="true"
|
||||
:selectorId="`DatePicker${filter.key}`"
|
||||
|
@ -13,8 +16,11 @@
|
|||
</div>
|
||||
<div class="w-full">
|
||||
<DateTimePicker
|
||||
:defaultValue="dateTo"
|
||||
class="w-full"
|
||||
@change="updateDateTo"
|
||||
@cleared="updateDateTo"
|
||||
:clearable="true"
|
||||
:placeholder="i18n.t('To')"
|
||||
:dateOnly="true"
|
||||
:selectorId="`DatePickerTo${filter.key}`"
|
||||
|
@ -25,33 +31,34 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimePicker from '../../date_time_picker.vue'
|
||||
import DateTimePicker from '../../date_time_picker.vue';
|
||||
|
||||
export default {
|
||||
name: 'DateRangeFilter',
|
||||
props: {
|
||||
filter: { type: Object, required: true }
|
||||
export default {
|
||||
name: 'DateRangeFilter',
|
||||
props: {
|
||||
filter: { type: Object, required: true },
|
||||
values: { type: Object, required: true }
|
||||
},
|
||||
components: { DateTimePicker },
|
||||
data() {
|
||||
return {
|
||||
dateFrom: this.values[`${this.filter.key}_from`],
|
||||
dateTo: this.values[`${this.filter.key}_to`]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateDateFrom(value) {
|
||||
this.dateFrom = value;
|
||||
this.updateFilter();
|
||||
},
|
||||
components: { DateTimePicker },
|
||||
data() {
|
||||
return {
|
||||
dateFrom: null,
|
||||
dateTo: null
|
||||
}
|
||||
updateDateTo(value) {
|
||||
this.dateTo = value;
|
||||
this.updateFilter();
|
||||
},
|
||||
methods: {
|
||||
updateDateFrom(value) {
|
||||
this.dateFrom = value;
|
||||
this.updateFilter();
|
||||
},
|
||||
updateDateTo(value) {
|
||||
this.dateTo = value;
|
||||
this.updateFilter();
|
||||
},
|
||||
updateFilter() {
|
||||
this.$emit('update', { key: `${this.filter.key}_from`, value: this.dateFrom });
|
||||
this.$emit('update', { key: `${this.filter.key}_to`, value: this.dateTo });
|
||||
}
|
||||
updateFilter() {
|
||||
this.$emit('update', { key: `${this.filter.key}_from`, value: this.dateFrom });
|
||||
this.$emit('update', { key: `${this.filter.key}_to`, value: this.dateTo });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<SelectDropdown
|
||||
:optionsUrl="filter.optionsUrl"
|
||||
:options="filter.options"
|
||||
:selectedValue="value"
|
||||
:value="value"
|
||||
:multiple="true"
|
||||
:with-checkboxes="true"
|
||||
:placeholder="filter.placeholder"
|
||||
|
@ -16,32 +16,33 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import SelectDropdown from '../../select_dropdown.vue';
|
||||
import SelectDropdown from '../../select_dropdown.vue';
|
||||
|
||||
export default {
|
||||
name: 'SelectFilter',
|
||||
props: {
|
||||
filter: { type: Object, required: true }
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
value: []
|
||||
export default {
|
||||
name: 'SelectFilter',
|
||||
props: {
|
||||
filter: { type: Object, required: true },
|
||||
values: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: this.values[this.filter.key]
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
let { value } = this;
|
||||
if (this.value.length === 0) {
|
||||
value = null;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: function() {
|
||||
let value = this.value;
|
||||
if (this.value.length == 0) {
|
||||
value = null;
|
||||
}
|
||||
this.$emit('update', { key: this.filter.key, value: value });
|
||||
}
|
||||
},
|
||||
components: { SelectDropdown },
|
||||
methods: {
|
||||
change: function(value) {
|
||||
this.value = value;
|
||||
}
|
||||
},
|
||||
this.$emit('update', { key: this.filter.key, value });
|
||||
}
|
||||
},
|
||||
components: { SelectDropdown },
|
||||
methods: {
|
||||
change(value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="mb-6">
|
||||
<inputWithHistory
|
||||
:modelValue="value"
|
||||
:modelValue="values[filter.key]"
|
||||
:label="filter.label || i18n.t('filters_modal.text.label')"
|
||||
@update:modelValue="update"
|
||||
:placeholder="filter.placeholder || i18n.t('filters_modal.text.placeholder')"
|
||||
|
@ -10,21 +10,21 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import inputWithHistory from '../../input_with_history.vue';
|
||||
import inputWithHistory from '../../input_with_history.vue';
|
||||
|
||||
export default {
|
||||
name: 'TextFilter',
|
||||
props: {
|
||||
filter: { type: Object, required: true },
|
||||
value: { type: String }
|
||||
},
|
||||
components: {
|
||||
inputWithHistory
|
||||
},
|
||||
methods: {
|
||||
update(value) {
|
||||
this.$emit('update', { key: this.filter.key, value: value });
|
||||
}
|
||||
export default {
|
||||
name: 'TextFilter',
|
||||
props: {
|
||||
filter: { type: Object, required: true },
|
||||
values: { type: Object }
|
||||
},
|
||||
components: {
|
||||
inputWithHistory
|
||||
},
|
||||
methods: {
|
||||
update(value) {
|
||||
this.$emit('update', { key: this.filter.key, value });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -58,7 +58,8 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
closeMenu(e) {
|
||||
if (e && e.target.closest('.sn-dropdown')) return;
|
||||
|
||||
if (e && e.target.closest('.sn-dropdown, .sn-select-dropdown, .dp__instance_calendar')) return;
|
||||
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
</div>
|
||||
<teleport to="body">
|
||||
<div v-if="isOpen" ref="flyout"
|
||||
class="sn-dropdown bg-white inline-block sn-shadow-menu-sm rounded w-full
|
||||
class="sn-select-dropdown bg-white inline-block sn-shadow-menu-sm rounded w-full
|
||||
fixed z-[3000]">
|
||||
<div v-if="multiple && withCheckboxes" class="p-2.5 pb-0">
|
||||
<div @click="selectAll" :class="sizeClass"
|
||||
|
@ -263,7 +263,7 @@ export default {
|
|||
this.$emit('change', this.newValue);
|
||||
},
|
||||
close(e) {
|
||||
if (e && e.target.closest('.sn-dropdown')) return;
|
||||
if (e && e.target.closest('.sn-select-dropdown')) return;
|
||||
|
||||
if (!this.isOpen) return;
|
||||
|
||||
|
|
|
@ -64,13 +64,13 @@ module Lists
|
|||
end
|
||||
|
||||
if @filters[:members].present?
|
||||
records = records.joins(:user_assignments).where(user_assignments: { user_id: @filters[:members] })
|
||||
records = records.joins(:user_assignments).where(user_assignments: { user_id: @filters[:members].values })
|
||||
end
|
||||
if @filters[:created_on_from].present?
|
||||
if @filters[:created_at_from].present?
|
||||
records = records.where('projects.created_at > ?',
|
||||
@filters[:created_on_from])
|
||||
@filters[:created_at_from])
|
||||
end
|
||||
records = records.where('projects.created_at < ?', @filters[:created_on_to]) if @filters[:created_on_to].present?
|
||||
records = records.where('projects.created_at < ?', @filters[:created_at_to]) if @filters[:created_at_to].present?
|
||||
if @filters[:archived_on_to].present?
|
||||
records = records.where('projects.archived_on < ?',
|
||||
@filters[:archived_on_to])
|
||||
|
|
Loading…
Reference in a new issue