Update according to UI values, id-name pairs
This commit is contained in:
parent
01f1d5216f
commit
810ebd7866
7 changed files with 580 additions and 7 deletions
|
@ -51,7 +51,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||
def show; end
|
||||
|
||||
def filter
|
||||
@contacts = ::Contacts::FilterService.new(params[:body], current_user).perform
|
||||
@contacts = ::Contacts::FilterService.new(params[:payload], current_user).perform
|
||||
end
|
||||
|
||||
def contactable_inboxes
|
||||
|
|
|
@ -32,15 +32,16 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
|||
def show; end
|
||||
|
||||
def filter
|
||||
@conversations = ::Conversations::FilterService.new(JSON.parse(params[:body]), current_user).perform
|
||||
@conversations_count = @conversations.count
|
||||
result = ::Conversations::FilterService.new(params.permit![:payload], current_user).perform
|
||||
@conversations = result[:conversations]
|
||||
@conversations_count = result[:count]
|
||||
end
|
||||
|
||||
def mute
|
||||
@conversation.mute!
|
||||
head :ok
|
||||
end
|
||||
F
|
||||
|
||||
def unmute
|
||||
@conversation.unmute!
|
||||
head :ok
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
<template>
|
||||
<div class="column">
|
||||
<woot-modal-header header-title="Filter Conversations">
|
||||
<p>
|
||||
{{ $t('FILTER.SUBTITLE') }}
|
||||
</p>
|
||||
</woot-modal-header>
|
||||
<div class="row modal-content">
|
||||
<div class="medium-12 columns filter-modal-content">
|
||||
<filter-input-box
|
||||
v-for="(filter, i) in appliedFilters"
|
||||
:key="i"
|
||||
v-model="appliedFilters[i]"
|
||||
:filter-data="filter"
|
||||
:filter-attributes="filterAttributes"
|
||||
:input-type="getInputType(appliedFilters[i].attribute_key)"
|
||||
:operators="getOperators(appliedFilters[i].attribute_key)"
|
||||
:dropdown-values="getDropdownValues(appliedFilters[i].attribute_key)"
|
||||
:show-query-operator="i !== appliedFilters.length - 1"
|
||||
:v="$v.appliedFilters.$each[i]"
|
||||
@clearPreviousValues="clearPreviousValues(i)"
|
||||
@removeFilter="removeFilter(i)"
|
||||
/>
|
||||
<div class="filter-actions">
|
||||
<button class="append-filter-btn" @click="appendNewFilter">
|
||||
<i class="icon ion-plus-circled margin-right-small" />
|
||||
<span>{{ $t('FILTER.ADD_NEW_FILTER') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-end">
|
||||
<woot-button class="button clear" @click.prevent="onClose">
|
||||
{{ $t('FILTER.CANCEL_BUTTON_LABEL') }}
|
||||
</woot-button>
|
||||
<woot-button @click="submitFilterQuery">
|
||||
{{ $t('FILTER.SUBMIT_BUTTON_LABEL') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from '../../../components/Modal';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import { required } from 'vuelidate/lib/validators';
|
||||
import filterInputBox from './components/FilterInput.vue';
|
||||
import languages from './advancedFilterItems/languages';
|
||||
import countries from './advancedFilterItems/countries';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
filterInputBox,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
props: {
|
||||
onClose: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
filterTypes: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
validations: {
|
||||
appliedFilters: {
|
||||
required,
|
||||
$each: {
|
||||
values: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: true,
|
||||
appliedFilters: [
|
||||
{
|
||||
attribute_key: 'status',
|
||||
filter_operator: 'equal_to',
|
||||
values: '',
|
||||
query_operator: 'and',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filterAttributes() {
|
||||
return this.filterTypes.map(type => {
|
||||
return {
|
||||
key: type.attributeKey,
|
||||
name: type.attributeName,
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('campaigns/get');
|
||||
},
|
||||
methods: {
|
||||
getInputType(key) {
|
||||
const type = this.filterTypes.find(filter => filter.attributeKey === key);
|
||||
return type.inputType;
|
||||
},
|
||||
getOperators(key) {
|
||||
const type = this.filterTypes.find(filter => filter.attributeKey === key);
|
||||
return type.filterOperators;
|
||||
},
|
||||
// eslint-disable-next-line consistent-return
|
||||
getDropdownValues(type) {
|
||||
switch (type) {
|
||||
case 'status':
|
||||
return [
|
||||
{
|
||||
id: 'open',
|
||||
name: 'Open',
|
||||
},
|
||||
{
|
||||
id: 'resolved',
|
||||
name: 'Resolved',
|
||||
},
|
||||
{
|
||||
id: 'pending',
|
||||
name: 'Pending',
|
||||
},
|
||||
{
|
||||
id: 'snoozed',
|
||||
name: 'Snoozed',
|
||||
},
|
||||
{
|
||||
id: 'all',
|
||||
name: 'All',
|
||||
},
|
||||
];
|
||||
case 'assignee_id':
|
||||
return this.$store.getters['agents/getAgents'];
|
||||
case 'contact':
|
||||
return this.$store.getters['contacts/getContacts'];
|
||||
case 'inbox':
|
||||
return this.$store.getters['inboxes/getInboxes'];
|
||||
case 'team_id':
|
||||
return this.$store.getters['teams/getTeams'];
|
||||
case 'campaign_id':
|
||||
return this.$store.getters['campaigns/getAllCampaigns'].map(i => {
|
||||
return {
|
||||
id: i.id,
|
||||
name: i.title,
|
||||
};
|
||||
});
|
||||
case 'labels':
|
||||
return this.$store.getters['labels/getLabels'];
|
||||
case 'browser_language':
|
||||
return languages;
|
||||
case 'country_code':
|
||||
return countries;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
appendNewFilter() {
|
||||
this.appliedFilters.push({
|
||||
attribute_key: 'status',
|
||||
filter_operator: 'equal_to',
|
||||
values: '',
|
||||
query_operator: 'and',
|
||||
});
|
||||
},
|
||||
removeFilter(index) {
|
||||
if (this.appliedFilters.length <= 1) {
|
||||
this.showAlert(this.$t('FILTER.FILTER_DELETE_ERROR'));
|
||||
} else {
|
||||
this.appliedFilters.splice(index, 1);
|
||||
}
|
||||
},
|
||||
submitFilterQuery() {
|
||||
this.$v.$touch();
|
||||
if (this.$v.$invalid) return;
|
||||
this.appliedFilters[this.appliedFilters.length - 1].query_operator = null;
|
||||
this.$emit('applyFilter', this.appliedFilters);
|
||||
},
|
||||
clearPreviousValues(index) {
|
||||
this.appliedFilters[index].values = '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
.filter-modal-content {
|
||||
border: 1px solid $color-border;
|
||||
border-radius: $space-small;
|
||||
padding: $space-normal;
|
||||
}
|
||||
.filter--attributes {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: $space-normal;
|
||||
}
|
||||
.filter--attribute_clearbtn {
|
||||
font-size: $font-size-bigger;
|
||||
margin-left: $space-normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
.filter--attributes_select {
|
||||
margin-bottom: $zero !important;
|
||||
}
|
||||
|
||||
.filter--values_select {
|
||||
margin-bottom: $zero !important;
|
||||
}
|
||||
|
||||
.padding-right-small {
|
||||
padding-right: $space-normal;
|
||||
}
|
||||
.margin-right-small {
|
||||
margin-right: $space-slab;
|
||||
}
|
||||
.append-filter-btn {
|
||||
width: 100%;
|
||||
border: 1px solid $color-border;
|
||||
border-radius: $space-small;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $color-woot;
|
||||
font-size: $font-size-big;
|
||||
padding: $space-normal;
|
||||
height: 38px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.filter-actions {
|
||||
margin: $space-large $zero $space-normal $zero;
|
||||
}
|
||||
.filter--attributes_input {
|
||||
margin-bottom: $zero !important;
|
||||
}
|
||||
.filter--query_operator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
margin: $space-normal $zero;
|
||||
}
|
||||
.filter--query_operator_line {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid $color-border;
|
||||
}
|
||||
.filter--query_operator_container {
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
margin: $zero;
|
||||
}
|
||||
.filter--query_operator_select {
|
||||
width: 100%;
|
||||
margin-bottom: $zero !important;
|
||||
border: none;
|
||||
padding: $zero $space-larger $zero $space-two;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,303 @@
|
|||
const filterTypes = [
|
||||
{
|
||||
attributeKey: 'status',
|
||||
attributeName: 'Status',
|
||||
inputType: 'multi_select',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
],
|
||||
attribute_type: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'assignee_id',
|
||||
attributeName: 'Assignee Name',
|
||||
inputType: 'search_select',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
},
|
||||
],
|
||||
attribute_type: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'contact',
|
||||
attributeName: 'Contact Name',
|
||||
inputType: 'search_select',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
},
|
||||
],
|
||||
attribute_type: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'inbox',
|
||||
attributeName: 'Inbox Name',
|
||||
inputType: 'search_select',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
},
|
||||
],
|
||||
attribute_type: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'team_id',
|
||||
attributeName: 'Team Name',
|
||||
inputType: 'search_select',
|
||||
dataType: 'number',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
},
|
||||
],
|
||||
attribute_type: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'id',
|
||||
attributeName: 'Conversation Identifier',
|
||||
inputType: 'plain_text',
|
||||
dataType: 'Number',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
},
|
||||
],
|
||||
attribute_type: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'campaign_id',
|
||||
attributeName: 'Campaign Name',
|
||||
inputType: 'search_select',
|
||||
dataType: 'Number',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
},
|
||||
],
|
||||
attribute_type: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'labels',
|
||||
attributeName: 'Labels',
|
||||
inputType: 'multi_select',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
},
|
||||
],
|
||||
attribute_type: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'browser_language',
|
||||
attributeName: 'Browser Language',
|
||||
inputType: 'search_select',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
],
|
||||
attribute_type: 'additional_attributes',
|
||||
},
|
||||
{
|
||||
attributeKey: 'country_code',
|
||||
attributeName: 'Country Name',
|
||||
inputType: 'search_select',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
],
|
||||
attribute_type: 'additional_attributes',
|
||||
},
|
||||
{
|
||||
attributeKey: 'referer',
|
||||
attributeName: 'Referer link',
|
||||
inputType: 'plain_text',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
},
|
||||
],
|
||||
attribute_type: 'additional_attributes',
|
||||
},
|
||||
];
|
||||
|
||||
export default filterTypes;
|
|
@ -18,7 +18,8 @@ class Conversations::FilterService < FilterService
|
|||
def conversation_query_builder
|
||||
conversation_filters = @filters['conversations']
|
||||
@params.each_with_index do |query_hash, current_index|
|
||||
query_hash = query_hash.with_indifferent_access
|
||||
# query_hash = query_hash.with_indifferent_access
|
||||
|
||||
current_filter = conversation_filters[query_hash['attribute_key']]
|
||||
@query_string += conversation_query_string(current_filter, query_hash, current_index)
|
||||
end
|
||||
|
|
|
@ -33,8 +33,11 @@ class FilterService
|
|||
end
|
||||
|
||||
def filter_values(query_hash)
|
||||
# query_hash = query_hash.with_indifferent_access
|
||||
if query_hash['attribute_key'] == 'labels' || query_hash['attribute_key'] == 'browser_language'
|
||||
query_hash['values'].map { |x| x['name'] }
|
||||
elsif query_hash['attribute_key'] == 'status'
|
||||
query_hash['values'].map { |x| Conversation.statuses[x['id'].to_sym] }
|
||||
else
|
||||
query_hash['values'].map { |x| x['id'] }
|
||||
end
|
||||
|
|
|
@ -42,11 +42,11 @@ describe ::Conversations::FilterService do
|
|||
filter_operator: 'equal_to',
|
||||
values: [
|
||||
{
|
||||
id: 0,
|
||||
id: 'open',
|
||||
name: 'open'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
id: 'pending',
|
||||
name: 'pending'
|
||||
}
|
||||
],
|
||||
|
|
Loading…
Reference in a new issue