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