Add support for multiple record list filters
This commit is contained in:
committed by
Jože Fortun
parent
363afde876
commit
63f1647f7b
@@ -24,13 +24,12 @@
|
||||
class="py-2 d-print-none"
|
||||
fluid
|
||||
>
|
||||
<b-row
|
||||
no-gutters
|
||||
class="justify-content-between wrap-with-vertical-gutters"
|
||||
<div
|
||||
class="d-flex flex-wrap justify-content-between wrap-with-vertical-gutters"
|
||||
>
|
||||
<div class="text-nowrap flex-grow-1">
|
||||
<div class="text-nowrap">
|
||||
<div
|
||||
class="wrap-with-vertical-gutters"
|
||||
class="wrap-with-vertical-gutters flex-wrap"
|
||||
>
|
||||
<template v-if="recordListModule.canCreateRecord">
|
||||
<template v-if="inlineEditing">
|
||||
@@ -77,6 +76,25 @@
|
||||
@export="onExport"
|
||||
/>
|
||||
|
||||
<b-dropdown
|
||||
v-if="options.recordFilters.length"
|
||||
size="lg"
|
||||
variant="light"
|
||||
:text="$t('filter.filter')"
|
||||
>
|
||||
<template
|
||||
v-for="(dropdown, idx) in options.recordFilters"
|
||||
>
|
||||
<b-dropdown-item
|
||||
v-if="checkFilterIsValid(dropdown.roles)"
|
||||
:key="idx"
|
||||
@click="updateFilter(dropdown.value, dropdown.title)"
|
||||
>
|
||||
{{ dropdown.title }}
|
||||
</b-dropdown-item>
|
||||
</template>
|
||||
</b-dropdown>
|
||||
|
||||
<column-picker
|
||||
v-if="!options.hideConfigureFieldsButton"
|
||||
:module="recordListModule"
|
||||
@@ -88,20 +106,31 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="!options.hideSearch && !inlineEditing"
|
||||
class="flex-grow-1 w-25"
|
||||
class="w-25"
|
||||
>
|
||||
<c-input-search
|
||||
v-model.trim="query"
|
||||
:placeholder="$t('general.label.search')"
|
||||
/>
|
||||
</div>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="drillDownFilter"
|
||||
class="d-flex justify-content-end mt-1"
|
||||
class="d-flex justify-content-between mt-1"
|
||||
>
|
||||
<b-form-tags
|
||||
v-model="selectedFilters"
|
||||
tag-variant="outline-primary"
|
||||
tag-pills
|
||||
input-class="d-none"
|
||||
placeholder=""
|
||||
style="width: fit-content"
|
||||
class="my-2 border-0 p-0"
|
||||
@input="removeFilter"
|
||||
/>
|
||||
|
||||
<b-button
|
||||
v-if="drillDownFilter"
|
||||
variant="outline-light"
|
||||
size="sm"
|
||||
class="text-nowrap text-primary border-0"
|
||||
@@ -259,6 +288,7 @@
|
||||
:record-list-filter="recordListFilter"
|
||||
@filter="onFilter"
|
||||
/>
|
||||
|
||||
<b-button
|
||||
v-if="field.sortable"
|
||||
variant="link p-0 ml-1"
|
||||
@@ -801,6 +831,7 @@ export default {
|
||||
ctr: 0,
|
||||
items: [],
|
||||
showingDeletedRecords: false,
|
||||
selectedFilters: [],
|
||||
}
|
||||
},
|
||||
|
||||
@@ -966,6 +997,10 @@ export default {
|
||||
isReminderActionVisible () {
|
||||
return !this.options.hideRecordReminderButton
|
||||
},
|
||||
|
||||
authUserRoles () {
|
||||
return this.$auth.user.roles
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -1667,6 +1702,40 @@ export default {
|
||||
|
||||
return !expressions.value
|
||||
},
|
||||
|
||||
updateFilter (filter, title) {
|
||||
const lastFilterIdx = this.recordListFilter.length - 1
|
||||
filter = filter.map((filter) => ({ ...filter, title }))
|
||||
|
||||
if (this.recordListFilter.length) {
|
||||
this.recordListFilter[lastFilterIdx].groupCondition = 'AND'
|
||||
}
|
||||
|
||||
this.recordListFilter = this.recordListFilter.concat(filter)
|
||||
this.selectedFilters.push(title)
|
||||
this.refresh(true)
|
||||
},
|
||||
|
||||
removeFilter (currentFilters) {
|
||||
this.recordListFilter = this.recordListFilter.filter(({ title }) => currentFilters.includes(title))
|
||||
this.refresh(true)
|
||||
},
|
||||
|
||||
checkFilterIsValid (filterRoles) {
|
||||
if (!filterRoles.length) return true
|
||||
|
||||
let result = false
|
||||
|
||||
for (let i = 0; i < filterRoles.length; i++) {
|
||||
const role = filterRoles[i]
|
||||
if (this.authUserRoles.includes(role)) {
|
||||
result = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -246,6 +246,98 @@
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-form-group
|
||||
breakpoint="md"
|
||||
:label="$t('filter.recordFilter')"
|
||||
>
|
||||
<b-table-simple
|
||||
v-if="recordListModule && recordListModule.fields.length"
|
||||
borderless
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{{ $t('filter.role') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ $t('filter.title') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(filter, index) in options.recordFilters"
|
||||
:key="index"
|
||||
>
|
||||
<td>
|
||||
<vue-select
|
||||
v-model="filter.roles"
|
||||
:options="roleOptions"
|
||||
:reduce="role => role.roleID"
|
||||
:get-option-label="getRoleLabel"
|
||||
append-to-body
|
||||
:placeholder="$t('filter.searchRolePlaceholder')"
|
||||
multiple
|
||||
class="bg-white"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<b-input-group>
|
||||
<b-form-input
|
||||
v-model="filter.title"
|
||||
placeholder="Title"
|
||||
type="text"
|
||||
class="h-100"
|
||||
/>
|
||||
<b-input-group-append>
|
||||
<b-button
|
||||
variant="light"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<record-list-filter
|
||||
class="d-print-none"
|
||||
:target="`record-filter-${index}`"
|
||||
:namespace="namespace"
|
||||
:module="recordListModule"
|
||||
:selected-field="recordListModule.fields[0]"
|
||||
:record-list-filter="filter.value"
|
||||
@filter="(filter) => onFilter(filter, index)"
|
||||
/>
|
||||
</b-button>
|
||||
<b-button
|
||||
variant="light"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<c-input-confirm
|
||||
button-class="text-right"
|
||||
@confirmed="options.recordFilters.splice(index, 1)"
|
||||
/>
|
||||
</b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</b-table-simple>
|
||||
|
||||
<b-button
|
||||
variant="primary"
|
||||
class="d-flex align-items-center px-0 text-decoration-none"
|
||||
@click="addNewRecordListFilter"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'plus']"
|
||||
size="sm"
|
||||
class="mr-1"
|
||||
/>
|
||||
{{ $t('general.label.add') }}
|
||||
</b-button>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
@@ -610,9 +702,11 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { NoID } from '@cortezaproject/corteza-js'
|
||||
import { VueSelect } from 'vue-select'
|
||||
import base from './base'
|
||||
import AutomationTab from './Shared/AutomationTab'
|
||||
import FieldPicker from 'corteza-webapp-compose/src/components/Common/FieldPicker'
|
||||
import RecordListFilter from 'corteza-webapp-compose/src/components/Common/RecordListFilter'
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
const { CInputPresort, CInputCheckbox } = components
|
||||
|
||||
@@ -628,6 +722,8 @@ export default {
|
||||
FieldPicker,
|
||||
CInputPresort,
|
||||
CInputCheckbox,
|
||||
RecordListFilter,
|
||||
VueSelect,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
@@ -638,6 +734,7 @@ export default {
|
||||
on: this.$t('general:label.yes'),
|
||||
off: this.$t('general:label.no'),
|
||||
},
|
||||
roleOptions: [],
|
||||
}
|
||||
},
|
||||
|
||||
@@ -778,5 +875,38 @@ export default {
|
||||
this.options.editFields = this.options.editFields.filter(a => fields.some(b => a.name === b.name))
|
||||
},
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.fetchRoles()
|
||||
},
|
||||
|
||||
methods: {
|
||||
getRoleLabel ({ name }) {
|
||||
return name
|
||||
},
|
||||
|
||||
async fetchRoles () {
|
||||
this.$SystemAPI.roleList().then(({ set: roles = [] }) => {
|
||||
this.roleOptions = roles
|
||||
})
|
||||
},
|
||||
|
||||
onFilter (filter = [], index) {
|
||||
this.options.recordFilters[index].value = filter
|
||||
},
|
||||
|
||||
addNewRecordListFilter () {
|
||||
this.options.recordFilters.push({
|
||||
title: '',
|
||||
value: [],
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.w-fit {
|
||||
width: fit-content;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
|
||||
<div
|
||||
v-if="toolbarSet"
|
||||
class="overflow-hidden"
|
||||
>
|
||||
<slot
|
||||
name="toolbar"
|
||||
|
||||
@@ -58,6 +58,7 @@ interface Options {
|
||||
|
||||
bulkRecordEditEnabled: boolean;
|
||||
inlineRecordEditEnabled: boolean;
|
||||
recordFilters: Array<Record<string, any>>
|
||||
}
|
||||
|
||||
const defaults: Readonly<Options> = Object.freeze({
|
||||
@@ -107,6 +108,7 @@ const defaults: Readonly<Options> = Object.freeze({
|
||||
|
||||
bulkRecordEditEnabled: true,
|
||||
inlineRecordEditEnabled: false
|
||||
recordFilters: []
|
||||
})
|
||||
|
||||
export class PageBlockRecordList extends PageBlock {
|
||||
@@ -130,6 +132,10 @@ export class PageBlockRecordList extends PageBlock {
|
||||
this.options.fields = o.fields
|
||||
}
|
||||
|
||||
if (o.recordFilters) {
|
||||
this.options.recordFilters = o.recordFilters
|
||||
}
|
||||
|
||||
if (o.editFields) {
|
||||
this.options.editFields = o.editFields
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ interface OAuth2TokenResponse {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
expires_in: number;
|
||||
|
||||
|
||||
roles?: string;
|
||||
name?: string;
|
||||
handle?: string;
|
||||
email?: string;
|
||||
@@ -573,6 +574,7 @@ export class Auth {
|
||||
name: oa2tkn.name,
|
||||
handle: oa2tkn.handle,
|
||||
email: oa2tkn.email,
|
||||
roles: oa2tkn.roles ? [oa2tkn.roles] : [],
|
||||
})
|
||||
|
||||
if (oa2tkn.preferred_language) {
|
||||
|
||||
@@ -758,3 +758,10 @@ tabs:
|
||||
addBlock: Add new block
|
||||
delete: Delete tab
|
||||
label: Tabs
|
||||
|
||||
filter:
|
||||
recordFilter: Record filter
|
||||
role: Role
|
||||
filter: Filter
|
||||
title: Title
|
||||
searchRolePlaceholder: Select role to apply filter to
|
||||
|
||||
Reference in New Issue
Block a user