diff --git a/app/javascript/dashboard/api/inbox/conversation.js b/app/javascript/dashboard/api/inbox/conversation.js
index d3942efea..3f1b2e199 100644
--- a/app/javascript/dashboard/api/inbox/conversation.js
+++ b/app/javascript/dashboard/api/inbox/conversation.js
@@ -6,13 +6,14 @@ class ConversationApi extends ApiClient {
super('conversations', { accountScoped: true });
}
- get({ inboxId, status, assigneeType, page }) {
+ get({ inboxId, status, assigneeType, page, labels }) {
return axios.get(this.url, {
params: {
inbox_id: inboxId,
status,
assignee_type: assigneeType,
page,
+ labels,
},
});
}
@@ -44,12 +45,13 @@ class ConversationApi extends ApiClient {
return axios.post(`${this.url}/${conversationId}/mute`);
}
- meta({ inboxId, status, assigneeType }) {
+ meta({ inboxId, status, assigneeType, labels }) {
return axios.get(`${this.url}/meta`, {
params: {
inbox_id: inboxId,
status,
assignee_type: assigneeType,
+ labels,
},
});
}
diff --git a/app/javascript/dashboard/api/labels.js b/app/javascript/dashboard/api/labels.js
new file mode 100644
index 000000000..8a088e840
--- /dev/null
+++ b/app/javascript/dashboard/api/labels.js
@@ -0,0 +1,9 @@
+import ApiClient from './ApiClient';
+
+class LabelsAPI extends ApiClient {
+ constructor() {
+ super('labels', { accountScoped: true });
+ }
+}
+
+export default new LabelsAPI();
diff --git a/app/javascript/dashboard/api/specs/labels.spec.js b/app/javascript/dashboard/api/specs/labels.spec.js
new file mode 100644
index 000000000..f43c807c0
--- /dev/null
+++ b/app/javascript/dashboard/api/specs/labels.spec.js
@@ -0,0 +1,14 @@
+import labels from '../labels';
+import ApiClient from '../ApiClient';
+
+describe('#LabelsAPI', () => {
+ it('creates correct instance', () => {
+ expect(labels).toBeInstanceOf(ApiClient);
+ expect(labels).toHaveProperty('get');
+ expect(labels).toHaveProperty('show');
+ expect(labels).toHaveProperty('create');
+ expect(labels).toHaveProperty('update');
+ expect(labels).toHaveProperty('delete');
+ expect(labels.url).toBe('/api/v1/labels');
+ });
+});
diff --git a/app/javascript/dashboard/assets/scss/_foundation-custom.scss b/app/javascript/dashboard/assets/scss/_foundation-custom.scss
index a544ae883..900dd4d9d 100644
--- a/app/javascript/dashboard/assets/scss/_foundation-custom.scss
+++ b/app/javascript/dashboard/assets/scss/_foundation-custom.scss
@@ -1,6 +1,6 @@
.button {
- font-weight: $font-weight-medium;
font-family: $body-font-family;
+ font-weight: $font-weight-medium;
&.round {
border-radius: 1000px;
@@ -20,10 +20,11 @@
}
.tooltip {
- max-width: 15rem;
- padding: $space-smaller $space-small;
border-radius: $space-smaller;
font-size: $font-size-mini;
+ max-width: 15rem;
+ padding: $space-smaller $space-small;
+ z-index: 9999;
}
code {
diff --git a/app/javascript/dashboard/assets/scss/_foundation-settings.scss b/app/javascript/dashboard/assets/scss/_foundation-settings.scss
index 169198dae..afa81cd80 100644
--- a/app/javascript/dashboard/assets/scss/_foundation-settings.scss
+++ b/app/javascript/dashboard/assets/scss/_foundation-settings.scss
@@ -382,7 +382,7 @@ $label-color: $primary-color;
$label-color-alt: $black;
$label-palette: $foundation-palette;
$label-font-size: $font-size-micro;
-$label-padding: $space-micro $space-smaller;
+$label-padding: $space-smaller $space-small;
$label-radius: $space-micro;
// 21. Media Object
diff --git a/app/javascript/dashboard/assets/scss/widgets/_modal.scss b/app/javascript/dashboard/assets/scss/widgets/_modal.scss
index 0ae8ecf21..8c4e656c4 100644
--- a/app/javascript/dashboard/assets/scss/widgets/_modal.scss
+++ b/app/javascript/dashboard/assets/scss/widgets/_modal.scss
@@ -67,6 +67,10 @@
font-size: $font-size-small;
}
+ .content {
+ @include padding($space-large);
+ }
+
form {
@include padding($space-large);
align-self: center;
diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue
index 9adb05dcc..5ca82a407 100644
--- a/app/javascript/dashboard/components/ChatList.vue
+++ b/app/javascript/dashboard/components/ChatList.vue
@@ -3,7 +3,7 @@
- {{ inbox.name || $t('CHAT_LIST.TAB_HEADING') }}
+ {{ pageTitle }}
@@ -15,14 +15,15 @@
@chatTabChange="updateAssigneeTab"
/>
-
+
{{ $t('CHAT_LIST.LIST.404') }}
@@ -40,7 +41,7 @@
{
+ const labels = this.$store.getters[
+ 'conversationLabels/getConversationLabels'
+ ](conversation.id);
+ return labels.includes(this.label);
+ });
+ },
},
watch: {
conversationInbox() {
this.resetAndFetchData();
},
+ label() {
+ this.resetAndFetchData();
+ },
},
mounted() {
this.$store.dispatch('setChatFilter', this.activeStatus);
this.resetAndFetchData();
- this.$store.dispatch('agents/get');
bus.$on('fetch_conversation_stats', () => {
this.$store.dispatch('conversationStats/get', this.conversationFilters);
@@ -159,17 +202,6 @@ export default {
this.resetAndFetchData();
}
},
- getChatsForTab() {
- let copyList = [];
- if (this.activeAssigneeTab === 'me') {
- copyList = this.mineChatsList.slice();
- } else if (this.activeAssigneeTab === 'unassigned') {
- copyList = this.unAssignedChatsList.slice();
- } else {
- copyList = this.allChatList.slice();
- }
- return copyList;
- },
},
};
diff --git a/app/javascript/dashboard/components/index.js b/app/javascript/dashboard/components/index.js
index 24a81ee20..4fbfa5338 100644
--- a/app/javascript/dashboard/components/index.js
+++ b/app/javascript/dashboard/components/index.js
@@ -6,6 +6,7 @@ import Code from './Code';
import ColorPicker from './widgets/ColorPicker';
import DeleteModal from './widgets/modal/DeleteModal.vue';
import Input from './widgets/forms/Input.vue';
+import Label from './widgets/Label.vue';
import LoadingState from './widgets/LoadingState';
import Modal from './Modal';
import ModalHeader from './ModalHeader';
@@ -25,6 +26,7 @@ const WootUIKit = {
DeleteModal,
Input,
LoadingState,
+ Label,
Modal,
ModalHeader,
ReportStatsCard,
diff --git a/app/javascript/dashboard/components/layout/Sidebar.vue b/app/javascript/dashboard/components/layout/Sidebar.vue
index 88578e635..4ec6c239f 100644
--- a/app/javascript/dashboard/components/layout/Sidebar.vue
+++ b/app/javascript/dashboard/components/layout/Sidebar.vue
@@ -18,6 +18,11 @@
:key="inboxSection.toState"
:menu-item="inboxSection"
/>
+
@@ -125,6 +130,7 @@ export default {
inboxes: 'inboxes/getInboxes',
accountId: 'getCurrentAccountId',
currentRole: 'getCurrentRole',
+ accountLabels: 'labels/getLabelsOnSidebar',
}),
sidemenuItems() {
return getSidebarItems(this.accountId);
@@ -170,6 +176,25 @@ export default {
})),
};
},
+ labelSection() {
+ return {
+ icon: 'ion-pound',
+ label: 'LABELS',
+ hasSubMenu: true,
+ key: 'label',
+ cssClass: 'menu-title align-justify',
+ toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
+ toStateName: 'labels_list',
+ children: this.accountLabels.map(label => ({
+ id: label.id,
+ label: label.title,
+ color: label.color,
+ toState: frontendURL(
+ `accounts/${this.accountId}/label/${label.title}`
+ ),
+ })),
+ };
+ },
dashboardPath() {
return frontendURL(`accounts/${this.accountId}/dashboard`);
},
diff --git a/app/javascript/dashboard/components/layout/SidebarItem.vue b/app/javascript/dashboard/components/layout/SidebarItem.vue
index 35356a685..7ef2dfa88 100644
--- a/app/javascript/dashboard/components/layout/SidebarItem.vue
+++ b/app/javascript/dashboard/components/layout/SidebarItem.vue
@@ -36,7 +36,13 @@
v-if="computedInboxClass(child)"
class="inbox-icon"
:class="computedInboxClass(child)"
- >
+ />
+
+
{{ child.label }}
@@ -126,8 +132,22 @@ export default {
};
diff --git a/app/javascript/dashboard/components/widgets/Label.vue b/app/javascript/dashboard/components/widgets/Label.vue
new file mode 100644
index 000000000..9b56a4c91
--- /dev/null
+++ b/app/javascript/dashboard/components/widgets/Label.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue
index d63940127..0f3a2cd66 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue
@@ -57,6 +57,10 @@ export default {
mixins: [timeMixin, conversationMixin],
props: {
+ activeLabel: {
+ type: String,
+ default: '',
+ },
chat: {
type: Object,
default: () => {},
@@ -116,7 +120,12 @@ export default {
methods: {
cardClick(chat) {
const { activeInbox } = this;
- const path = conversationUrl(this.accountId, activeInbox, chat.id);
+ const path = conversationUrl({
+ accountId: this.accountId,
+ activeInbox,
+ id: chat.id,
+ label: this.activeLabel,
+ });
router.push({ path: frontendURL(path) });
},
inboxName(inboxId) {
diff --git a/app/javascript/dashboard/helper/URLHelper.js b/app/javascript/dashboard/helper/URLHelper.js
index 5e6cae568..97744e43b 100644
--- a/app/javascript/dashboard/helper/URLHelper.js
+++ b/app/javascript/dashboard/helper/URLHelper.js
@@ -5,11 +5,14 @@ export const frontendURL = (path, params) => {
return `/app/${path}${stringifiedParams}`;
};
-export const conversationUrl = (accountId, activeInbox, id) => {
- const path = activeInbox
- ? `accounts/${accountId}/inbox/${activeInbox}/conversations/${id}`
- : `accounts/${accountId}/conversations/${id}`;
- return path;
+export const conversationUrl = ({ accountId, activeInbox, id, label }) => {
+ if (activeInbox) {
+ return `accounts/${accountId}/inbox/${activeInbox}/conversations/${id}`;
+ }
+ if (label) {
+ return `accounts/${accountId}/label/${label}/conversations/${id}`;
+ }
+ return `accounts/${accountId}/conversations/${id}`;
};
export const accountIdFromPathname = pathname => {
diff --git a/app/javascript/dashboard/helper/specs/URLHelper.spec.js b/app/javascript/dashboard/helper/specs/URLHelper.spec.js
index 5c45cf269..354cffcd6 100644
--- a/app/javascript/dashboard/helper/specs/URLHelper.spec.js
+++ b/app/javascript/dashboard/helper/specs/URLHelper.spec.js
@@ -7,15 +7,20 @@ import {
describe('#URL Helpers', () => {
describe('conversationUrl', () => {
it('should return direct conversation URL if activeInbox is nil', () => {
- expect(conversationUrl(1, undefined, 1)).toBe(
+ expect(conversationUrl({ accountId: 1, id: 1 })).toBe(
'accounts/1/conversations/1'
);
});
- it('should return ibox conversation URL if activeInbox is not nil', () => {
- expect(conversationUrl(1, 2, 1)).toBe(
+ it('should return inbox conversation URL if activeInbox is not nil', () => {
+ expect(conversationUrl({ accountId: 1, id: 1, activeInbox: 2 })).toBe(
'accounts/1/inbox/2/conversations/1'
);
});
+ it('should return correct conversation URL if label is active', () => {
+ expect(
+ conversationUrl({ accountId: 1, label: 'customer-support', id: 1 })
+ ).toBe('accounts/1/label/customer-support/conversations/1');
+ });
});
describe('frontendURL', () => {
@@ -27,16 +32,6 @@ describe('#URL Helpers', () => {
});
});
- /*
-
- export const accountIdFromPathname = pathname => {
- const isInsideAccountScopedURLs = pathname.includes('/app/accounts');
- const accountId = isInsideAccountScopedURLs ? pathname.split('/')[3] : '';
- return Number(accountId);
-};
-
- */
-
describe('accountIdFromPathname', () => {
it('should return account id if accont scoped url is passed', () => {
expect(accountIdFromPathname('/app/accounts/1/settings/general')).toBe(1);
diff --git a/app/javascript/dashboard/i18n/default-sidebar.js b/app/javascript/dashboard/i18n/default-sidebar.js
index 4b479f5f6..246bc0af2 100644
--- a/app/javascript/dashboard/i18n/default-sidebar.js
+++ b/app/javascript/dashboard/i18n/default-sidebar.js
@@ -10,6 +10,8 @@ export const getSidebarItems = accountId => ({
'settings_account_reports',
'profile_settings',
'profile_settings_index',
+ 'label_conversations',
+ 'conversations_through_label',
],
menuItems: {
assignedToMe: {
@@ -40,9 +42,8 @@ export const getSidebarItems = accountId => ({
settings: {
routes: [
'agent_list',
- 'agent_new',
'canned_list',
- 'canned_new',
+ 'labels_list',
'settings_inbox',
'settings_inbox_new',
'settings_inbox_list',
@@ -78,6 +79,13 @@ export const getSidebarItems = accountId => ({
toState: frontendURL(`accounts/${accountId}/settings/inboxes/list`),
toStateName: 'settings_inbox_list',
},
+ labels: {
+ icon: 'ion-pricetags',
+ label: 'LABELS',
+ hasSubMenu: false,
+ toState: frontendURL(`accounts/${accountId}/settings/labels/list`),
+ toStateName: 'labels_list',
+ },
cannedResponses: {
icon: 'ion-chatbox-working',
label: 'CANNED_RESPONSES',
diff --git a/app/javascript/dashboard/i18n/locale/en/contact.json b/app/javascript/dashboard/i18n/locale/en/contact.json
index eb7b2d354..e5c3ec25d 100644
--- a/app/javascript/dashboard/i18n/locale/en/contact.json
+++ b/app/javascript/dashboard/i18n/locale/en/contact.json
@@ -11,11 +11,19 @@
},
"LABELS": {
"TITLE": "Conversation Labels",
- "UPDATE_BUTTON": "Update Labels",
- "UPDATE_ERROR": "Couldn't update labels, try again.",
- "TAG_PLACEHOLDER": "Add new label",
- "PLACEHOLDER": "Search or add a label"
+ "MODAL": {
+ "TITLE": "Labels for",
+ "ACTIVE_LABELS": "Labels added to the conversation",
+ "INACTIVE_LABELS": "Labels available in the account",
+ "REMOVE": "Click on X icon to remove the label",
+ "ADD": "Click on + icon to add the label",
+ "UPDATE_BUTTON": "Update labels",
+ "UPDATE_ERROR": "Couldn't update labels, try again."
+ },
+ "NO_LABELS_TO_ADD": "There are no more labels defined in the account.",
+ "NO_AVAILABLE_LABELS": "There are no labels added to this conversation."
},
- "MUTE_CONTACT": "Mute Contact"
+ "MUTE_CONTACT": "Mute Contact",
+ "EDIT_LABEL": "Edit"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/en/index.js b/app/javascript/dashboard/i18n/locale/en/index.js
index 558cc9b3a..5dfc44e4b 100644
--- a/app/javascript/dashboard/i18n/locale/en/index.js
+++ b/app/javascript/dashboard/i18n/locale/en/index.js
@@ -1,5 +1,5 @@
-/* eslint-disable */
import { default as _agentMgmt } from './agentMgmt.json';
+import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
@@ -23,6 +23,7 @@ export default {
..._inboxMgmt,
..._login,
..._report,
+ ..._labelsMgmt,
..._resetPassword,
..._setNewPassword,
..._settings,
diff --git a/app/javascript/dashboard/i18n/locale/en/labelsMgmt.json b/app/javascript/dashboard/i18n/locale/en/labelsMgmt.json
new file mode 100644
index 000000000..b0dbe439f
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/en/labelsMgmt.json
@@ -0,0 +1,68 @@
+{
+ "LABEL_MGMT": {
+ "HEADER": "Labels",
+ "HEADER_BTN_TXT": "Add label",
+ "LOADING": "Fetching labels",
+ "SEARCH_404": "There are no items matching this query",
+ "SIDEBAR_TXT": "Labels
Labels help you to categorize conversations and prioritize them. You can assign label to a conversation from the sidepanel. Labels are tied to the account and can be used to create custom workflows in your organization. You can assign custom color to a label, it makes it easier to identify the label. You will be able to display the label on the sidebar to filter the conversations easily.
",
+ "LIST": {
+ "404": "There are no labels available in this account.",
+ "TITLE": "Manage labels",
+ "DESC": "Labels let you group the conversations together.",
+ "TABLE_HEADER": [
+ "Name",
+ "Description",
+ "Color"
+ ]
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Label Name",
+ "PLACEHOLDER": "Label name",
+ "ERROR": "Label Name is required"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description",
+ "PLACEHOLDER": "Label Description"
+ },
+ "COLOR": {
+ "LABEL": "Color"
+ },
+ "SHOW_ON_SIDEBAR": {
+ "LABEL": "Show label on sidebar"
+ },
+ "EDIT": "Edit",
+ "CREATE": "Create",
+ "DELETE": "Delete",
+ "CANCEL": "Cancel"
+ },
+ "ADD": {
+ "TITLE": "Add label",
+ "DESC": "Labels let you group the conversations together.",
+ "API": {
+ "SUCCESS_MESSAGE": "Label added successfully",
+ "ERROR_MESSAGE": "There was an error, please try again"
+ }
+ },
+ "EDIT": {
+ "TITLE": "Edit label",
+ "API": {
+ "SUCCESS_MESSAGE": "Label updated successfully",
+ "ERROR_MESSAGE": "There was an error, please try again"
+ }
+ },
+ "DELETE": {
+ "BUTTON_TEXT": "Delete",
+ "API": {
+ "SUCCESS_MESSAGE": "Label deleted successfully",
+ "ERROR_MESSAGE": "There was an error, please try again"
+ },
+ "CONFIRM": {
+ "TITLE": "Confirm Deletion",
+ "MESSAGE": "Are you sure to delete ",
+ "YES": "Yes, Delete ",
+ "NO": "No, Keep "
+ }
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json
index 38f5a4aac..67470727f 100644
--- a/app/javascript/dashboard/i18n/locale/en/settings.json
+++ b/app/javascript/dashboard/i18n/locale/en/settings.json
@@ -103,6 +103,7 @@
"INBOXES": "Inboxes",
"CANNED_RESPONSES": "Canned Responses",
"INTEGRATIONS": "Integrations",
- "ACCOUNT_SETTINGS": "Account Settings"
+ "ACCOUNT_SETTINGS": "Account Settings",
+ "LABELS": "Labels"
}
}
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ContactDetailsItem.vue b/app/javascript/dashboard/routes/dashboard/conversation/ContactDetailsItem.vue
index 41068876f..fda504c35 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/ContactDetailsItem.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/ContactDetailsItem.vue
@@ -1,8 +1,13 @@
-
- {{ title }}
+
+
+ {{ title }}
+
+
+ {{ $t('CONTACT_PANEL.EDIT_LABEL') }}
+
{{ value }}
@@ -16,6 +21,12 @@ export default {
title: { type: String, required: true },
icon: { type: String, default: '' },
value: { type: [String, Number], default: '' },
+ showEdit: { type: Boolean, default: false },
+ },
+ methods: {
+ onEdit() {
+ this.$emit('edit');
+ },
},
};
@@ -31,14 +42,18 @@ export default {
padding-bottom: 0;
}
- .conv-details--item__icon {
- padding-right: $space-smaller;
- }
-
.conv-details--item__label {
- font-weight: $font-weight-medium;
- margin-bottom: $space-micro;
+ align-items: center;
+ display: flex;
font-size: $font-size-small;
+ font-weight: $font-weight-medium;
+ justify-content: space-between;
+ margin-bottom: $space-micro;
+
+ button {
+ cursor: pointer;
+ color: $color-body;
+ }
}
.conv-details--item__value {
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue
index d653eb1cf..74a86070a 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue
@@ -2,7 +2,7 @@