Skip to content

Commit

Permalink
Fix/461 views filtering (#538)
Browse files Browse the repository at this point in the history
* adjust FE to allow filtering for single selection fields
- adjust filter config
- code clean up
- refactor data management within vue filter components

Signed-off-by: Florian Steffens <florian.steffens@nextcloud.com>

* add query logic for single selection filtering

Signed-off-by: Florian Steffens <florian.steffens@nextcloud.com>

* add query logic for single selection filtering

Signed-off-by: Florian Steffens <florian.steffens@nextcloud.com>

---------

Signed-off-by: Florian Steffens <florian.steffens@nextcloud.com>
  • Loading branch information
Florian authored Sep 12, 2023
1 parent ef49b5e commit e803ba8
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 83 deletions.
8 changes: 7 additions & 1 deletion lib/Db/ColumnTypes/SelectionColumnQB.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

class SelectionColumnQB extends SuperColumnQB implements IColumnTypeQB {
public function passSearchValue(IQueryBuilder $qb, string $unformattedSearchValue, string $operator, string $searchValuePlaceHolder): void {
$qb->setParameter($searchValuePlaceHolder, $unformattedSearchValue, IQueryBuilder::PARAM_STR);
if(substr($unformattedSearchValue, 0, 1) === '@') {
$parts = explode('-', $unformattedSearchValue);
$selectedId = intval($parts[2]);
$qb->setParameter($searchValuePlaceHolder, $selectedId, IQueryBuilder::PARAM_INT);
} else {
$qb->setParameter($searchValuePlaceHolder, $unformattedSearchValue, IQueryBuilder::PARAM_STR);
}
}
}
140 changes: 79 additions & 61 deletions src/modules/main/partials/editViewPartials/filter/FilterEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,20 @@
v-model="selectedColumn"
class="select-field"
:options="columns"
:get-option-key="(option) => option.id"
:placeholder="t('tables', 'Column')"
label="title" />
label="title"
:placeholder="t('tables', 'Column')" />
<NcSelect
v-if="selectedColumn"
v-model="selectedOperator"
class="select-field"
:options="operatorArray"
:get-option-key="(option) => option.id"
:placeholder="t('tables', 'Operator')"
label="label" />
:options="operators"
:placeholder="t('tables', 'Operator')" />
<NcSelect
v-if="selectedOperator && !selectedOperator.noSearchValue"
v-model="searchValue"
class="select-field"
:options="magicFieldsArray"
:placeholder="valuePlaceHolder"
:options="magicFields"
:placeholder="getValuePlaceholder"
@search="v => term = v" />
</div>
<NcButton
Expand All @@ -39,18 +36,17 @@

<script>
import { NcButton, NcSelect } from '@nextcloud/vue'
import { getFilterWithId } from '../../../../../shared/components/ncTable/mixins/filter.js'
import { getMagicFieldWithId } from '../../../../../shared/components/ncTable/mixins/magicFields.js'
import Delete from 'vue-material-design-icons/Delete.vue'
import { ColumnTypes } from '../../../../../shared/components/ncTable/mixins/columnHandler.js'
export default {
name: 'FilterEntry',
components: {
NcSelect,
NcButton,
Delete,
},
props: {
filterEntry: {
type: Object,
Expand All @@ -61,34 +57,96 @@ export default {
default: null,
},
},
data() {
return {
selectedColumn: null,
selectedOperator: null,
mutableFilterEntry: this.filterEntry,
searchValue: '',
term: '',
}
},
computed: {
operatorArray() {
mutableFilterEntry: {
get() {
return this.filterEntry
},
set(filterEntry) {
this.$emit('update:filter-entry', filterEntry)
},
},
searchValue: {
get() {
// if no value is set, just return ''
if (this.filterEntry?.value === null || this.filterEntry?.value === '') {
return ''
}
// if the value starts with @, we try to load the magic-value object
if (this.filterEntry?.value.substr(0, 1) === '@') {
return this.magicFields.find(item => item.id === this.filterEntry?.value || item.id === this.filterEntry?.value.substr(1))
}
return this.filterEntry.value
},
set(searchValue) {
if (typeof searchValue === 'object' && searchValue?.id) {
this.mutableFilterEntry.value = searchValue.id
} else if (typeof searchValue === 'string') {
this.mutableFilterEntry.value = searchValue
} else {
this.mutableFilterEntry.value = ''
}
},
},
selectedColumn: {
get() {
return this.columns.find(col => col.id === this.filterEntry.columnId)
},
set(column) {
if (this.operators.length >= 1) {
this.selectedOperator = this.operators[0]
} else {
this.selectedOperator = null
}
this.searchValue = null
this.mutableFilterEntry.columnId = column?.id ?? null
},
},
selectedOperator: {
get() {
return this.operators.find(item => this.filterEntry.operator === item.id)
},
set(operator) {
this.mutableFilterEntry.operator = operator?.id
},
},
operators() {
if (this.selectedColumn) {
return this.selectedColumn.getPossibleOperators()
} else {
return []
}
},
magicFieldsArray() {
if (this.selectedColumn) {
magicFields() {
if (this.selectedColumn && this.selectedColumn.type.substr(0, 9) !== 'selection') {
if (this.term) {
return [this.term, ...this.selectedColumn.getPossibleMagicFields()]
}
return this.selectedColumn.getPossibleMagicFields()
} else if (this.selectedColumn && this.selectedColumn.type.substr(0, 9) === 'selection') {
const options = []
this.selectedColumn.selectionOptions.forEach(item => {
options.push({
id: '@selection-id-' + item.id,
label: item.label,
})
})
return options
} else {
return []
}
},
valuePlaceHolder() {
getValuePlaceholder() {
if (this.selectedColumn.type === ColumnTypes.Datetime) {
return t('tables', 'JJJJ-MM-DD hh:mm')
} else if (this.selectedColumn.type === ColumnTypes.DatetimeDate) {
Expand All @@ -99,45 +157,6 @@ export default {
return t('tables', 'Search Value')
},
},
watch: {
selectedColumn() {
if (!this.selectedOperator || (this.selectedOperator && !this.operatorArray.includes(this.selectedOperator))) {
if (this.operatorArray.length === 1) {
this.selectedOperator = this.operatorArray[0]
} else {
this.selectedOperator = null
}
}
if (this.searchValue && typeof this.searchValue === 'object' && !this.magicFieldsArray.includes(this.searchValue)) {
this.searchValue = null
}
this.mutableFilterEntry.columnId = this.selectedColumn?.id
},
selectedOperator() {
this.mutableFilterEntry.operator = this.selectedOperator?.id
},
searchValue() {
if (this.searchValue) {
this.mutableFilterEntry.value = typeof this.searchValue === 'object' ? '@' + this.searchValue.id : this.searchValue
} else {
this.mutableFilterEntry.value = ''
}
},
},
mounted() {
this.reset()
},
methods: {
clearValue() {
this.searchValue = ''
},
reset() {
this.selectedColumn = this.columns.find(col => col.id === this.filterEntry.columnId)
this.selectedOperator = getFilterWithId(this.filterEntry.operator)
this.searchValue = this.filterEntry.value && this.filterEntry.value.startsWith('@') ? getMagicFieldWithId(this.filterEntry.value.substring(1)) : this.filterEntry.value
},
},
}
</script>
Expand All @@ -149,8 +168,7 @@ export default {
.select-field {
width: 30%;
/* flex: 1; */
padding: 8px;
padding: calc(var(--default-grid-baseline) * 2);
min-width: auto !important;
}
Expand Down
17 changes: 10 additions & 7 deletions src/modules/main/partials/editViewPartials/filter/FilterForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</div>
<div v-for="(filterGroup, i) in mutableFilters" :key="i">
<FilterGroup
:filter-group="filterGroup"
:filter-group.sync="mutableFilters[i]"
:view-filter-group="viewFilters ? viewFilters[i] ?? [] : null"
:generated-filter-group="generatedFilters ? generatedFilters[i] ?? [] : null"
:columns="columns"
Expand Down Expand Up @@ -60,15 +60,18 @@ export default {
default: null,
},
},
data() {
return {
mutableFilters: this.filters,
}
},
computed: {
hasFilter() {
return (this.mutableFilters !== null && this.mutableFilters.length > 0)
return this.mutableFilters?.length > 0
},
mutableFilters: {
get() {
return this.filters
},
set(filters) {
this.$emit('update:filters', filters)
},
},
},
Expand Down
22 changes: 11 additions & 11 deletions src/modules/main/partials/editViewPartials/filter/FilterGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
<div class="group-text">
{{ t('tables', '... that meet all of the following conditions') }}
</div>
<div v-for="(filter, index) in filterGroup"
:key="filter.columnId + filter.operator?.id+ filter.value + index">
<div v-for="(filter, index) in mutableFilterGroup"
:key="index">
<FilterEntry
:filter-entry="filter"
:filter-entry.sync="mutableFilterGroup[index]"
:columns="columns"
:class="{'locallyAdded': isLocallyAdded(filter)}"
@delete-filter="deleteFilter(index)" />
Expand Down Expand Up @@ -54,14 +54,14 @@ export default {
default: null,
},
},
data() {
return {
mutableFilterGroup: this.filterGroup,
}
},
watch: {
filterGroup() {
this.mutableFilterGroup = this.filterGroup
computed: {
mutableFilterGroup: {
get() {
return this.filterGroup
},
set(filterGroup) {
this.$emit('update:filter-group', filterGroup)
},
},
},
methods: {
Expand Down
12 changes: 10 additions & 2 deletions src/modules/modals/ViewSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<!--filtering-->
<NcAppSettingsSection v-if="columns != null && canManageTable(view)" id="filter" :title="t('tables', 'Filter')">
<FilterForm
:filters="mutableView.filter"
:filters.sync="mutableFilters"
:view-filters="viewSetting ? view.filter : null"
:generated-filters="viewSetting ? generatedView.filter : null"
:columns="allColumns" />
Expand Down Expand Up @@ -92,7 +92,7 @@ export default {
type: Object,
default: null,
},
// If a new view is created or an existing view is edit
// If a new view is created or an existing view is edited
createView: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -121,6 +121,14 @@ export default {
}
},
computed: {
mutableFilters: {
get() {
return this.mutableView.filter
},
set(filters) {
this.mutableView.filter = filters
},
},
saveText() {
if (this.createView) {
return t('tables', 'Create View')
Expand Down
2 changes: 1 addition & 1 deletion src/shared/components/ncTable/mixins/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const Filters = {
id: FilterIds.IsEqual,
label: t('tables', 'Is equal'),
shortLabel: '=',
goodFor: [ColumnTypes.TextLine, ColumnTypes.Number, ColumnTypes.SelectionCheck, ColumnTypes.TextLink, ColumnTypes.NumberStars, ColumnTypes.NumberProgress, ColumnTypes.DatetimeDate, ColumnTypes.DatetimeTime, ColumnTypes.Datetime],
goodFor: [ColumnTypes.TextLine, ColumnTypes.Number, ColumnTypes.SelectionCheck, ColumnTypes.TextLink, ColumnTypes.NumberStars, ColumnTypes.NumberProgress, ColumnTypes.DatetimeDate, ColumnTypes.DatetimeTime, ColumnTypes.Datetime, ColumnTypes.Selection],
incompatibleWith: [FilterIds.IsEmpty, FilterIds.IsEqual, FilterIds.BeginsWith, FilterIds.EndsWith, FilterIds.Contains, FilterIds.IsGreaterThan, FilterIds.IsGreaterThanOrEqual, FilterIds.IsLowerThan, FilterIds.IsLowerThanOrEqual],
}),
IsGreaterThan: new Filter({
Expand Down

0 comments on commit e803ba8

Please sign in to comment.