Feature: Add/Edit conversation labels (#488)

Co-authored-by: Pranav Raj S <pranavrajs@gmail.com>
This commit is contained in:
Nithin David Thomas
2020-02-16 15:46:26 +05:30
committed by GitHub
parent 77473dc2aa
commit e61ba95cf7
31 changed files with 863 additions and 323 deletions

View File

@@ -1,13 +1,10 @@
<template>
<div class="contact-conversation--panel">
<contact-details-item
icon="ion-chatbubbles"
:title="$t('CONTACT_PANEL.CONVERSATIONS.TITLE')"
/>
<contact-details-item :title="$t('CONTACT_PANEL.CONVERSATIONS.TITLE')" />
<div v-if="!uiFlags.isFetching">
<i v-if="!previousConversations.length">
<p v-if="!previousConversations.length" class="no-results">
{{ $t('CONTACT_PANEL.CONVERSATIONS.NO_RECORDS_FOUND') }}
</i>
</p>
<div v-else class="contact-conversation--list">
<conversation-card
v-for="conversation in previousConversations"
@@ -78,11 +75,13 @@ export default {
@import '~dashboard/assets/scss/mixins';
.contact-conversation--panel {
@include border-normal-top;
padding: $space-medium;
padding: $space-normal $space-normal $space-normal $space-medium;
padding-top: 0;
}
.contact-conversation--list {
margin-top: -$space-normal;
.no-results {
margin: 0;
color: $color-gray;
padding: 0 $space-small;
}
</style>

View File

@@ -1,9 +1,9 @@
<template>
<div class="conv-details--item">
<div class="conv-details--item__label">
<i :class="icon" class="conv-details--item__icon"></i>
<h4 class="conv-details--item__label">
<i v-if="icon" :class="icon" class="conv-details--item__icon"></i>
{{ title }}
</div>
</h4>
<div v-if="value" class="conv-details--item__value">
{{ value }}
</div>
@@ -14,7 +14,7 @@
export default {
props: {
title: { type: String, required: true },
icon: { type: String, required: true },
icon: { type: String, default: '' },
value: { type: [String, Number], default: '' },
},
};
@@ -25,23 +25,25 @@ export default {
@import '~dashboard/assets/scss/mixins';
.conv-details--item {
padding-bottom: $space-normal;
padding-bottom: $space-medium;
&:last-child {
padding-bottom: 0;
}
.conv-details--item__icon {
padding-right: $space-micro;
padding-right: $space-smaller;
}
.conv-details--item__label {
font-weight: $font-weight-medium;
margin-bottom: $space-micro;
font-size: $font-size-small;
}
.conv-details--item__value {
word-break: break-all;
margin-top: $space-small;
}
}
</style>

View File

@@ -48,6 +48,12 @@
{{ contact.additional_attributes.description }}
</div>
</div>
<conversation-labels :conversation-id="conversationId" />
<contact-conversations
v-if="contact.id"
:contact-id="contact.id"
:conversation-id="conversationId"
/>
<div v-if="browser" class="conversation--details">
<contact-details-item
v-if="browser.browser_name"
@@ -74,13 +80,6 @@
icon="ion-clock"
/>
</div>
<contact-conversations
v-if="contact.id"
:contact-id="contact.id"
:conversation-id="conversationId"
/>
<conversation-labels :conversation-id="conversationId" />
</div>
</template>
@@ -164,15 +163,13 @@ export default {
@include border-normal-left;
font-size: $font-size-small;
overflow-y: auto;
background: $color-white;
background: white;
overflow: auto;
}
.contact--profile {
width: 100%;
padding: $space-normal $space-medium $zero;
padding: $space-medium $space-normal 0 $space-medium;
align-items: center;
.user-thumbnail-box {
margin-right: $space-normal;
}
@@ -191,9 +188,10 @@ export default {
.contact--name {
@include text-ellipsis;
text-transform: capitalize;
font-weight: $font-weight-bold;
font-size: $font-size-default;
font-size: $font-size-medium;
}
.contact--email {
@@ -210,8 +208,7 @@ export default {
}
.conversation--details {
padding: $space-medium;
width: 100%;
padding: $space-two $space-normal $space-two $space-medium;
}
.conversation--labels {

View File

@@ -1,21 +1,42 @@
<template>
<div class="contact-conversation--panel">
<contact-details-item
icon="ion-pricetags"
:title="$t('CONTACT_PANEL.LABELS.TITLE')"
/>
<div v-if="!uiFlags.isFetching">
<i v-if="!labels.length">
{{ $t('CONTACT_PANEL.LABELS.NO_RECORDS_FOUND') }}
</i>
<div v-else class="contact-conversation--list">
<span
v-for="label in labels"
:key="label"
class="conversation--label label primary"
>
{{ label }}
</span>
<div
class="contact-conversation--panel sidebar-labels-wrap"
:class="hasEditedClass"
>
<div v-if="!conversationUiFlags.isFetching" class="wrap">
<div class="contact-conversation--list">
<label class="select-tags">
{{ $t('CONTACT_PANEL.LABELS.TITLE') }}
<multiselect
v-model="selectedLabels"
:options="savedLabels"
:tag-placeholder="$t('CONTACT_PANEL.LABELS.TAG_PLACEHOLDER')"
:placeholder="$t('CONTACT_PANEL.LABELS.PLACEHOLDER')"
:multiple="true"
:taggable="true"
hide-selected
:show-labels="false"
@tag="addLabel"
/>
</label>
<div class="row align-middle align-justify">
<span v-if="labelUiFlags.isError" class="error">{{
$t('CONTACT_PANEL.LABELS.UPDATE_ERROR')
}}</span>
<button
v-if="hasEdited"
type="button"
class="button nice tiny"
@click="onUpdateLabels"
>
<spinner v-if="labelUiFlags.isUpdating" size="tiny" />
{{
labelUiFlags.isUpdating
? 'saving...'
: $t('CONTACT_PANEL.LABELS.UPDATE_BUTTON')
}}
</button>
</div>
</div>
</div>
<spinner v-else></spinner>
@@ -24,12 +45,10 @@
<script>
import { mapGetters } from 'vuex';
import Spinner from 'shared/components/Spinner.vue';
import ContactDetailsItem from './ContactDetailsItem.vue';
import Spinner from 'shared/components/Spinner';
export default {
components: {
ContactDetailsItem,
Spinner,
},
props: {
@@ -38,25 +57,64 @@ export default {
required: true,
},
},
data() {
return {
isSearching: false,
selectedLabels: [],
};
},
computed: {
labels() {
return this.$store.getters['conversationLabels/getConversationLabels'](
this.conversationId
hasEdited() {
if (this.selectedLabels.length !== this.savedLabels.length) {
return true;
}
const isSame = this.selectedLabels.every(label =>
this.savedLabels.includes(label)
);
return !isSame;
},
savedLabels() {
const saved = this.$store.getters[
'conversationLabels/getConversationLabels'
](this.conversationId);
return saved;
},
hasEditedClass() {
return this.hasEdited ? 'has-edited' : '';
},
...mapGetters({
uiFlags: 'contactConversations/getUIFlags',
conversationUiFlags: 'contactConversations/getUIFlags',
labelUiFlags: 'conversationLabels/getUIFlags',
}),
},
watch: {
conversationId(newConversationId, prevConversationId) {
if (newConversationId && newConversationId !== prevConversationId) {
this.$store.dispatch('conversationLabels/get', newConversationId);
this.fetchLabels(newConversationId);
}
},
},
mounted() {
this.$store.dispatch('conversationLabels/get', this.conversationId);
const { conversationId } = this;
this.fetchLabels(conversationId);
},
methods: {
addLabel(label) {
this.selectedLabels = [...this.selectedLabels, label];
},
onUpdateLabels() {
this.$store.dispatch('conversationLabels/update', {
conversationId: this.conversationId,
labels: this.selectedLabels,
});
},
async fetchLabels(conversationId) {
try {
await this.$store.dispatch('conversationLabels/get', conversationId);
this.selectedLabels = [...this.savedLabels];
// eslint-disable-next-line no-empty
} catch (error) {}
},
},
};
</script>
@@ -66,12 +124,7 @@ export default {
@import '~dashboard/assets/scss/mixins';
.contact-conversation--panel {
@include border-normal-top;
padding: $space-medium;
}
.contact-conversation--list {
margin-top: -$space-normal;
padding: $space-normal $space-normal $space-normal $space-medium;
}
.conversation--label {
@@ -80,4 +133,38 @@ export default {
font-size: $font-size-small;
padding: $space-smaller;
}
.wrap {
margin-top: $space-slab;
}
.select-tags {
margin-top: $space-small;
.multiselect {
&:hover {
cursor: pointer;
}
transition: $transition-ease-in;
margin-bottom: 0;
}
}
.button {
margin-top: $space-small;
margin-left: auto;
}
.no-results-wrap {
padding: 0 $space-small;
}
.no-results {
margin: $space-normal 0 0 0;
color: $color-gray;
font-weight: $font-weight-normal;
}
.error {
color: $alert-color;
font-size: $font-size-mini;
font-weight: $font-weight-medium;
}
</style>