Change-Id: I15e9d100233408fbfe82cbb916c7d2fb76a311f8mr12.0
parent
8e7c0ad2b2
commit
177f01d535
@ -0,0 +1,313 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="row justify-center full-width q-gutter-x-sm"
|
||||
>
|
||||
<div
|
||||
class="col-xs-12 col-md-3"
|
||||
>
|
||||
<q-select
|
||||
v-model="filterTypeModel"
|
||||
dense
|
||||
:options="filterTypeOptions"
|
||||
:label="$t('Filter by')"
|
||||
data-cy="csc-cdr-filter"
|
||||
:disable="loading"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="filterType === 'timerange'"
|
||||
class="row col-xs-12 col-md-6"
|
||||
>
|
||||
<q-input
|
||||
v-model="dateStartFilter"
|
||||
class="q-pr-sm col-6"
|
||||
dense
|
||||
:disable="loading || filterType === null"
|
||||
:label="$t('From')"
|
||||
data-cy="csc-cdr-filter-from"
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon
|
||||
name="event"
|
||||
class="cursor-pointer"
|
||||
@click="loadFormattedDateStart()"
|
||||
>
|
||||
<q-popup-proxy
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
@hide="addFilter('startTime', dateStartFilter)"
|
||||
>
|
||||
<q-date
|
||||
v-model="dateStartFilter"
|
||||
mask="YYYY-MM-DD"
|
||||
format24h
|
||||
>
|
||||
<div class="row items-center justify-end">
|
||||
<q-btn
|
||||
v-close-popup
|
||||
:label="$t('Close')"
|
||||
color="primary"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
</q-date>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
<q-input
|
||||
v-model="dateEndFilter"
|
||||
class="col-6"
|
||||
dense
|
||||
:disable="loading || filterType === null"
|
||||
:label="$t('To')"
|
||||
data-cy="csc-cdr-filter-to"
|
||||
@input="triggerFilter"
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon
|
||||
name="event"
|
||||
class="cursor-pointer"
|
||||
@click="loadFormattedDateEnd()"
|
||||
>
|
||||
<q-popup-proxy
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
@hide="addFilter('endTime', dateEndFilter)"
|
||||
>
|
||||
<q-date
|
||||
v-model="dateEndFilter"
|
||||
mask="YYYY-MM-DD"
|
||||
>
|
||||
<div class="row items-center justify-end">
|
||||
<q-btn
|
||||
v-close-popup
|
||||
:label="$t('Close')"
|
||||
color="primary"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
</q-date>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="false"
|
||||
class="row col-xs-12 col-md-3"
|
||||
>
|
||||
<q-select
|
||||
v-model="filterDirection"
|
||||
class="full-width"
|
||||
dense
|
||||
:options="filterDirectionOptions"
|
||||
:label="$t('In/Out')"
|
||||
data-cy="csc-cdr-filter"
|
||||
:disable="loading"
|
||||
@update:model-value="triggerDirectionFilter"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="filterType === 'type'"
|
||||
class="row col-xs-12 col-md-3"
|
||||
>
|
||||
<q-select
|
||||
v-model="filterTypeField"
|
||||
class="full-width"
|
||||
dense
|
||||
:options="filterTypeFieldOptions"
|
||||
:label="$t('Type')"
|
||||
data-cy="csc-cdr-filter"
|
||||
:disable="loading"
|
||||
@update:model-value="triggerTypeFieldFilter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="row justify-center full-width q-gutter-x-sm"
|
||||
>
|
||||
<div
|
||||
class="col-xs-12 col-md-4"
|
||||
>
|
||||
<q-chip
|
||||
v-for="({ filterInfo, id }) in filtersList"
|
||||
:key="id"
|
||||
:label="filterInfo"
|
||||
:disable="false"
|
||||
icon="filter_alt"
|
||||
removable
|
||||
dense
|
||||
color="primary"
|
||||
text-color="dark"
|
||||
@remove="removeFilter(id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'CscCdrFilters',
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['filter'],
|
||||
data () {
|
||||
return {
|
||||
filterTypeModel: null,
|
||||
filterDirection: null,
|
||||
filterTypeField: null,
|
||||
dateStartFilter: null,
|
||||
dateEndFilter: null,
|
||||
filters: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'getCurrentFormattedDateWithDash'
|
||||
]),
|
||||
filterType () {
|
||||
return this.filterTypeModel && this.filterTypeModel.value
|
||||
},
|
||||
filterTypeOptions () {
|
||||
return [
|
||||
{
|
||||
label: this.$t('Timerange'),
|
||||
value: 'timerange'
|
||||
},
|
||||
/* {
|
||||
label: this.$t('Direction'),
|
||||
value: 'direction'
|
||||
}, */
|
||||
{
|
||||
label: this.$t('Type'),
|
||||
value: 'type'
|
||||
}
|
||||
]
|
||||
},
|
||||
filterDirectionOptions () {
|
||||
return [
|
||||
{
|
||||
label: this.$t('In'),
|
||||
value: 'in'
|
||||
},
|
||||
{
|
||||
label: this.$t('Out'),
|
||||
value: 'out'
|
||||
}
|
||||
]
|
||||
},
|
||||
filterTypeFieldOptions () {
|
||||
return [
|
||||
{
|
||||
label: this.$t('Call'),
|
||||
value: 'call'
|
||||
},
|
||||
{
|
||||
label: this.$t('Voicemail'),
|
||||
value: 'voicemail'
|
||||
},
|
||||
{
|
||||
label: this.$t('Sms'),
|
||||
value: 'sms'
|
||||
},
|
||||
{
|
||||
label: this.$t('Fax'),
|
||||
value: 'fax'
|
||||
}
|
||||
]
|
||||
},
|
||||
filtersList () {
|
||||
return this.filters.map((filterItem) => {
|
||||
const filterDisplayValue = filterItem.value
|
||||
let filterName
|
||||
switch (filterItem.name) {
|
||||
case 'startTime':
|
||||
filterName = this.$t('Start time')
|
||||
break
|
||||
case 'endTime' :
|
||||
filterName = this.$t('End time')
|
||||
break
|
||||
default:
|
||||
filterName = this.filterTypeOptions.find(option => option.value === filterItem.name).label
|
||||
}
|
||||
return {
|
||||
id: filterItem.name,
|
||||
filterInfo: filterName + ': ' + filterDisplayValue
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
filterTypeModel () {
|
||||
this.resetFilters()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
triggerDirectionFilter () {
|
||||
this.addFilter(this.filterTypeModel?.value, this.filterDirection.value)
|
||||
},
|
||||
triggerTypeFieldFilter () {
|
||||
this.addFilter(this.filterTypeModel?.value, this.filterTypeField.value)
|
||||
},
|
||||
removeFilter (name) {
|
||||
this.filters = this.filters.filter(item => item.name !== name)
|
||||
this.filter()
|
||||
},
|
||||
removeFilters () {
|
||||
if (this.filters.length > 0) {
|
||||
this.filters = []
|
||||
this.filter()
|
||||
}
|
||||
},
|
||||
addFilter (name, value) {
|
||||
const valueTrimmed = _.trim(value)
|
||||
if (valueTrimmed) {
|
||||
this.resetFilters()
|
||||
this.filters = this.filters.filter(item => item.name !== name)
|
||||
const filter = {
|
||||
name: name,
|
||||
value: valueTrimmed
|
||||
}
|
||||
this.filters.push(filter)
|
||||
this.filter()
|
||||
}
|
||||
},
|
||||
filter () {
|
||||
const params = {}
|
||||
this.filters.forEach(filter => {
|
||||
params[filter.name] = filter.value
|
||||
})
|
||||
this.$emit('filter', params)
|
||||
},
|
||||
resetFilters () {
|
||||
this.typedFilter = null
|
||||
this.dateStartFilter = null
|
||||
this.dateEndFilter = null
|
||||
this.filterDirection = null
|
||||
this.filterTypeField = null
|
||||
},
|
||||
loadFormattedDateStart () {
|
||||
const currentDate = this.getCurrentFormattedDateWithDash
|
||||
if (!this.dateStartFilter) {
|
||||
this.dateStartFilter = currentDate
|
||||
}
|
||||
},
|
||||
loadFormattedDateEnd () {
|
||||
const currentDate = this.getCurrentFormattedDateWithDash
|
||||
if (!this.dateEndFilter) {
|
||||
this.dateEndFilter = currentDate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,221 @@
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
|
||||
<template>
|
||||
<csc-page-sticky
|
||||
id="csc-page-pbx-statistics-cdr"
|
||||
>
|
||||
<template
|
||||
#header
|
||||
>
|
||||
<q-btn
|
||||
v-if="!showFilters"
|
||||
icon="filter_alt"
|
||||
color="primary"
|
||||
flat
|
||||
:label="$t('Filter')"
|
||||
@click="openFilters"
|
||||
/>
|
||||
<q-btn
|
||||
v-if="showFilters"
|
||||
icon="clear"
|
||||
color="negative"
|
||||
flat
|
||||
:label="$t('Close filters')"
|
||||
@click="closeFilters"
|
||||
/>
|
||||
</template>
|
||||
<template
|
||||
#toolbar
|
||||
>
|
||||
<csc-cdr-filters
|
||||
v-if="showFilters"
|
||||
ref="filters"
|
||||
:loading="$wait.is('loadConversations')"
|
||||
class="q-mb-md q-pa-md"
|
||||
@filter="filterEvent"
|
||||
/>
|
||||
</template>
|
||||
<div>
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
v-model:pagination="pagination"
|
||||
class="no-shadow"
|
||||
:columns="columns"
|
||||
:rows="conversations"
|
||||
:loading="$wait.is('loadConversations')"
|
||||
row-key="id"
|
||||
@request="fetchPaginatedConversations"
|
||||
>
|
||||
<template #loading>
|
||||
<q-inner-loading
|
||||
showing
|
||||
color="primary"
|
||||
>
|
||||
<csc-spinner />
|
||||
</q-inner-loading>
|
||||
</template>
|
||||
|
||||
<template #top-left>
|
||||
<q-btn
|
||||
icon="refresh"
|
||||
size="sm"
|
||||
flat
|
||||
@click="refresh"
|
||||
>
|
||||
{{ $t('Refresh') }}
|
||||
</q-btn>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</div>
|
||||
</csc-page-sticky>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import CscPageSticky from 'components/CscPageSticky'
|
||||
import { mapWaitingActions } from 'vue-wait'
|
||||
import CscSpinner from 'components/CscSpinner'
|
||||
import { LIST_DEFAULT_ROWS } from 'src/api/common'
|
||||
import CscCdrFilters from 'components/pages/PbxStatistics/CscCdrFilters'
|
||||
|
||||
import _ from 'lodash'
|
||||
export default {
|
||||
name: 'CscPagePbxStatisticsCdr',
|
||||
components: {
|
||||
CscSpinner,
|
||||
CscPageSticky,
|
||||
CscCdrFilters
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
data: [],
|
||||
pagination: {
|
||||
sortBy: 'timestamp',
|
||||
descending: true,
|
||||
page: 1,
|
||||
rowsPerPage: LIST_DEFAULT_ROWS,
|
||||
rowsNumber: 0
|
||||
},
|
||||
showFilters: false,
|
||||
filter: {
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
caller: null,
|
||||
callee: null
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('conversations', [
|
||||
'conversations'
|
||||
]),
|
||||
columns () {
|
||||
return [
|
||||
{
|
||||
name: 'start_time',
|
||||
required: true,
|
||||
label: this.$t('Start time'),
|
||||
align: 'left',
|
||||
field: row => this.$filters.smartTime(row.start_time),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
required: true,
|
||||
align: 'left',
|
||||
label: this.$t('Type'),
|
||||
field: row => _.capitalize(row.type),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'caller',
|
||||
required: true,
|
||||
align: 'left',
|
||||
label: this.$t('Caller'),
|
||||
field: row => row.caller,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'callee',
|
||||
required: true,
|
||||
align: 'left',
|
||||
label: this.$t('Callee'),
|
||||
field: row => row.callee,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'direction',
|
||||
required: true,
|
||||
align: 'left',
|
||||
label: this.$t('Direction'),
|
||||
field: row => row.direction,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
required: true,
|
||||
align: 'left',
|
||||
label: this.$t('Duration'),
|
||||
field: row => row.duration,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
required: true,
|
||||
align: 'left',
|
||||
label: this.$t('Status'),
|
||||
field: row => row.status,
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
await this.refresh()
|
||||
},
|
||||
methods: {
|
||||
...mapWaitingActions('conversations', {
|
||||
loadConversations: 'loadConversations'
|
||||
}),
|
||||
async refresh () {
|
||||
await this.fetchPaginatedConversations({
|
||||
pagination: this.pagination
|
||||
})
|
||||
},
|
||||
async fetchPaginatedConversations (props) {
|
||||
const { page, rowsPerPage, sortBy, descending } = props.pagination
|
||||
const { startTime, endTime, direction, type } = this.filter
|
||||
const count = await this.loadConversations({
|
||||
page,
|
||||
rows: rowsPerPage,
|
||||
order_by: sortBy,
|
||||
order_by_direction: descending ? 'desc' : 'asc',
|
||||
from: startTime,
|
||||
to: endTime,
|
||||
direction: direction,
|
||||
type: type
|
||||
})
|
||||
this.pagination = { ...props.pagination }
|
||||
this.pagination.rowsNumber = count
|
||||
},
|
||||
openFilters () {
|
||||
this.showFilters = true
|
||||
},
|
||||
closeFilters () {
|
||||
if (this.$refs.filters) {
|
||||
this.$refs.filters.removeFilters()
|
||||
}
|
||||
this.showFilters = false
|
||||
},
|
||||
filterEvent (filter) {
|
||||
this.$scrollTo(this.$parent.$el)
|
||||
console.log(filter)
|
||||
this.filter = filter
|
||||
this.fetchPaginatedConversations({
|
||||
pagination: this.pagination
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Reference in new issue