Merge pull request #6907 from aignatov-bio/ai-sci-9939-fix-filter-component

Refactor filter component [SCI-9939]
This commit is contained in:
aignatov-bio 2024-01-09 14:01:14 +01:00 committed by GitHub
commit 3194a28106
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 181 additions and 154 deletions

View file

@ -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));

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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;
}

View file

@ -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;

View file

@ -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])