mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 12:08:01 +00:00
feat: Show errors for message bubbles in dashboard (#3585)
This commit is contained in:
committed by
GitHub
parent
6fe5484119
commit
83655f4ca4
@@ -1,4 +1,4 @@
|
|||||||
version: "2"
|
version: '2'
|
||||||
plugins:
|
plugins:
|
||||||
rubocop:
|
rubocop:
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -17,30 +17,30 @@ checks:
|
|||||||
method-count:
|
method-count:
|
||||||
enabled: true
|
enabled: true
|
||||||
config:
|
config:
|
||||||
threshold: 30
|
threshold: 32
|
||||||
file-lines:
|
file-lines:
|
||||||
enabled: true
|
enabled: true
|
||||||
config:
|
config:
|
||||||
threshold: 300
|
threshold: 300
|
||||||
exclude_patterns:
|
exclude_patterns:
|
||||||
- "spec/"
|
- 'spec/'
|
||||||
- "**/specs/"
|
- '**/specs/'
|
||||||
- "db/*"
|
- 'db/*'
|
||||||
- "bin/**/*"
|
- 'bin/**/*'
|
||||||
- "db/**/*"
|
- 'db/**/*'
|
||||||
- "config/**/*"
|
- 'config/**/*'
|
||||||
- "public/**/*"
|
- 'public/**/*'
|
||||||
- "vendor/**/*"
|
- 'vendor/**/*'
|
||||||
- "node_modules/**/*"
|
- 'node_modules/**/*'
|
||||||
- "lib/tasks/auto_annotate_models.rake"
|
- 'lib/tasks/auto_annotate_models.rake'
|
||||||
- "app/test-matchers.js"
|
- 'app/test-matchers.js'
|
||||||
- "docs/*"
|
- 'docs/*'
|
||||||
- "**/*.md"
|
- '**/*.md'
|
||||||
- "**/*.yml"
|
- '**/*.yml'
|
||||||
- "app/javascript/dashboard/i18n/locale"
|
- 'app/javascript/dashboard/i18n/locale'
|
||||||
- "**/*.stories.js"
|
- '**/*.stories.js'
|
||||||
- "stories/"
|
- 'stories/'
|
||||||
- "app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js"
|
- 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js'
|
||||||
- "app/javascript/shared/constants/countries.js"
|
- 'app/javascript/shared/constants/countries.js'
|
||||||
- "app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js"
|
- 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js'
|
||||||
- "app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js"
|
- 'app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js'
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export default {
|
|||||||
this.isExpanded ? 'expanded' : '',
|
this.isExpanded ? 'expanded' : '',
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
withTextIconSize() {
|
iconSize() {
|
||||||
switch (this.size) {
|
switch (this.size) {
|
||||||
case 'tiny':
|
case 'tiny':
|
||||||
return 12;
|
return 12;
|
||||||
@@ -101,26 +101,6 @@ export default {
|
|||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
withoutTextIconSize() {
|
|
||||||
switch (this.size) {
|
|
||||||
case 'tiny':
|
|
||||||
return 14;
|
|
||||||
case 'small':
|
|
||||||
return 16;
|
|
||||||
case 'medium':
|
|
||||||
return 18;
|
|
||||||
case 'large':
|
|
||||||
return 20;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 18;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
iconSize() {
|
|
||||||
return this.hasOnlyIcon
|
|
||||||
? this.withoutTextIconSize
|
|
||||||
: this.withTextIconSize;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleClick(evt) {
|
handleClick(evt) {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span v-else-if="lastMessageInChat.attachments">
|
<span v-else-if="lastMessageInChat.attachments">
|
||||||
<fluent-icon
|
<fluent-icon
|
||||||
|
v-if="attachmentIcon"
|
||||||
size="16"
|
size="16"
|
||||||
class="message--attachment-icon"
|
class="message--attachment-icon"
|
||||||
:icon="attachmentIcon"
|
:icon="attachmentIcon"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<li v-if="hasAttachments || data.content" :class="alignBubble">
|
<li v-if="hasAttachments || data.content" :class="alignBubble">
|
||||||
<div :class="wrapClass">
|
<div :class="wrapClass">
|
||||||
<div v-tooltip.top-start="sentByMessage" :class="bubbleClass">
|
<div v-tooltip.top-start="messageToolTip" :class="bubbleClass">
|
||||||
<bubble-mail-head
|
<bubble-mail-head
|
||||||
:email-attributes="contentAttributes.email"
|
:email-attributes="contentAttributes.email"
|
||||||
:cc="emailHeadAttributes.cc"
|
:cc="emailHeadAttributes.cc"
|
||||||
@@ -73,8 +73,18 @@
|
|||||||
{{ sender.name }}
|
{{ sender.name }}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
<div v-if="isFailed" class="message-failed--alert">
|
||||||
|
<woot-button
|
||||||
|
v-tooltip.top-end="$t('CONVERSATION.TRY_AGAIN')"
|
||||||
|
size="tiny"
|
||||||
|
color-scheme="alert"
|
||||||
|
variant="clear"
|
||||||
|
icon="arrow-clockwise"
|
||||||
|
@click="retrySendMessage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-menu-wrap">
|
<div v-if="shouldShowContextMenu" class="context-menu-wrap">
|
||||||
<context-menu
|
<context-menu
|
||||||
v-if="isBubble && !isMessageDeleted"
|
v-if="isBubble && !isMessageDeleted"
|
||||||
:is-open="showContextMenu"
|
:is-open="showContextMenu"
|
||||||
@@ -248,10 +258,13 @@ export default {
|
|||||||
hasText() {
|
hasText() {
|
||||||
return !!this.data.content;
|
return !!this.data.content;
|
||||||
},
|
},
|
||||||
sentByMessage() {
|
messageToolTip() {
|
||||||
if (this.isMessageDeleted) {
|
if (this.isMessageDeleted) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (this.isFailed) {
|
||||||
|
return this.$t(`CONVERSATION.SEND_FAILED`);
|
||||||
|
}
|
||||||
const { sender } = this;
|
const { sender } = this;
|
||||||
return this.data.message_type === 1 && !isEmptyObject(sender)
|
return this.data.message_type === 1 && !isEmptyObject(sender)
|
||||||
? {
|
? {
|
||||||
@@ -265,6 +278,7 @@ export default {
|
|||||||
wrap: this.isBubble,
|
wrap: this.isBubble,
|
||||||
'activity-wrap': !this.isBubble,
|
'activity-wrap': !this.isBubble,
|
||||||
'is-pending': this.isPending,
|
'is-pending': this.isPending,
|
||||||
|
'is-failed': this.isFailed,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
bubbleClass() {
|
bubbleClass() {
|
||||||
@@ -275,19 +289,30 @@ export default {
|
|||||||
'is-video': this.hasMediaAttachment('video'),
|
'is-video': this.hasMediaAttachment('video'),
|
||||||
'is-text': this.hasText,
|
'is-text': this.hasText,
|
||||||
'is-from-bot': this.isSentByBot,
|
'is-from-bot': this.isSentByBot,
|
||||||
|
'is-failed': this.isFailed,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
isPending() {
|
isPending() {
|
||||||
return this.data.status === MESSAGE_STATUS.PROGRESS;
|
return this.data.status === MESSAGE_STATUS.PROGRESS;
|
||||||
},
|
},
|
||||||
|
isFailed() {
|
||||||
|
return this.data.status === MESSAGE_STATUS.FAILED;
|
||||||
|
},
|
||||||
isSentByBot() {
|
isSentByBot() {
|
||||||
if (this.isPending) return false;
|
if (this.isPending || this.isFailed) return false;
|
||||||
return !this.sender.type || this.sender.type === 'agent_bot';
|
return !this.sender.type || this.sender.type === 'agent_bot';
|
||||||
},
|
},
|
||||||
contextMenuPosition() {
|
contextMenuPosition() {
|
||||||
const { message_type: messageType } = this.data;
|
const { message_type: messageType } = this.data;
|
||||||
return messageType ? 'right' : 'left';
|
return messageType ? 'right' : 'left';
|
||||||
},
|
},
|
||||||
|
shouldShowContextMenu() {
|
||||||
|
return !(this.isFailed || this.isPending);
|
||||||
|
},
|
||||||
|
errorMessage() {
|
||||||
|
const { meta } = this.data;
|
||||||
|
return meta ? meta.error : '';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
data() {
|
data() {
|
||||||
@@ -327,6 +352,9 @@ export default {
|
|||||||
this.showAlert(this.$t('CONTACT_PANEL.COPY_SUCCESSFUL'));
|
this.showAlert(this.$t('CONTACT_PANEL.COPY_SUCCESSFUL'));
|
||||||
this.showContextMenu = false;
|
this.showContextMenu = false;
|
||||||
},
|
},
|
||||||
|
async retrySendMessage() {
|
||||||
|
await this.$store.dispatch('sendMessageWithData', this.data);
|
||||||
|
},
|
||||||
onImageLoadError() {
|
onImageLoadError() {
|
||||||
this.hasImageError = true;
|
this.hasImageError = true;
|
||||||
},
|
},
|
||||||
@@ -396,6 +424,14 @@ export default {
|
|||||||
color: var(--v-50);
|
color: var(--v-50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-failed {
|
||||||
|
background: var(--r-200);
|
||||||
|
|
||||||
|
.message-text--metadata .time {
|
||||||
|
color: var(--r-50);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-pending {
|
&.is-pending {
|
||||||
@@ -426,6 +462,13 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-failed--alert {
|
||||||
|
color: var(--r-900);
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: right;
|
||||||
|
margin-top: var(--space-smaller) var(--space-smaller) 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.button--delete-message {
|
.button--delete-message {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
@@ -451,6 +494,17 @@ li.right .context-menu-wrap {
|
|||||||
li.right {
|
li.right {
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.wrap.is-pending {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap.is-failed {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
align-items: flex-end;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-context-menu {
|
.has-context-menu {
|
||||||
|
|||||||
@@ -369,7 +369,10 @@ export default {
|
|||||||
const messagePayload = this.getMessagePayload(newMessage);
|
const messagePayload = this.getMessagePayload(newMessage);
|
||||||
this.clearMessage();
|
this.clearMessage();
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('sendMessage', messagePayload);
|
await this.$store.dispatch(
|
||||||
|
'createPendingMessageAndSend',
|
||||||
|
messagePayload
|
||||||
|
);
|
||||||
this.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
this.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
|
|||||||
@@ -90,6 +90,8 @@
|
|||||||
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit",
|
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit",
|
||||||
"MESSAGE_ERROR": "Unable to send this message, please try again later",
|
"MESSAGE_ERROR": "Unable to send this message, please try again later",
|
||||||
"SENT_BY": "Sent by:",
|
"SENT_BY": "Sent by:",
|
||||||
|
"SEND_FAILED": "Couldn't send message! Try again",
|
||||||
|
"TRY_AGAIN": "retry",
|
||||||
"ASSIGNMENT": {
|
"ASSIGNMENT": {
|
||||||
"SELECT_AGENT": "Select Agent",
|
"SELECT_AGENT": "Select Agent",
|
||||||
"REMOVE": "Remove",
|
"REMOVE": "Remove",
|
||||||
|
|||||||
@@ -158,17 +158,33 @@ const actions = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
sendMessage: async ({ commit }, data) => {
|
createPendingMessageAndSend: async ({ dispatch }, data) => {
|
||||||
// eslint-disable-next-line no-useless-catch
|
const pendingMessage = createPendingMessage(data);
|
||||||
|
dispatch('sendMessageWithData', pendingMessage);
|
||||||
|
},
|
||||||
|
|
||||||
|
sendMessageWithData: async ({ commit }, pendingMessage) => {
|
||||||
try {
|
try {
|
||||||
const pendingMessage = createPendingMessage(data);
|
commit(types.ADD_MESSAGE, {
|
||||||
commit(types.ADD_MESSAGE, pendingMessage);
|
...pendingMessage,
|
||||||
|
status: MESSAGE_STATUS.PROGRESS,
|
||||||
|
});
|
||||||
const response = await MessageApi.create(pendingMessage);
|
const response = await MessageApi.create(pendingMessage);
|
||||||
commit(types.ADD_MESSAGE, {
|
commit(types.ADD_MESSAGE, {
|
||||||
...response.data,
|
...response.data,
|
||||||
status: MESSAGE_STATUS.SENT,
|
status: MESSAGE_STATUS.SENT,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const errorMessage = error.response
|
||||||
|
? error.response.data.error
|
||||||
|
: undefined;
|
||||||
|
commit(types.ADD_MESSAGE, {
|
||||||
|
...pendingMessage,
|
||||||
|
meta: {
|
||||||
|
error: errorMessage,
|
||||||
|
},
|
||||||
|
status: MESSAGE_STATUS.FAILED,
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"alert-outline": "M12 1.996a7.49 7.49 0 0 1 7.496 7.25l.004.25v4.097l1.38 3.156a1.25 1.25 0 0 1-1.145 1.75L15 18.502a3 3 0 0 1-5.995.177L9 18.499H4.275a1.251 1.251 0 0 1-1.147-1.747L4.5 13.594V9.496c0-4.155 3.352-7.5 7.5-7.5ZM13.5 18.5l-3 .002a1.5 1.5 0 0 0 2.993.145l.006-.147ZM12 3.496c-3.32 0-6 2.674-6 6v4.41L4.656 17h14.697L18 13.907V9.509l-.004-.225A5.988 5.988 0 0 0 12 3.496Z",
|
"alert-outline": "M12 1.996a7.49 7.49 0 0 1 7.496 7.25l.004.25v4.097l1.38 3.156a1.25 1.25 0 0 1-1.145 1.75L15 18.502a3 3 0 0 1-5.995.177L9 18.499H4.275a1.251 1.251 0 0 1-1.147-1.747L4.5 13.594V9.496c0-4.155 3.352-7.5 7.5-7.5ZM13.5 18.5l-3 .002a1.5 1.5 0 0 0 2.993.145l.006-.147ZM12 3.496c-3.32 0-6 2.674-6 6v4.41L4.656 17h14.697L18 13.907V9.509l-.004-.225A5.988 5.988 0 0 0 12 3.496Z",
|
||||||
"arrow-chevron-left-outline": "M15 17.898c0 1.074-1.265 1.648-2.073.941l-6.31-5.522a1.75 1.75 0 0 1 0-2.634l6.31-5.522c.808-.707 2.073-.133 2.073.941v11.796Z",
|
"arrow-chevron-left-outline": "M15 17.898c0 1.074-1.265 1.648-2.073.941l-6.31-5.522a1.75 1.75 0 0 1 0-2.634l6.31-5.522c.808-.707 2.073-.133 2.073.941v11.796Z",
|
||||||
"arrow-chevron-right-outline": "M9 17.898c0 1.074 1.265 1.648 2.073.941l6.31-5.522a1.75 1.75 0 0 0 0-2.634l-6.31-5.522C10.265 4.454 9 5.028 9 6.102v11.796Z",
|
"arrow-chevron-right-outline": "M9 17.898c0 1.074 1.265 1.648 2.073.941l6.31-5.522a1.75 1.75 0 0 0 0-2.634l-6.31-5.522C10.265 4.454 9 5.028 9 6.102v11.796Z",
|
||||||
|
"arrow-clockwise-outline": "M12 4.75a7.25 7.25 0 1 0 7.201 6.406c-.068-.588.358-1.156.95-1.156.515 0 .968.358 1.03.87a9.25 9.25 0 1 1-3.432-6.116V4.25a1 1 0 1 1 2.001 0v2.698l.034.052h-.034v.25a1 1 0 0 1-1 1h-3a1 1 0 1 1 0-2h.666A7.219 7.219 0 0 0 12 4.75Z",
|
||||||
"arrow-download-outline": "M18.25 20.5a.75.75 0 1 1 0 1.5l-13 .004a.75.75 0 1 1 0-1.5l13-.004ZM11.648 2.012l.102-.007a.75.75 0 0 1 .743.648l.007.102-.001 13.685 3.722-3.72a.75.75 0 0 1 .976-.073l.085.073a.75.75 0 0 1 .072.976l-.073.084-4.997 4.997a.75.75 0 0 1-.976.073l-.085-.073-5.003-4.996a.75.75 0 0 1 .976-1.134l.084.072 3.719 3.714L11 2.755a.75.75 0 0 1 .648-.743l.102-.007-.102.007Z",
|
"arrow-download-outline": "M18.25 20.5a.75.75 0 1 1 0 1.5l-13 .004a.75.75 0 1 1 0-1.5l13-.004ZM11.648 2.012l.102-.007a.75.75 0 0 1 .743.648l.007.102-.001 13.685 3.722-3.72a.75.75 0 0 1 .976-.073l.085.073a.75.75 0 0 1 .072.976l-.073.084-4.997 4.997a.75.75 0 0 1-.976.073l-.085-.073-5.003-4.996a.75.75 0 0 1 .976-1.134l.084.072 3.719 3.714L11 2.755a.75.75 0 0 1 .648-.743l.102-.007-.102.007Z",
|
||||||
"arrow-redo-outline": "M19.25 2a.75.75 0 0 0-.743.648l-.007.102v5.69l-4.574-4.56a6.41 6.41 0 0 0-8.878-.179l-.186.18a6.41 6.41 0 0 0 0 9.063l8.845 8.84a.75.75 0 0 0 1.06-1.062l-8.845-8.838a4.91 4.91 0 0 1 6.766-7.112l.178.17L17.438 9.5H11.75a.75.75 0 0 0-.743.648L11 10.25c0 .38.282.694.648.743l.102.007h7.5a.75.75 0 0 0 .743-.648L20 10.25v-7.5a.75.75 0 0 0-.75-.75Z",
|
"arrow-redo-outline": "M19.25 2a.75.75 0 0 0-.743.648l-.007.102v5.69l-4.574-4.56a6.41 6.41 0 0 0-8.878-.179l-.186.18a6.41 6.41 0 0 0 0 9.063l8.845 8.84a.75.75 0 0 0 1.06-1.062l-8.845-8.838a4.91 4.91 0 0 1 6.766-7.112l.178.17L17.438 9.5H11.75a.75.75 0 0 0-.743.648L11 10.25c0 .38.282.694.648.743l.102.007h7.5a.75.75 0 0 0 .743-.648L20 10.25v-7.5a.75.75 0 0 0-.75-.75Z",
|
||||||
"arrow-reply-outline": "M9.277 16.221a.75.75 0 0 1-1.061 1.06l-4.997-5.003a.75.75 0 0 1 0-1.06L8.217 6.22a.75.75 0 0 1 1.061 1.06L5.557 11h7.842c1.595 0 2.81.242 3.889.764l.246.126a6.203 6.203 0 0 1 2.576 2.576c.61 1.14.89 2.418.89 4.135a.75.75 0 0 1-1.5 0c0-1.484-.228-2.52-.713-3.428a4.702 4.702 0 0 0-1.96-1.96c-.838-.448-1.786-.676-3.094-.709L13.4 12.5H5.562l3.715 3.721Z",
|
"arrow-reply-outline": "M9.277 16.221a.75.75 0 0 1-1.061 1.06l-4.997-5.003a.75.75 0 0 1 0-1.06L8.217 6.22a.75.75 0 0 1 1.061 1.06L5.557 11h7.842c1.595 0 2.81.242 3.889.764l.246.126a6.203 6.203 0 0 1 2.576 2.576c.61 1.14.89 2.418.89 4.135a.75.75 0 0 1-1.5 0c0-1.484-.228-2.52-.713-3.428a4.702 4.702 0 0 0-1.96-1.96c-.838-.448-1.786-.676-3.094-.709L13.4 12.5H5.562l3.715 3.721Z",
|
||||||
|
|||||||
Reference in New Issue
Block a user