feat: Shows an alert while replying if chats are not assigned to them (#3867)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
parent
a737f89c47
commit
1a3d39a7f3
4 changed files with 233 additions and 75 deletions
129
app/javascript/dashboard/components/ui/Banner.vue
Normal file
129
app/javascript/dashboard/components/ui/Banner.vue
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<template>
|
||||||
|
<div class="banner" :class="bannerClasses">
|
||||||
|
<span>
|
||||||
|
{{ bannerMessage }}
|
||||||
|
<a
|
||||||
|
v-if="hrefLink"
|
||||||
|
:href="hrefLink"
|
||||||
|
rel="noopener noreferrer nofollow"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ hrefLinkText }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<woot-button
|
||||||
|
v-if="hasActionButton"
|
||||||
|
size="small"
|
||||||
|
variant="link"
|
||||||
|
icon="arrow-right"
|
||||||
|
color-scheme="primary"
|
||||||
|
class-names="banner-action__button"
|
||||||
|
@click="onClick"
|
||||||
|
>
|
||||||
|
{{ actionButtonLabel }}
|
||||||
|
</woot-button>
|
||||||
|
<woot-button
|
||||||
|
v-if="hasCloseButton"
|
||||||
|
size="small"
|
||||||
|
variant="link"
|
||||||
|
color-scheme="warning"
|
||||||
|
icon="dismiss-circle"
|
||||||
|
class-names="banner-action__button"
|
||||||
|
@click="onClickClose"
|
||||||
|
>
|
||||||
|
</woot-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
bannerMessage: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
hrefLink: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
hrefLinkText: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
hasActionButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
actionButtonLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
colorScheme: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
hasCloseButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
bannerClasses() {
|
||||||
|
return [this.colorScheme];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick(e) {
|
||||||
|
this.$emit('click', e);
|
||||||
|
},
|
||||||
|
onClickClose(e) {
|
||||||
|
this.$emit('close', e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.banner {
|
||||||
|
display: flex;
|
||||||
|
color: var(--white);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
padding: var(--space-slab) var(--space-normal);
|
||||||
|
justify-content: center;
|
||||||
|
position: sticky;
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
background: var(--s-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.alert {
|
||||||
|
background: var(--r-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: var(--y-800);
|
||||||
|
color: var(--s-600);
|
||||||
|
a {
|
||||||
|
color: var(--s-600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.gray {
|
||||||
|
background: var(--b-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: var(--white);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-action__button {
|
||||||
|
margin: 0 var(--space-smaller);
|
||||||
|
|
||||||
|
::v-deep .button__content {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,56 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="view-box fill-height">
|
<div class="view-box fill-height">
|
||||||
<div
|
<banner
|
||||||
v-if="!currentChat.can_reply && !isAWhatsappChannel"
|
v-if="!currentChat.can_reply && !isAWhatsappChannel"
|
||||||
class="banner messenger-policy--banner"
|
color-scheme="alert"
|
||||||
>
|
:banner-message="$t('CONVERSATION.CANNOT_REPLY')"
|
||||||
<span>
|
:href-link="facebookReplyPolicy"
|
||||||
{{ $t('CONVERSATION.CANNOT_REPLY') }}
|
:href-link-text="$t('CONVERSATION.24_HOURS_WINDOW')"
|
||||||
<a
|
/>
|
||||||
:href="facebookReplyPolicy"
|
|
||||||
rel="noopener noreferrer nofollow"
|
<banner
|
||||||
target="_blank"
|
v-if="!currentChat.can_reply && isAWhatsappChannel"
|
||||||
>
|
color-scheme="alert"
|
||||||
{{ $t('CONVERSATION.24_HOURS_WINDOW') }}
|
:banner-message="$t('CONVERSATION.TWILIO_WHATSAPP_CAN_REPLY')"
|
||||||
</a>
|
:href-link="twilioWhatsAppReplyPolicy"
|
||||||
</span>
|
:href-link-text="$t('CONVERSATION.TWILIO_WHATSAPP_24_HOURS_WINDOW')"
|
||||||
</div>
|
/>
|
||||||
<div
|
|
||||||
v-if="!currentChat.can_reply && isAWhatsappChannel"
|
<banner
|
||||||
class="banner messenger-policy--banner"
|
v-if="isATweet"
|
||||||
>
|
color-scheme="gray"
|
||||||
<span>
|
:banner-message="tweetBannerText"
|
||||||
{{ $t('CONVERSATION.TWILIO_WHATSAPP_CAN_REPLY') }}
|
:has-close-button="hasSelectedTweetId"
|
||||||
<a
|
@close="removeTweetSelection"
|
||||||
:href="twilioWhatsAppReplyPolicy"
|
/>
|
||||||
rel="noopener noreferrer nofollow"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ $t('CONVERSATION.TWILIO_WHATSAPP_24_HOURS_WINDOW') }}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isATweet" class="banner">
|
|
||||||
<span v-if="!selectedTweetId">
|
|
||||||
{{ $t('CONVERSATION.SELECT_A_TWEET_TO_REPLY') }}
|
|
||||||
</span>
|
|
||||||
<span v-else>
|
|
||||||
{{ $t('CONVERSATION.REPLYING_TO') }}
|
|
||||||
{{ selectedTweet.content || '' }}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
v-if="selectedTweetId"
|
|
||||||
class="banner-close-button"
|
|
||||||
@click="removeTweetSelection"
|
|
||||||
>
|
|
||||||
<fluent-icon
|
|
||||||
v-tooltip="$t('CONVERSATION.REMOVE_SELECTION')"
|
|
||||||
size="16"
|
|
||||||
icon="dismiss"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-toggle__wrap">
|
<div class="sidebar-toggle__wrap">
|
||||||
<woot-button
|
<woot-button
|
||||||
variant="smooth"
|
variant="smooth"
|
||||||
|
@ -126,6 +99,7 @@ import { mapGetters } from 'vuex';
|
||||||
import ReplyBox from './ReplyBox';
|
import ReplyBox from './ReplyBox';
|
||||||
import Message from './Message';
|
import Message from './Message';
|
||||||
import conversationMixin from '../../../mixins/conversations';
|
import conversationMixin from '../../../mixins/conversations';
|
||||||
|
import Banner from 'dashboard/components/ui/Banner.vue';
|
||||||
import { getTypingUsersText } from '../../../helper/commons';
|
import { getTypingUsersText } from '../../../helper/commons';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
import { REPLY_POLICY } from 'shared/constants/links';
|
import { REPLY_POLICY } from 'shared/constants/links';
|
||||||
|
@ -139,6 +113,7 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
Message,
|
Message,
|
||||||
ReplyBox,
|
ReplyBox,
|
||||||
|
Banner,
|
||||||
},
|
},
|
||||||
mixins: [conversationMixin, inboxMixin, eventListenerMixins, clickaway],
|
mixins: [conversationMixin, inboxMixin, eventListenerMixins, clickaway],
|
||||||
props: {
|
props: {
|
||||||
|
@ -173,7 +148,17 @@ export default {
|
||||||
inbox() {
|
inbox() {
|
||||||
return this.$store.getters['inboxes/getInbox'](this.inboxId);
|
return this.$store.getters['inboxes/getInbox'](this.inboxId);
|
||||||
},
|
},
|
||||||
|
hasSelectedTweetId() {
|
||||||
|
return !!this.selectedTweetId;
|
||||||
|
},
|
||||||
|
|
||||||
|
tweetBannerText() {
|
||||||
|
return !this.selectedTweetId
|
||||||
|
? this.$t('CONVERSATION.SELECT_A_TWEET_TO_REPLY')
|
||||||
|
: `
|
||||||
|
${this.$t('CONVERSATION.REPLYING_TO')}
|
||||||
|
${this.selectedTweet.content}` || '';
|
||||||
|
},
|
||||||
typingUsersList() {
|
typingUsersList() {
|
||||||
const userList = this.$store.getters[
|
const userList = this.$store.getters[
|
||||||
'conversationTypingStatus/getUserList'
|
'conversationTypingStatus/getUserList'
|
||||||
|
@ -375,31 +360,6 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.banner {
|
|
||||||
background: var(--b-500);
|
|
||||||
color: var(--white);
|
|
||||||
font-size: var(--font-size-mini);
|
|
||||||
padding: var(--space-slab) var(--space-normal);
|
|
||||||
text-align: center;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: var(--white);
|
|
||||||
font-size: var(--font-size-mini);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.messenger-policy--banner {
|
|
||||||
background: var(--r-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner-close-button {
|
|
||||||
cursor: pointer;
|
|
||||||
margin-left: var(--space--two);
|
|
||||||
color: var(--white);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner--container {
|
.spinner--container {
|
||||||
min-height: var(--space-jumbo);
|
min-height: var(--space-jumbo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="reply-box" :class="replyBoxClass">
|
<div class="reply-box" :class="replyBoxClass">
|
||||||
|
<banner
|
||||||
|
v-if="showSelfAssignBanner"
|
||||||
|
color-scheme="secondary"
|
||||||
|
:banner-message="$t('CONVERSATION.NOT_ASSIGNED_TO_YOU')"
|
||||||
|
:has-action-button="true"
|
||||||
|
:action-button-label="$t('CONVERSATION.ASSIGN_TO_ME')"
|
||||||
|
@click="onClickSelfAssign"
|
||||||
|
/>
|
||||||
<reply-top-panel
|
<reply-top-panel
|
||||||
:mode="replyType"
|
:mode="replyType"
|
||||||
:set-reply-mode="setReplyMode"
|
:set-reply-mode="setReplyMode"
|
||||||
|
@ -90,6 +98,7 @@ import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview';
|
||||||
import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel';
|
import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel';
|
||||||
import ReplyEmailHead from './ReplyEmailHead';
|
import ReplyEmailHead from './ReplyEmailHead';
|
||||||
import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel';
|
import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel';
|
||||||
|
import Banner from 'dashboard/components/ui/Banner.vue';
|
||||||
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
||||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
||||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||||
|
@ -117,6 +126,7 @@ export default {
|
||||||
ReplyEmailHead,
|
ReplyEmailHead,
|
||||||
ReplyBottomPanel,
|
ReplyBottomPanel,
|
||||||
WootMessageEditor,
|
WootMessageEditor,
|
||||||
|
Banner,
|
||||||
},
|
},
|
||||||
mixins: [clickaway, inboxMixin, uiSettingsMixin, alertMixin],
|
mixins: [clickaway, inboxMixin, uiSettingsMixin, alertMixin],
|
||||||
props: {
|
props: {
|
||||||
|
@ -150,6 +160,11 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentChat: 'getSelectedChat',
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
|
}),
|
||||||
|
|
||||||
showRichContentEditor() {
|
showRichContentEditor() {
|
||||||
if (this.isOnPrivateNote) {
|
if (this.isOnPrivateNote) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -164,7 +179,36 @@ export default {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
...mapGetters({ currentChat: 'getSelectedChat' }),
|
assignedAgent: {
|
||||||
|
get() {
|
||||||
|
return this.currentChat.meta.assignee;
|
||||||
|
},
|
||||||
|
set(agent) {
|
||||||
|
const agentId = agent ? agent.id : 0;
|
||||||
|
this.$store.dispatch('setCurrentChatAssignee', agent);
|
||||||
|
this.$store
|
||||||
|
.dispatch('assignAgent', {
|
||||||
|
conversationId: this.currentChat.id,
|
||||||
|
agentId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.showAlert(this.$t('CONVERSATION.CHANGE_AGENT'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
showSelfAssignBanner() {
|
||||||
|
if (this.message !== '' && !this.isOnPrivateNote) {
|
||||||
|
if (!this.assignedAgent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.assignedAgent.id !== this.currentUser.id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
enterToSendEnabled() {
|
enterToSendEnabled() {
|
||||||
return !!this.uiSettings.enter_to_send_enabled;
|
return !!this.uiSettings.enter_to_send_enabled;
|
||||||
},
|
},
|
||||||
|
@ -372,6 +416,29 @@ export default {
|
||||||
toggleEnterToSend(enterToSendEnabled) {
|
toggleEnterToSend(enterToSendEnabled) {
|
||||||
this.updateUISettings({ enter_to_send_enabled: enterToSendEnabled });
|
this.updateUISettings({ enter_to_send_enabled: enterToSendEnabled });
|
||||||
},
|
},
|
||||||
|
onClickSelfAssign() {
|
||||||
|
const {
|
||||||
|
account_id,
|
||||||
|
availability_status,
|
||||||
|
available_name,
|
||||||
|
email,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
avatar_url,
|
||||||
|
} = this.currentUser;
|
||||||
|
const selfAssign = {
|
||||||
|
account_id,
|
||||||
|
availability_status,
|
||||||
|
available_name,
|
||||||
|
email,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
thumbnail: avatar_url,
|
||||||
|
};
|
||||||
|
this.assignedAgent = selfAssign;
|
||||||
|
},
|
||||||
async sendMessage() {
|
async sendMessage() {
|
||||||
if (this.isReplyButtonDisabled) {
|
if (this.isReplyButtonDisabled) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
"LOADING_CONVERSATIONS": "Loading Conversations",
|
"LOADING_CONVERSATIONS": "Loading Conversations",
|
||||||
"CANNOT_REPLY": "You cannot reply due to",
|
"CANNOT_REPLY": "You cannot reply due to",
|
||||||
"24_HOURS_WINDOW": "24 hour message window restriction",
|
"24_HOURS_WINDOW": "24 hour message window restriction",
|
||||||
|
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
|
||||||
|
"ASSIGN_TO_ME": "Assign to me",
|
||||||
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
|
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
|
||||||
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
|
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
|
||||||
"SELECT_A_TWEET_TO_REPLY": "Please select a tweet to reply to.",
|
"SELECT_A_TWEET_TO_REPLY": "Please select a tweet to reply to.",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue