Skip to content

Commit 8b4f8b2

Browse files
committed
MT#62260 Add Phonebook search bar
* Add a search field to search for contacts by Name, Phone number or the property Shared. Note, the name and phone search use the wild card before and after the typed string. * Move call button outside the 3 dots menus for easier access. Change-Id: I2c135d744a66c921a028dcece4a889b482ccd33f
1 parent 0340bd7 commit 8b4f8b2

File tree

2 files changed

+266
-19
lines changed

2 files changed

+266
-19
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<template>
2+
<div>
3+
<div
4+
class="row justify-center full-width q-gutter-x-sm"
5+
>
6+
<div
7+
class="col-xs-12 col-md-2"
8+
>
9+
<q-select
10+
v-model="filterType"
11+
emit-value
12+
map-options
13+
dense
14+
:options="filterTypeOptions"
15+
:label="$t('Filter by')"
16+
/>
17+
</div>
18+
<div
19+
class="col-xs-12 col-md-2"
20+
>
21+
<q-select
22+
v-if="filterType === 'shared'"
23+
v-model="typedFilter"
24+
:options="sharedFilterOptions"
25+
dense
26+
:disable="filterType === null"
27+
:label="$t('Is shared?')"
28+
@update:model-value="triggerFilter"
29+
/>
30+
<q-input
31+
v-else
32+
v-model="typedFilter"
33+
type="text"
34+
dense
35+
:disable="filterType === null"
36+
:label="$t('Type something')"
37+
@keypress.enter="triggerFilter"
38+
>
39+
<template
40+
#append
41+
>
42+
<q-btn
43+
icon="search"
44+
color="primary"
45+
dense
46+
flat
47+
@click="triggerFilter"
48+
/>
49+
</template>
50+
</q-input>
51+
</div>
52+
</div>
53+
<div
54+
class="row justify-center full-width q-gutter-x-sm"
55+
>
56+
<div
57+
class="col-xs-12 col-md-4"
58+
>
59+
<q-chip
60+
v-for="(filterItem, index) in filters"
61+
:key="index"
62+
:label="getFilterLabel(filterItem)"
63+
:disable="false"
64+
icon="filter_alt"
65+
removable
66+
dense
67+
color="primary"
68+
text-color="dark"
69+
@remove="removeFilter(filterItem.name)"
70+
/>
71+
</div>
72+
</div>
73+
</div>
74+
</template>
75+
76+
<script>
77+
export default {
78+
name: 'CscSubscriberFilters',
79+
emits: ['filter'],
80+
data () {
81+
return {
82+
filterType: null,
83+
typedFilter: '',
84+
filters: []
85+
}
86+
},
87+
computed: {
88+
filterTypeOptions () {
89+
return [
90+
{
91+
label: this.$t('Name'),
92+
value: 'name'
93+
},
94+
{
95+
label: this.$t('Number'),
96+
value: 'number'
97+
},
98+
{
99+
label: this.$t('Shared'),
100+
value: 'shared'
101+
}
102+
]
103+
},
104+
sharedFilterOptions () {
105+
return [
106+
{ label: this.$t('Yes'), value: '1' },
107+
{ label: this.$t('No'), value: '0' }
108+
]
109+
}
110+
},
111+
methods: {
112+
triggerFilter () {
113+
this.addFilter(this.filterType, this.typedFilter)
114+
},
115+
removeFilter (name) {
116+
this.filters = this.filters.filter((item) => item.name !== name)
117+
this.filter()
118+
},
119+
removeFilters () {
120+
if (this.filters.length > 0) {
121+
this.filters = []
122+
this.filter()
123+
}
124+
},
125+
addFilter (name, value) {
126+
const valueTrimmed = value?.value || value.trim()
127+
if (valueTrimmed) {
128+
this.typedFilter = ''
129+
this.filters = this.filters.filter((item) => item.name !== name)
130+
const filter = {
131+
name,
132+
value: valueTrimmed
133+
}
134+
this.filters.push(filter)
135+
this.filter()
136+
}
137+
},
138+
filter () {
139+
const params = {}
140+
this.filters.forEach((filter) => {
141+
params[filter.name] = filter.value
142+
})
143+
this.$emit('filter', params)
144+
},
145+
getFilterLabel (filterItem) {
146+
const filterNameTranslation = {
147+
name: this.$t('Name'),
148+
number: this.$t('Number'),
149+
shared: this.$t('Shared')
150+
}
151+
const filterNameTitle = filterNameTranslation[filterItem.name] || this.$t('Unknown name')
152+
153+
if (filterItem.name === 'shared') {
154+
const yes = this.$t('Yes')
155+
const no = this.$t('No')
156+
return `${filterNameTitle}: ${filterItem.value === '1' ? yes : no}`
157+
}
158+
return `${filterNameTitle}: ${filterItem.value}`
159+
}
160+
}
161+
}
162+
</script>

src/pages/CscPageSubscriberPhonebook.vue

