diff --git a/app/javascript/dashboard/assets/scss/_foundation-custom.scss b/app/javascript/dashboard/assets/scss/_foundation-custom.scss index 650fa884d..7c344ce5b 100644 --- a/app/javascript/dashboard/assets/scss/_foundation-custom.scss +++ b/app/javascript/dashboard/assets/scss/_foundation-custom.scss @@ -67,3 +67,8 @@ code { .margin-right-small { margin-right: var(--space-small); } + +input[type="checkbox"], +input[type="radio"] { + accent-color: var(--w-500); +} diff --git a/app/javascript/dashboard/assets/scss/_foundation-settings.scss b/app/javascript/dashboard/assets/scss/_foundation-settings.scss index 52fa64ba2..c342b0c31 100644 --- a/app/javascript/dashboard/assets/scss/_foundation-settings.scss +++ b/app/javascript/dashboard/assets/scss/_foundation-settings.scss @@ -249,7 +249,7 @@ $button-background-hover: scale-color($button-background, $lightness: -15%); $button-color: $white; $button-color-alt: $white; $button-radius: var(--border-radius-normal); -$button-sizes: (tiny: var(--font-size-micro), +$button-sizes: (tiny: var(--font-size-mini), small: var(--font-size-mini), default: var(--font-size-small), large: var(--font-size-medium)); diff --git a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss index 478045000..15f344ddf 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss @@ -155,12 +155,20 @@ $default-button-height: 4.0rem; // Sizes &.tiny { height: var(--space-medium); + + .icon+.button__content { + padding-left: var(--space-micro); + } } &.small { height: var(--space-large); padding-bottom: var(--space-smaller); padding-top: var(--space-smaller); + + .icon+.button__content { + padding-left: var(--space-smaller); + } } &.large { @@ -190,6 +198,10 @@ $default-button-height: 4.0rem; height: auto; margin: 0; padding: 0; + + &:hover { + text-decoration: underline; + } } } diff --git a/app/javascript/dashboard/assets/scss/widgets/_conversation-card.scss b/app/javascript/dashboard/assets/scss/widgets/_conversation-card.scss index 52a50afb6..c5fbb037d 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_conversation-card.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_conversation-card.scss @@ -1,11 +1,12 @@ @keyframes left-shift-animation { + 0%, 100% { transform: translateX(0); } 50% { - transform: translateX(1px); + transform: translateX(-1px); } } @@ -13,70 +14,24 @@ @include flex; @include flex-shrink; border-bottom: 1px solid transparent; - border-left: $space-micro solid transparent; border-top: 1px solid transparent; cursor: pointer; - padding: 0 0 0 $space-normal; position: relative; &.active { - animation: left-shift-animation 0.25s $swift-ease-out-function; - background: $color-background; - border-bottom-color: $color-border-light; - border-left-color: $color-woot; - border-top-color: $color-border-light; + animation: left-shift-animation 0.25s ease-in-out; + box-shadow: var(--shadow-small); - .conversation--details { - border-top-color: transparent; - } - - + .conversation .conversation--details { + +.conversation::after { border-top-color: transparent; } } - &:first-child { - .conversation--details { - border-top-color: transparent; - } - } - - &:last-child { - .conversation--details { - border-bottom-color: $color-border-light; - } - } - - .conversation--details { - @include border-light-bottom; - @include border-light-top; - border-bottom-color: transparent; - margin: 0 0 0 $space-one; - padding: $space-slab 0; - } - - .conversation--user { - font-size: $font-size-small; - margin-bottom: 0; - text-transform: capitalize; - - .label { - left: $space-micro; - max-width: $space-jumbo; - overflow: hidden; - position: relative; - text-overflow: ellipsis; - top: $space-micro; - white-space: nowrap; - } + &:first-child::after { + border-top-color: transparent; } .conversation--message { - color: $color-body; - font-size: $font-size-small; - font-weight: $font-weight-normal; - height: $space-medium; - line-height: $space-medium; margin: 0; max-width: 96%; overflow: hidden; @@ -119,24 +74,11 @@ } } - &.unread-chat { - .unread { - display: inline-block; - } - - .conversation--message { - font-weight: $font-weight-bold; - } - - .conversation--user { - font-weight: $font-weight-bold; - } - } &.compact { padding-left: 0; - .conversation--details { + .message { border-radius: var(--border-radius-small); margin-left: 0; padding-left: var(--space-two); diff --git a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss index b67932e6d..e5178941b 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss @@ -93,7 +93,7 @@ .page-title { margin-bottom: $zero; - margin-left: $space-normal; + margin-left: var(--space-small); } .status--filter { diff --git a/app/javascript/dashboard/assets/scss/widgets/_tabs.scss b/app/javascript/dashboard/assets/scss/widgets/_tabs.scss index 9a6f6951c..1fd8042ad 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_tabs.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_tabs.scss @@ -34,6 +34,7 @@ // Tab chat type .tab--chat-type { @include flex; + margin-left: var(--space-minus-small); .tabs-title { a { @@ -47,7 +48,7 @@ .tabs-title { flex-shrink: 0; - margin: $zero $space-slab; + margin: $zero $space-small; .badge { background: $color-background; diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index a957ff78b..865b57841 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -117,7 +117,7 @@ :folders-id="foldersId" :chat="chat" :conversation-type="conversationType" - :show-assignee="showAssigneeInConversationCard" + show-assignee :selected="isConversationSelected(chat.id)" @select-conversation="selectConversation" @de-select-conversation="deSelectConversation" @@ -303,12 +303,6 @@ export default { }; }); }, - showAssigneeInConversationCard() { - return ( - this.hasAppliedFiltersOrActiveFolders || - this.activeAssigneeTab === wootConstants.ASSIGNEE_TYPE.ALL - ); - }, inbox() { return this.$store.getters['inboxes/getInbox'](this.activeInbox); }, @@ -796,12 +790,7 @@ export default { flex-shrink: 0; width: 34rem; overflow: hidden; - @include breakpoint(large up) { - width: 36rem; - } - @include breakpoint(xlarge up) { - width: 35rem; - } + @include breakpoint(xxlarge up) { width: 38rem; } diff --git a/app/javascript/dashboard/components/ui/Label.vue b/app/javascript/dashboard/components/ui/Label.vue index 6a0c7de4a..8ea5ebebb 100644 --- a/app/javascript/dashboard/components/ui/Label.vue +++ b/app/javascript/dashboard/components/ui/Label.vue @@ -4,7 +4,7 @@ @@ -117,14 +117,17 @@ export default { height: var(--space-medium); &.small { - font-size: var(--font-size-micro); + font-size: var(--font-size-mini); padding: var(--space-micro) var(--space-smaller); line-height: 1.2; - letter-spacing: 0.15px; + height: var(--space-two); } .label--icon { cursor: pointer; + } + + .label-color-dot { margin-right: var(--space-smaller); } @@ -143,10 +146,10 @@ export default { /* Color Schemes */ &.primary { background: var(--w-100); - color: var(--w-900); + color: var(--w-800); border: 1px solid var(--w-200); a { - color: var(--w-900); + color: var(--w-800); } .label-color-dot { background: var(--w-600); @@ -154,10 +157,10 @@ export default { } &.secondary { background: var(--s-100); - color: var(--s-900); + color: var(--s-800); border: 1px solid var(--s-200); a { - color: var(--s-900); + color: var(--s-800); } .label-color-dot { background: var(--s-600); @@ -165,10 +168,10 @@ export default { } &.success { background: var(--g-100); - color: var(--g-900); + color: var(--g-800); border: 1px solid var(--g-200); a { - color: var(--g-900); + color: var(--g-800); } .label-color-dot { background: var(--g-600); @@ -176,10 +179,10 @@ export default { } &.alert { background: var(--r-100); - color: var(--r-900); + color: var(--r-800); border: 1px solid var(--r-200); a { - color: var(--r-900); + color: var(--r-800); } .label-color-dot { background: var(--r-600); @@ -187,20 +190,20 @@ export default { } &.warning { background: var(--y-100); - color: var(--y-900); + color: var(--y-800); border: 1px solid var(--y-200); a { - color: var(--y-900); + color: var(--y-800); } .label-color-dot { - background: var(--y-900); + background: var(--y-800); } } &.smooth { background: transparent; - border: 1px solid var(--s-75); - color: var(--s-800); + border: 1px solid var(--s-100); + color: var(--s-700); } } @@ -221,7 +224,8 @@ export default { } .label-action--button { - margin-bottom: var(--space-minus-micro); + display: flex; + margin-right: var(--space-smaller); } .label-color-dot { @@ -230,5 +234,13 @@ export default { height: var(--space-one); border-radius: var(--border-radius-small); margin-right: var(--space-smaller); + box-shadow: var(--shadow-small); +} +.label.small .label-color-dot { + width: var(--space-small); + height: var(--space-small); + border-radius: var(--border-radius-small); + margin-right: var(--space-micro); + box-shadow: var(--shadow-small); } diff --git a/app/javascript/dashboard/components/widgets/InboxName.vue b/app/javascript/dashboard/components/widgets/InboxName.vue index cb0b9fb06..e787a5e13 100644 --- a/app/javascript/dashboard/components/widgets/InboxName.vue +++ b/app/javascript/dashboard/components/widgets/InboxName.vue @@ -1,8 +1,16 @@ - - + {{ inbox.name }} - + - diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue index 3f50c5577..3644910f7 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue @@ -1,5 +1,7 @@ @@ -24,74 +26,134 @@ - - - - - - {{ assignee.name }} - + + + + + + + {{ chat.id }} + + + - - {{ currentContact.name }} - - - + + {{ currentContact.name }} + + - - - - {{ parsedLastMessage }} + + + + + {{ unreadCount > 9 ? '9+' : unreadCount }} - + - {{ this.$t(`${attachmentMessageContent}`) }} - - - {{ $t('CHAT_LIST.NO_CONTENT') }} - - - - - - {{ this.$t(`CHAT_LIST.NO_MESSAGES`) }} - - - - - - - {{ unreadCount > 9 ? '9+' : unreadCount }} + + + + {{ parsedLastMessage }} + + + + {{ this.$t(`${attachmentMessageContent}`) }} + + + {{ $t('CHAT_LIST.NO_CONTENT') }} + + + + + + {{ this.$t(`CHAT_LIST.NO_MESSAGES`) }} + + + + + + + + + + import { mapGetters } from 'vuex'; import { MESSAGE_TYPE } from 'widget/helpers/constants'; +import conversationLabelMixin from 'dashboard/mixins/conversation/labelMixin'; import messageFormatterMixin from 'shared/mixins/messageFormatterMixin'; +import { copyTextToClipboard } from 'shared/helpers/clipboard'; +import { frontendURL, conversationUrl } from 'dashboard/helper/URLHelper'; + import Thumbnail from '../Thumbnail'; import conversationMixin from '../../../mixins/conversations'; import timeMixin from '../../../mixins/time'; import router from '../../../routes'; -import { frontendURL, conversationUrl } from '../../../helper/URLHelper'; import InboxName from '../InboxName'; import inboxMixin from 'shared/mixins/inboxMixin'; import ConversationContextMenu from './contextMenu/Index.vue'; @@ -145,11 +210,12 @@ export default { }, mixins: [ - inboxMixin, - timeMixin, - conversationMixin, - messageFormatterMixin, alertMixin, + conversationMixin, + conversationLabelMixin, + inboxMixin, + messageFormatterMixin, + timeMixin, ], props: { activeLabel: { @@ -193,6 +259,8 @@ export default { return { hovered: false, showContextMenu: false, + showAllLabels: false, + showExpandLabelButton: false, contextMenu: { x: null, y: null, @@ -207,9 +275,15 @@ export default { currentUser: 'getCurrentUser', accountId: 'getCurrentAccountId', }), + + conversationId() { + return this.chat.id; + }, + bulkActionCheck() { return !this.hideThumbnail && !this.hovered && !this.selected; }, + chatMetadata() { return this.chat.meta || {}; }, @@ -250,10 +324,6 @@ export default { return this.unreadCount > 0; }, - isInboxNameVisible() { - return !this.activeInbox; - }, - lastMessageInChat() { return this.lastMessage(this.chat); }, @@ -289,18 +359,116 @@ export default { }, showInboxName() { - return ( - !this.hideInboxName && - this.isInboxNameVisible && - this.inboxesList.length > 1 - ); + return !this.hideInboxName && this.inboxesList.length > 1; }, inboxName() { const stateInbox = this.inbox; return stateInbox.name || ''; }, + assigneeName() { + return ( + this.assignee.name || this.$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER') + ); + }, + getConversationUrl() { + const path = conversationUrl({ + accountId: this.accountId, + id: this.chat.id, + }); + return frontendURL(path); + }, + badgeTooltipKey() { + return { + instagram_direct_message: 'INSTAGRAM_DM', + facebook: 'FB_DM', + 'twitter-tweet': 'TWITTER_TWEET', + 'twitter-dm': 'TWITTER_DM', + whatsapp: 'WHATSAPP', + sms: 'SMS', + 'Channel::Line': 'LINE', + 'Channel::Telegram': 'TELEGRAM', + 'Channel::WebWidget': 'WEB_WIDGET', + }[this.inboxBadge]; + }, + badgeSrc() { + return { + instagram_direct_message: 'instagram-dm', + facebook: 'messenger', + 'twitter-tweet': 'twitter-tweet', + 'twitter-dm': 'twitter-dm', + whatsapp: 'whatsapp', + sms: 'sms', + 'Channel::Line': 'line', + 'Channel::Telegram': 'telegram', + 'Channel::WebWidget': '', + }[this.inboxBadge]; + }, + badgeStyle() { + const size = 10; + const badgeSize = `${size + 2}px`; + const borderRadius = `${size / 2}px`; + return { width: badgeSize, height: badgeSize, borderRadius }; + }, + remainingLabel() { + const { label } = this.chat; + const { name } = this.activeLabel; + return label.filter(l => l.name !== name); + }, + }, + watch: { + activeLabels() { + this.collapseLabels(); + }, + }, + mounted() { + this.collapseLabels(); }, methods: { + collapseLabels() { + const footer = this.$refs.conversationCard.querySelector('.footer'); + const labels = this.$refs.conversationCard.querySelectorAll('.label'); + const labelsWrap = this.$refs.conversationCard.querySelector( + '.labels-wrap' + ); + + // If labels are not rendered yet or has no labels, return + if (!footer || !labelsWrap || this.activeLabels.length === 0) { + return; + } + + const labelsWrapWidth = footer.scrollWidth; + let currentIndex = 0; + let labelsWidth = 0; + + // Remove hidden class from all labels as we are going to calculate the overflow + Array.from(labels).forEach(label => { + label.classList.remove('hidden'); + }); + this.showExpandLabelButton = + footer.offsetWidth - 24 < labelsWrap.scrollWidth; + + // If labels are not overflowing, return + if (!this.showExpandLabelButton) return; + + // Find from which index labels are overflowing + do { + if (labelsWidth + 80 < labelsWrapWidth) { + labelsWidth += Array.from(labels)[currentIndex].offsetWidth + 8; + currentIndex += 1; + } else { + break; + } + } while (currentIndex < labels.length); + + // Hide labels from currentIndex + Array.from(labels).forEach((label, index) => { + if (index >= currentIndex) { + label.classList.add('hidden'); + } else { + label.classList.remove('hidden'); + } + }); + }, cardClick(chat) { const { activeInbox } = this; const path = conversationUrl({ @@ -317,6 +485,12 @@ export default { } router.push({ path: frontendURL(path) }); }, + async onCopyId(e) { + e.stopPropagation(); + const url = window.chatwootConfig.hostURL + this.getConversationUrl; + await copyTextToClipboard(url); + this.showAlert(this.$t('CONTACT_PANEL.COPY_SUCCESSFUL')); + }, onCardHover() { this.hovered = !this.hideThumbnail; }, @@ -349,6 +523,10 @@ export default { snoozedUntil ); }, + onShowLabels(e) { + e.stopPropagation(); + this.showAllLabels = !this.showAllLabels; + }, async onAssignAgent(agent) { this.$emit('assign-agent', agent, [this.chat.id]); this.closeContextMenu(); @@ -370,81 +548,277 @@ export default { diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue index 5abf0eb5c..1d70ffac1 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue @@ -21,7 +21,7 @@ /> - + diff --git a/app/javascript/dashboard/components/widgets/conversation/conversationBulkActions/Index.vue b/app/javascript/dashboard/components/widgets/conversation/conversationBulkActions/Index.vue index 12fb6210c..360dc8bc4 100644 --- a/app/javascript/dashboard/components/widgets/conversation/conversationBulkActions/Index.vue +++ b/app/javascript/dashboard/components/widgets/conversation/conversationBulkActions/Index.vue @@ -186,16 +186,17 @@ export default {
- + + {{ currentContact.name }} + + - - - - {{ parsedLastMessage }} +
- {{ this.$t(`${attachmentMessageContent}`) }} -
- - - {{ this.$t(`CHAT_LIST.NO_MESSAGES`) }} - -
+ + + {{ this.$t(`CHAT_LIST.NO_MESSAGES`) }} + +