Skip to content

Commit b6e0bc3

Browse files
committed
wip: Enhance collection filtering #1151
1 parent 3cf4683 commit b6e0bc3

File tree

4 files changed

+127
-15
lines changed

4 files changed

+127
-15
lines changed

core/client/components/collection/KTagsFilterControl.vue

+9-9
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
color="grey-9"
88
flat
99
direction="down"
10-
:vertical-actions-align="responsiveAlignment"
10+
:vertical-actions-align="computedAlignment"
1111
padding="xs"
12-
@show="enableTooltip = false"
13-
@hide="enableTooltip = true"
12+
@show="showTooltip = false"
13+
@hide="showTooltip = true"
1414
>
1515
<template v-for="tag in options" :key="tag.name">
1616
<q-fab-action
@@ -23,7 +23,7 @@
2323
/>
2424
</template>
2525
</q-fab>
26-
<q-tooltip v-if="enableTooltip">
26+
<q-tooltip v-if="showTooltip">
2727
{{ $t('KTagsFilterControl.FILTER') }}
2828
</q-tooltip>
2929
</div>
@@ -46,25 +46,25 @@ const props = defineProps({
4646
const { Screen } = useScreen()
4747
const { CurrentActivityContext } = useCurrentActivity()
4848
const tagsFilter = CurrentActivityContext.state.tagsFilter
49-
const enableTooltip = ref(true)
49+
const showTooltip = ref(true)
5050
5151
// Computed
5252
const selection = computed(() => {
53-
return tagsFilter.selection
53+
return _.get(tagsFilter, 'selection', [])
5454
})
5555
const options = computed(() => {
56-
return _.difference(tagsFilter.options, tagsFilter.selection)
56+
return _.difference(_.get(tagsFilter, 'options'), selection.value)
5757
})
5858
const hasOptions = computed(() => {
5959
return !_.isEmpty(options.value)
6060
})
61-
const responsiveAlignment = computed(() => {
61+
const computedAlignment = computed(() => {
6262
if (_.isString(props.alignment)) return props.alignment
6363
return _.get(props.alignment, Screen.name)
6464
})
6565
6666
// Function
6767
function onTagAdded (tag) {
68-
Object.assign(CurrentActivityContext.state.tagsFilter, { selection: _.concat(selection.value, [tag]) })
68+
_.set(tagsFilter, 'selection', _.concat(selection.value, [tag]))
6969
}
7070
</script>

core/client/components/collection/KTimeFilterControl.vue

+6-6
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,18 @@ defineProps({
2222
2323
// Data
2424
const { CurrentActivityContext } = useCurrentActivity()
25-
const { state } = CurrentActivityContext
25+
const timeFilter = CurrentActivityContext.state.timeFilter
2626
2727
// Computed
28+
const min = computed(() => _.get(timeFilter, 'min'))
29+
const max = computed(() => _.get(timeFilter, 'max'))
2830
const hasTimeRange = computed(() => {
29-
return state.timeFilter && state.timeFilter.min !== state.timeFilter.max
31+
return !_.isEmpty(min.value) && !_.isEmpty(max.value) && min.value !== max.value
3032
})
3133
3234
// Functions
3335
async function showTimeRangeSlider () {
34-
Object.assign(state.timeFilter, {
35-
start: state.timeFilter.min,
36-
end: state.timeFilter.max
37-
})
36+
_.set(timeFilter, 'start', min.value)
37+
_.set(timeFilter, 'end', max.value)
3838
}
3939
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import _ from 'lodash'
2+
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
3+
import { getCollectionService, getLatestTime, getOldestTime, listenToServiceEvents, unlistenToServiceEvents } from '../utils/index.js'
4+
import { useCurrentActivity } from './activity.js'
5+
6+
export function useCollectionFilter (options = {}) {
7+
// Data
8+
const { CurrentActivityContext } = useCurrentActivity()
9+
const tagsFilter = CurrentActivityContext.state.tagsFilter
10+
const timeFilter = CurrentActivityContext.state.timeFilter
11+
const filterQuery = ref({})
12+
let listeners
13+
14+
// Computed
15+
const selectedTags = computed(() => {
16+
return _.get(tagsFilter, 'selection', [])
17+
})
18+
const hasTagsSelection = computed(() => {
19+
return !_.isEmpty(selectedTags.value)
20+
})
21+
const selectedTime = computed(() => {
22+
return _.cloneDeep(timeFilter)
23+
})
24+
const hasTimeSelection = computed(() => {
25+
const start = _.get(timeFilter, 'start')
26+
const end = _.get(timeFilter, 'end')
27+
return start && end && start !== end
28+
})
29+
30+
// Watch
31+
watch(selectedTags, () => refreshFilterQuery())
32+
watch(selectedTime, (newTimeRange, oldTimeRange) => {
33+
if (oldTimeRange && oldTimeRange.start !== null && oldTimeRange.end !== null) refreshFilterQuery()
34+
}, { deep: true })
35+
36+
// Functions
37+
function getService () {
38+
return getCollectionService(options.service.value, options.context ? options.context.value : null)
39+
}
40+
function getBaseQuery () {
41+
return _.get(options, 'baseQuery.value', {})
42+
}
43+
function getTimeField () {
44+
return _.get(options, 'timeField.value', 'createdAt')
45+
}
46+
function getTagFields () {
47+
return _.get(options, 'tagFields', [])
48+
}
49+
async function refreshOptions () {
50+
const timeField = getTimeField()
51+
// update time filter
52+
const min = await getOldestTime(getService(), timeField, getBaseQuery())
53+
if (min) _.set(timeFilter, 'min', min)
54+
const max = await getLatestTime(getService(), timeField, getBaseQuery())
55+
if (max) _.set(timeFilter, 'max', max)
56+
// update tags filter
57+
const tagFields = getTagFields()
58+
let tagsOptions = []
59+
_.forEach(tagFields, (values, property) => {
60+
const propertyOptions = _.map(values, (state, key) => {
61+
return _.merge({ scope: property, name: key }, state)
62+
})
63+
if (_.size(propertyOptions) > 1) tagsOptions = _.concat(tagsOptions, propertyOptions)
64+
})
65+
_.set(tagsFilter, 'options', tagsOptions)
66+
}
67+
function refreshFilterQuery () {
68+
const query = {}
69+
// filter against the selected tags
70+
if (hasTagsSelection.value) {
71+
const tagFields = getTagFields()
72+
_.forEach(tagFields, (values, property) => {
73+
const tagsProperty = _.map(_.filter(selectedTags.value, { scope: property }), tag => { return tag.name })
74+
if (!_.isEmpty(tagsProperty)) {
75+
_.merge(query, { [property]: { $in: tagsProperty } })
76+
}
77+
})
78+
}
79+
// filter against the selected time range
80+
if (hasTimeSelection.value) {
81+
_.merge(query, {
82+
[getTimeField()]: {
83+
$gte: selectedTime.value.start,
84+
$lte: selectedTime.value.end
85+
}
86+
})
87+
}
88+
filterQuery.value = query
89+
}
90+
91+
// Hooks
92+
onMounted(async () => {
93+
await refreshOptions()
94+
listeners = listenToServiceEvents(getService(), {
95+
created: () => refreshOptions('created'),
96+
updated: () => refreshOptions('updated'),
97+
patched: () => refreshOptions('patched'),
98+
removed: () => refreshOptions('removed')
99+
})
100+
})
101+
onBeforeUnmount(() => {
102+
unlistenToServiceEvents(listeners)
103+
})
104+
105+
// Expose
106+
return {
107+
filterQuery,
108+
hasTagsSelection,
109+
hasTimeSelection
110+
}
111+
}

core/client/composables/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './activity.js'
22
export * from './collection-counter.js'
3+
export * from './collection-filter.js'
34
export * from './collection-timerange.js'
45
export * from './collection.js'
56
export * from './context.js'

0 commit comments

Comments
 (0)