feat: Display "Snoozed Until" time on conversation header (#3028)

This commit is contained in:
Pranav Raj S
2021-09-29 19:33:51 +05:30
committed by GitHub
parent 49ac4a4400
commit 57abdc4d5f
19 changed files with 217 additions and 172 deletions

View File

@@ -0,0 +1,35 @@
<template>
<span class="inbox--name">
<i :class="computedInboxClass" />
{{ inbox.name }}
</span>
</template>
<script>
import { getInboxClassByType } from 'dashboard/helper/inbox';
export default {
props: {
inbox: {
type: Object,
default: () => {},
},
},
computed: {
computedInboxClass() {
const { phone_number: phoneNumber, channel_type: type } = this.inbox;
const classByType = getInboxClassByType(type, phoneNumber);
return classByType;
},
},
};
</script>
<style scoped>
.inbox--name {
padding: var(--space-micro) 0;
line-height: var(--space-slab);
font-weight: var(--font-weight-medium);
background: none;
color: var(--s-500);
font-size: var(--font-size-mini);
}
</style>

View File

@@ -19,11 +19,7 @@
/>
<div class="conversation--details columns">
<div class="conversation--metadata">
<span v-if="showInboxName" class="label">
<i :class="computedInboxClass" />
{{ inboxName }}
</span>
<inbox-name v-if="showInboxName" :inbox="inbox" />
<span
v-if="showAssignee && assignee"
class="label assignee-label text-truncate"
@@ -72,16 +68,17 @@
import { mapGetters } from 'vuex';
import { MESSAGE_TYPE } from 'widget/helpers/constants';
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
import { getInboxClassByType } from 'dashboard/helper/inbox';
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';
export default {
components: {
InboxName,
Thumbnail,
},
@@ -192,12 +189,6 @@ export default {
return stateInbox;
},
computedInboxClass() {
const { phone_number: phoneNumber, channel_type: type } = this.inbox;
const classByType = getInboxClassByType(type, phoneNumber);
return classByType;
},
showInboxName() {
return (
!this.hideInboxName &&
@@ -244,15 +235,6 @@ export default {
}
}
.conversation--details .label {
padding: var(--space-micro) 0 var(--space-micro) 0;
line-height: var(--space-slab);
font-weight: var(--font-weight-medium);
background: none;
color: var(--s-500);
font-size: var(--font-size-mini);
}
.conversation--details {
.conversation--user {
padding-top: var(--space-micro);
@@ -276,6 +258,15 @@ export default {
justify-content: space-between;
padding-right: var(--space-normal);
.label {
padding: var(--space-micro) 0 var(--space-micro) 0;
line-height: var(--space-slab);
font-weight: var(--font-weight-medium);
background: none;
color: var(--s-500);
font-size: var(--font-size-mini);
}
.assignee-label {
max-width: 50%;
}

View File

@@ -12,20 +12,23 @@
<h3 class="user--name text-truncate">
{{ currentContact.name }}
</h3>
<woot-button
class="user--profile__button"
size="small"
variant="link"
@click="$emit('contact-panel-toggle')"
>
{{
`${
isContactPanelOpen
? $t('CONVERSATION.HEADER.CLOSE')
: $t('CONVERSATION.HEADER.OPEN')
} ${$t('CONVERSATION.HEADER.DETAILS')}`
}}
</woot-button>
<div class="conversation--header--actions">
<inbox-name :inbox="inbox" class="margin-right-small" />
<span
v-if="isSnoozed"
class="snoozed--display-text margin-right-small"
>
{{ snoozedDisplayText }}
</span>
<woot-button
class="user--profile__button margin-right-small"
size="small"
variant="link"
@click="$emit('contact-panel-toggle')"
>
{{ contactPanelToggleText }}
</woot-button>
</div>
</div>
</div>
<div
@@ -44,9 +47,13 @@ import agentMixin from '../../../mixins/agentMixin.js';
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
import inboxMixin from 'shared/mixins/inboxMixin';
import { hasPressedAltAndOKey } from 'shared/helpers/KeyboardHelpers';
import wootConstants from '../../../constants';
import differenceInHours from 'date-fns/differenceInHours';
import InboxName from '../InboxName';
export default {
components: {
InboxName,
MoreActions,
Thumbnail,
},
@@ -61,39 +68,50 @@ export default {
default: false,
},
},
data() {
return {
currentChatAssignee: null,
inboxId: null,
};
},
computed: {
...mapGetters({
uiFlags: 'inboxAssignableAgents/getUIFlags',
currentChat: 'getSelectedChat',
}),
chatMetadata() {
return this.chat.meta;
},
inbox() {
const { inbox_id: inboxId } = this.chat;
const stateInbox = this.$store.getters['inboxes/getInbox'](inboxId);
return stateInbox;
},
currentContact() {
return this.$store.getters['contacts/getContact'](
this.chat.meta.sender.id
);
},
},
mounted() {
const { inbox_id: inboxId } = this.chat;
this.inboxId = inboxId;
isSnoozed() {
return this.currentChat.status === wootConstants.STATUS_TYPE.SNOOZED;
},
snoozedDisplayText() {
const { snoozed_until: snoozedUntil } = this.currentChat;
if (snoozedUntil) {
// When the snooze is applied, it schedules the unsnooze event to next day/week 9AM.
// By that logic if the time difference is less than or equal to 24 + 9 hours we can consider it tomorrow.
const MAX_TIME_DIFFERENCE = 33;
const isSnoozedUntilTomorrow =
differenceInHours(new Date(snoozedUntil), new Date()) <=
MAX_TIME_DIFFERENCE;
return this.$t(
isSnoozedUntilTomorrow
? 'CONVERSATION.HEADER.SNOOZED_UNTIL_TOMORROW'
: 'CONVERSATION.HEADER.SNOOZED_UNTIL_NEXT_WEEK'
);
}
return this.$t('CONVERSATION.HEADER.SNOOZED_UNTIL_NEXT_REPLY');
},
contactPanelToggleText() {
return `${
this.isContactPanelOpen
? this.$t('CONVERSATION.HEADER.CLOSE')
: this.$t('CONVERSATION.HEADER.OPEN')
} ${this.$t('CONVERSATION.HEADER.DETAILS')}`;
},
inbox() {
const { inbox_id: inboxId } = this.chat;
return this.$store.getters['inboxes/getInbox'](inboxId);
},
},
methods: {
@@ -129,4 +147,28 @@ export default {
flex-shrink: 0;
}
}
.user--name {
display: inline-block;
font-size: var(--font-size-medium);
line-height: 1.3;
margin: 0;
text-transform: capitalize;
width: 100%;
}
.conversation--header--actions {
align-items: center;
display: flex;
font-size: var(--font-size-mini);
.user--profile__button {
padding: 0;
}
.snoozed--display-text {
font-weight: var(--font-weight-medium);
color: var(--y-900);
}
}
</style>

View File

@@ -1,41 +1,29 @@
<template>
<div class="flex-container actions--container">
<woot-button
v-if="!currentChat.muted"
v-tooltip="$t('CONTACT_PANEL.MUTE_CONTACT')"
class="hollow secondary actions--button"
icon="ion-volume-mute"
@click="mute"
/>
<woot-button
v-else
v-tooltip.left="$t('CONTACT_PANEL.UNMUTE_CONTACT')"
class="hollow secondary actions--button"
icon="ion-volume-medium"
@click="unmute"
/>
<woot-button
v-tooltip="$t('CONTACT_PANEL.SEND_TRANSCRIPT')"
class="hollow secondary actions--button"
icon="ion-share"
@click="toggleEmailActionsModal"
/>
<resolve-action
:conversation-id="currentChat.id"
:status="currentChat.status"
/>
<woot-button
class="more--button"
variant="clear"
size="large"
color-scheme="secondary"
icon="ion-android-more-vertical"
@click="toggleConversationActions"
/>
<div
v-if="showConversationActions"
v-on-clickaway="hideConversationActions"
class="dropdown-pane dropdowm--bottom"
:class="{ 'dropdown-pane--open': showConversationActions }"
>
<woot-dropdown-menu>
<woot-dropdown-item v-if="!currentChat.muted">
<button class="button clear alert " @click="mute">
<span>{{ $t('CONTACT_PANEL.MUTE_CONTACT') }}</span>
</button>
</woot-dropdown-item>
<woot-dropdown-item v-else>
<button class="button clear alert" @click="unmute">
<span>{{ $t('CONTACT_PANEL.UNMUTE_CONTACT') }}</span>
</button>
</woot-dropdown-item>
<woot-dropdown-item>
<button class="button clear" @click="toggleEmailActionsModal">
{{ $t('CONTACT_PANEL.SEND_TRANSCRIPT') }}
</button>
</woot-dropdown-item>
</woot-dropdown-menu>
</div>
<email-transcript-modal
v-if="showEmailActionsModal"
:show="showEmailActionsModal"
@@ -50,13 +38,9 @@ import { mixin as clickaway } from 'vue-clickaway';
import alertMixin from 'shared/mixins/alertMixin';
import EmailTranscriptModal from './EmailTranscriptModal';
import ResolveAction from '../../buttons/ResolveAction';
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
export default {
components: {
WootDropdownMenu,
WootDropdownItem,
EmailTranscriptModal,
ResolveAction,
},
@@ -97,7 +81,16 @@ export default {
};
</script>
<style scoped lang="scss">
@import '~dashboard/assets/scss/mixins';
.actions--container {
align-items: center;
.button {
font-size: var(--font-size-large);
margin-right: var(--space-small);
border-color: var(--color-border);
color: var(--s-400);
}
}
.more--button {
align-items: center;

View File

@@ -1,6 +1,7 @@
import { createLocalVue, mount } from '@vue/test-utils';
import Vuex from 'vuex';
import VueI18n from 'vue-i18n';
import VTooltip from 'v-tooltip';
import Button from 'dashboard/components/buttons/Button';
import i18n from 'dashboard/i18n';
@@ -10,6 +11,7 @@ import MoreActions from '../MoreActions';
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueI18n);
localVue.use(VTooltip);
localVue.component('woot-button', Button);
@@ -63,21 +65,9 @@ describe('MoveActions', () => {
moreActions = mount(MoreActions, { store, localVue, i18n: i18nConfig });
});
it('opens the menu when user clicks "more"', async () => {
expect(moreActions.find('.dropdown-pane').exists()).toBe(false);
await moreActions.find('.more--button').trigger('click');
expect(moreActions.find('.dropdown-pane').exists()).toBe(true);
});
describe('muting discussion', () => {
it('triggers "muteConversation"', async () => {
await moreActions.find('.more--button').trigger('click');
await moreActions
.find('.dropdown-pane button:first-child')
.trigger('click');
await moreActions.find('button:first-child').trigger('click');
expect(muteConversation).toBeCalledWith(
expect.any(Object),
@@ -87,11 +77,7 @@ describe('MoveActions', () => {
});
it('shows alert', async () => {
await moreActions.find('.more--button').trigger('click');
await moreActions
.find('.dropdown-pane button:first-child')
.trigger('click');
await moreActions.find('button:first-child').trigger('click');
expect(window.bus.$emit).toBeCalledWith(
'newToastMessage',
@@ -106,11 +92,7 @@ describe('MoveActions', () => {
});
it('triggers "unmuteConversation"', async () => {
await moreActions.find('.more--button').trigger('click');
await moreActions
.find('.dropdown-pane button:first-child')
.trigger('click');
await moreActions.find('button:first-child').trigger('click');
expect(unmuteConversation).toBeCalledWith(
expect.any(Object),
@@ -120,11 +102,7 @@ describe('MoveActions', () => {
});
it('shows alert', async () => {
await moreActions.find('.more--button').trigger('click');
await moreActions
.find('.dropdown-pane button:first-child')
.trigger('click');
await moreActions.find('button:first-child').trigger('click');
expect(window.bus.$emit).toBeCalledWith(
'newToastMessage',