Lines changed: 104 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,38 @@
2626
<csc-page
2727
class="q-pa-lg"
2828
>
29+
<csc-list-actions
30+
class="row justify-center q-mb-xs"
31+
>
32+
<template
33+
#slot1
34+
>
35+
<csc-list-action-button
36+
v-if="!showFilters"
37+
icon="filter_alt"
38+
color="primary"
39+
:label="$t('Search Contact')"
40+
data-cy="groups-filter-open"
41+
@click="openSearchFilters"
42+
/>
43+
<csc-list-action-button
44+
v-if="showFilters"
45+
icon="clear"
46+
color="negative"
47+
:label="$t('Close')"
48+
data-cy="groups-filter-close"
49+
@click="closeFilters"
50+
/>
51+
</template>
52+
</csc-list-actions>
53+
54+
<csc-subscriber-filters
55+
v-if="showFilters"
56+
ref="filters"
57+
class="q-mb-md q-pa-md"
58+
@filter="applyFilter"
59+
/>
60+
2961
<q-table
3062
v-model:pagination="pagination"
3163
class="no-shadow"
@@ -65,28 +97,32 @@
6597
</template>
6698
<template #body-cell-menu="{ row }">
6799
<td>
68-
<csc-more-menu>
69-
<csc-popup-menu-item
100+
<div class="q-gutter-x-sm">
101+
<csc-more-menu>
102+
<csc-popup-menu-item
103+
icon="fas fa-pen"
104+
color="primary"
105+
:label="$t('Edit')"
106+
:disable="isLevelEntry(row.id)"
107+
@click="showPhonebookDetails(row)"
108+
/>
109+
<csc-popup-menu-item
110+
icon="delete"
111+
color="negative"
112+
:label="$t('Delete')"
113+
:disable="isLevelEntry(row.id)"
114+
@click="deleteRow(row)"
115+
/>
116+
</csc-more-menu>
117+
<q-btn
70118
icon="fas fa-phone-alt"
71119
color="primary"
120+
size="sm"
121+
flat
72122
:label="$t('Call back')"
73123
@click="homePageCall(row)"
74124
/>
75-
<csc-popup-menu-item
76-
icon="fas fa-pen"
77-
color="primary"
78-
:label="$t('Edit')"
79-
:disable="isLevelEntry(row.id)"
80-
@click="showPhonebookDetails(row)"
81-
/>
82-
<csc-popup-menu-item
83-
icon="delete"
84-
color="negative"
85-
:label="$t('Delete')"
86-
:disable="isLevelEntry(row.id)"
87-
@click="deleteRow(row)"
88-
/>
89-
</csc-more-menu>
125+
</div>
90126
</td>
91127
</template>
92128
</q-table>
@@ -95,11 +131,14 @@
95131
</template>
96132

97133
<script>
134+
import CscListActionButton from 'components/CscListActionButton'
135+
import CscListActions from 'components/CscListActions'
98136
import CscMoreMenu from 'components/CscMoreMenu'
99137
import CscPage from 'components/CscPage'
100138
import CscPageSticky from 'components/CscPageSticky'
101139
import CscPopupMenuItem from 'components/CscPopupMenuItem'
102140
import CscSpinner from 'components/CscSpinner'
141+
import CscSubscriberFilters from 'components/pages/SubscriberPhonebook/CscSubscriberFilters'
103142
import { LIST_DEFAULT_ROWS } from 'src/api/common'
104143
import { mapWaitingActions } from 'vue-wait'
105144
import { mapGetters, mapState } from 'vuex'
@@ -110,7 +149,10 @@ export default {
110149
CscPage,
111150
CscMoreMenu,
112151
CscPopupMenuItem,
113-
CscPageSticky
152+
CscPageSticky,
153+
CscListActionButton,
154+
CscListActions,
155+
CscSubscriberFilters
114156
},
115157
data () {
116158
return {
@@ -121,7 +163,9 @@ export default {
121163
page: 1,
122164
rowsPerPage: LIST_DEFAULT_ROWS,
123165
rowsNumber: 0
124-
}
166+
},
167+
filters: {},
168+
showFilters: false
125169
}
126170
},
127171
computed: {
@@ -166,6 +210,9 @@ export default {
166210
sortable: true
167211
}
168212
]
213+
},
214+
hasFilters () {
215+
return Object.keys(this.filters).length > 0
169216
}
170217
},
171218
async mounted () {
@@ -233,6 +280,44 @@ export default {
233280
},
234281
openSeatTable () {
235282
this.$router.push('/user/seats')
283+
},
284+
applyFilter (filters) {
285+
this.filters = filters
286+
// Add wildcards to make search more extensive
287+
if (filters?.name) {
288+
this.filters.name = `*${filters.name}*`
289+
}
290+
if (filters?.number) {
291+
this.filters.number = `*${filters.number}*`
292+
}
293+
294+
this.pagination.page = 1 // Reset to first page on filter change
295+
296+
this.$scrollTo(this.$parent.$el)
297+
const payload = this.filters
298+
payload.page = 1
299+
payload.subscriber_id = this.getSubscriberId
300+
301+
this.loadSubscriberPhonebook(payload)
302+
},
303+
closeFilters () {
304+
this.showFilters = false
305+
this.resetFilters()
306+
},
307+
openSearchFilters () {
308+
this.showFilters = true
309+
},
310+
resetFilters () {
311+
if (this.hasFilters) {
312+
this.filters = {}
313+
this.loadSubscriberPhonebook({
314+
page: this.pagination.page,
315+
rows: this.pagination.rowsPerPage,
316+
order_by: this.pagination.sortBy,
317+
order_by_direction: this.pagination.descending ? 'desc' : 'asc',
318+
subscriber_id: this.getSubscriberId
319+
})
320+
}
236321
}
237322
}
238323
}

0 commit comments

Comments
 (0)