mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 12:08:01 +00:00
feat: Ability to send attachment in new conversation (#7698)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
@@ -152,6 +152,10 @@
|
|||||||
|
|
||||||
&.is-image {
|
&.is-image {
|
||||||
@apply rounded-lg;
|
@apply rounded-lg;
|
||||||
|
|
||||||
|
.message__mail-head {
|
||||||
|
@apply px-4 py-2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,6 @@
|
|||||||
@apply flex items-center justify-center bg-modal dark:bg-modal z-[9990] h-full left-0 fixed top-0 w-full;
|
@apply flex items-center justify-center bg-modal dark:bg-modal z-[9990] h-full left-0 fixed top-0 w-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal--close {
|
|
||||||
@apply absolute right-2 rtl:right-[unset] rtl:left-2 top-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-top-bar {
|
.page-top-bar {
|
||||||
@apply px-8 pt-9 pb-0;
|
@apply px-8 pt-9 pb-0;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
icon="dismiss"
|
icon="dismiss"
|
||||||
variant="clear"
|
variant="clear"
|
||||||
class="modal--close"
|
class="absolute ltr:right-2 rtl:left-2 top-2 z-10"
|
||||||
@click="close"
|
@click="close"
|
||||||
/>
|
/>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="preview-item__wrap flex overflow-auto max-h-[12.5rem]">
|
||||||
class="preview-item__wrap flex flex-col overflow-auto mt-4 max-h-[12.5rem]"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
v-for="(attachment, index) in attachments"
|
v-for="(attachment, index) in attachments"
|
||||||
:key="attachment.id"
|
:key="attachment.id"
|
||||||
class="preview-item flex p-1 bg-slate-50 dark:bg-slate-800 rounded-md w-[15rem] mb-1"
|
class="preview-item flex items-center p-1 bg-slate-50 dark:bg-slate-800 gap-1 rounded-md w-[15rem] mb-1"
|
||||||
>
|
>
|
||||||
<div class="max-w-[4rem] flex-shrink-0 w-6 flex items-center">
|
<div class="max-w-[4rem] flex-shrink-0 w-6 flex items-center">
|
||||||
<img
|
<img
|
||||||
@@ -17,7 +15,7 @@
|
|||||||
📄
|
📄
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="max-w-[60%] min-w-[50%] overflow-hidden text-ellipsis ml-2">
|
<div class="max-w-[60%] min-w-[50%] overflow-hidden text-ellipsis">
|
||||||
<span
|
<span
|
||||||
class="h-4 overflow-hidden text-sm font-medium text-ellipsis whitespace-nowrap"
|
class="h-4 overflow-hidden text-sm font-medium text-ellipsis whitespace-nowrap"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
<file-upload
|
<file-upload
|
||||||
ref="upload"
|
ref="upload"
|
||||||
v-tooltip.top-end="$t('CONVERSATION.REPLYBOX.TIP_ATTACH_ICON')"
|
v-tooltip.top-end="$t('CONVERSATION.REPLYBOX.TIP_ATTACH_ICON')"
|
||||||
|
input-id="conversationAttachment"
|
||||||
:size="4096 * 4096"
|
:size="4096 * 4096"
|
||||||
:accept="allowedFileTypes"
|
:accept="allowedFileTypes"
|
||||||
:multiple="enableMultipleFileUpload"
|
:multiple="enableMultipleFileUpload"
|
||||||
:drop="true"
|
:drop="enableDragAndDrop"
|
||||||
:drop-directory="false"
|
:drop-directory="false"
|
||||||
:data="{
|
:data="{
|
||||||
direct_upload_url: '/rails/active_storage/direct_uploads',
|
direct_upload_url: '/rails/active_storage/direct_uploads',
|
||||||
@@ -100,9 +101,9 @@
|
|||||||
<transition name="modal-fade">
|
<transition name="modal-fade">
|
||||||
<div
|
<div
|
||||||
v-show="$refs.upload && $refs.upload.dropActive"
|
v-show="$refs.upload && $refs.upload.dropActive"
|
||||||
class="modal-mask"
|
class="flex items-center justify-center gap-2 fixed left-0 right-0 top-0 bottom-0 w-full h-full z-20 text-slate-600 dark:text-slate-200 bg-white_transparent dark:bg-black_transparent flex-col"
|
||||||
>
|
>
|
||||||
<fluent-icon icon="cloud-backup" />
|
<fluent-icon icon="cloud-backup" size="40" />
|
||||||
<h4 class="page-sub-title text-slate-600 dark:text-slate-200">
|
<h4 class="page-sub-title text-slate-600 dark:text-slate-200">
|
||||||
{{ $t('CONVERSATION.REPLYBOX.DRAG_DROP') }}
|
{{ $t('CONVERSATION.REPLYBOX.DRAG_DROP') }}
|
||||||
</h4>
|
</h4>
|
||||||
@@ -228,6 +229,10 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
newConversationModalActive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
@@ -274,6 +279,9 @@ export default {
|
|||||||
}
|
}
|
||||||
return ALLOWED_FILE_TYPES;
|
return ALLOWED_FILE_TYPES;
|
||||||
},
|
},
|
||||||
|
enableDragAndDrop() {
|
||||||
|
return !this.newConversationModalActive;
|
||||||
|
},
|
||||||
audioRecorderPlayStopIcon() {
|
audioRecorderPlayStopIcon() {
|
||||||
switch (this.recordingAudioState) {
|
switch (this.recordingAudioState) {
|
||||||
// playing paused recording stopped inactive destroyed
|
// playing paused recording stopped inactive destroyed
|
||||||
@@ -346,12 +354,4 @@ export default {
|
|||||||
@apply dark:bg-slate-800 bg-slate-100;
|
@apply dark:bg-slate-800 bg-slate-100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-mask {
|
|
||||||
@apply text-slate-600 dark:text-slate-200 bg-white_transparent dark:bg-black_transparent flex-col;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
@apply text-[5rem];
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="hasAttachments" class="attachment-preview-box" @paste="onPaste">
|
<div v-if="hasAttachments" class="attachment-preview-box" @paste="onPaste">
|
||||||
<attachment-preview
|
<attachment-preview
|
||||||
|
class="mt-4 flex-col"
|
||||||
:attachments="attachedFiles"
|
:attachments="attachedFiles"
|
||||||
:remove-attachment="removeAttachment"
|
:remove-attachment="removeAttachment"
|
||||||
/>
|
/>
|
||||||
@@ -124,6 +125,7 @@
|
|||||||
:toggle-audio-recorder="toggleAudioRecorder"
|
:toggle-audio-recorder="toggleAudioRecorder"
|
||||||
:toggle-emoji-picker="toggleEmojiPicker"
|
:toggle-emoji-picker="toggleEmojiPicker"
|
||||||
:message="message"
|
:message="message"
|
||||||
|
:new-conversation-modal-active="newConversationModalActive"
|
||||||
@selectWhatsappTemplate="openWhatsappTemplateModal"
|
@selectWhatsappTemplate="openWhatsappTemplateModal"
|
||||||
@toggle-editor="toggleRichContentEditor"
|
@toggle-editor="toggleRichContentEditor"
|
||||||
@replace-text="replaceText"
|
@replace-text="replaceText"
|
||||||
@@ -160,12 +162,7 @@ import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/cons
|
|||||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
||||||
import WootAudioRecorder from 'dashboard/components/widgets/WootWriter/AudioRecorder';
|
import WootAudioRecorder from 'dashboard/components/widgets/WootWriter/AudioRecorder';
|
||||||
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
||||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
import { AUDIO_FORMATS } from 'shared/constants/messages';
|
||||||
import {
|
|
||||||
MAXIMUM_FILE_UPLOAD_SIZE,
|
|
||||||
MAXIMUM_FILE_UPLOAD_SIZE_TWILIO_SMS_CHANNEL,
|
|
||||||
AUDIO_FORMATS,
|
|
||||||
} from 'shared/constants/messages';
|
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
import {
|
import {
|
||||||
getMessageVariables,
|
getMessageVariables,
|
||||||
@@ -177,13 +174,13 @@ import { buildHotKeys } from 'shared/helpers/KeyboardHelpers';
|
|||||||
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
||||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
import { DirectUpload } from 'activestorage';
|
|
||||||
import { frontendURL } from '../../../helper/URLHelper';
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
import { trimContent, debounce } from '@chatwoot/utils';
|
import { trimContent, debounce } from '@chatwoot/utils';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
||||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||||
|
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
||||||
|
|
||||||
const EmojiInput = () => import('shared/components/emoji/EmojiInput');
|
const EmojiInput = () => import('shared/components/emoji/EmojiInput');
|
||||||
|
|
||||||
@@ -208,6 +205,7 @@ export default {
|
|||||||
alertMixin,
|
alertMixin,
|
||||||
messageFormatterMixin,
|
messageFormatterMixin,
|
||||||
rtlMixin,
|
rtlMixin,
|
||||||
|
fileUploadMixin,
|
||||||
],
|
],
|
||||||
props: {
|
props: {
|
||||||
selectedTweet: {
|
selectedTweet: {
|
||||||
@@ -247,6 +245,7 @@ export default {
|
|||||||
showUserMentions: false,
|
showUserMentions: false,
|
||||||
showCannedMenu: false,
|
showCannedMenu: false,
|
||||||
showVariablesMenu: false,
|
showVariablesMenu: false,
|
||||||
|
newConversationModalActive: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -571,11 +570,25 @@ export default {
|
|||||||
500,
|
500,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// A hacky fix to solve the drag and drop
|
||||||
|
// Is showing on top of new conversation modal drag and drop
|
||||||
|
// TODO need to find a better solution
|
||||||
|
bus.$on(
|
||||||
|
BUS_EVENTS.NEW_CONVERSATION_MODAL,
|
||||||
|
this.onNewConversationModalActive
|
||||||
|
);
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
document.removeEventListener('paste', this.onPaste);
|
document.removeEventListener('paste', this.onPaste);
|
||||||
document.removeEventListener('keydown', this.handleKeyEvents);
|
document.removeEventListener('keydown', this.handleKeyEvents);
|
||||||
},
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
bus.$off(
|
||||||
|
BUS_EVENTS.NEW_CONVERSATION_MODAL,
|
||||||
|
this.onNewConversationModalActive
|
||||||
|
);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleRichContentEditor() {
|
toggleRichContentEditor() {
|
||||||
this.updateUISettings({
|
this.updateUISettings({
|
||||||
@@ -904,67 +917,6 @@ export default {
|
|||||||
isPrivate,
|
isPrivate,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onFileUpload(file) {
|
|
||||||
if (this.globalConfig.directUploadsEnabled) {
|
|
||||||
this.onDirectFileUpload(file);
|
|
||||||
} else {
|
|
||||||
this.onIndirectFileUpload(file);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDirectFileUpload(file) {
|
|
||||||
const MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE = this.isATwilioSMSChannel
|
|
||||||
? MAXIMUM_FILE_UPLOAD_SIZE_TWILIO_SMS_CHANNEL
|
|
||||||
: MAXIMUM_FILE_UPLOAD_SIZE;
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (checkFileSizeLimit(file, MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE)) {
|
|
||||||
const upload = new DirectUpload(
|
|
||||||
file.file,
|
|
||||||
`/api/v1/accounts/${this.accountId}/conversations/${this.currentChat.id}/direct_uploads`,
|
|
||||||
{
|
|
||||||
directUploadWillCreateBlobWithXHR: xhr => {
|
|
||||||
xhr.setRequestHeader(
|
|
||||||
'api_access_token',
|
|
||||||
this.currentUser.access_token
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
upload.create((error, blob) => {
|
|
||||||
if (error) {
|
|
||||||
this.showAlert(error);
|
|
||||||
} else {
|
|
||||||
this.attachFile({ file, blob });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.showAlert(
|
|
||||||
this.$t('CONVERSATION.FILE_SIZE_LIMIT', {
|
|
||||||
MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onIndirectFileUpload(file) {
|
|
||||||
const MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE = this.isATwilioSMSChannel
|
|
||||||
? MAXIMUM_FILE_UPLOAD_SIZE_TWILIO_SMS_CHANNEL
|
|
||||||
: MAXIMUM_FILE_UPLOAD_SIZE;
|
|
||||||
if (!file) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (checkFileSizeLimit(file, MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE)) {
|
|
||||||
this.attachFile({ file });
|
|
||||||
} else {
|
|
||||||
this.showAlert(
|
|
||||||
this.$t('CONVERSATION.FILE_SIZE_LIMIT', {
|
|
||||||
MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
attachFile({ blob, file }) {
|
attachFile({ blob, file }) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file.file);
|
reader.readAsDataURL(file.file);
|
||||||
@@ -1096,6 +1048,14 @@ export default {
|
|||||||
this.bccEmails = bcc.join(', ');
|
this.bccEmails = bcc.join(', ');
|
||||||
this.toEmails = to.join(', ');
|
this.toEmails = to.join(', ');
|
||||||
},
|
},
|
||||||
|
onNewConversationModalActive(isActive) {
|
||||||
|
// Issue is if the new conversation modal is open and we drag and drop the file
|
||||||
|
// then the file is not getting attached to the new conversation modal
|
||||||
|
// and it is getting attached to the current conversation reply box
|
||||||
|
// so to fix this we are removing the drag and drop event listener from the current conversation reply box
|
||||||
|
// When new conversation modal is open
|
||||||
|
this.newConversationModalActive = isActive;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -204,6 +204,10 @@
|
|||||||
"PLACEHOLDER": "Write your message here",
|
"PLACEHOLDER": "Write your message here",
|
||||||
"ERROR": "Message can't be empty"
|
"ERROR": "Message can't be empty"
|
||||||
},
|
},
|
||||||
|
"ATTACHMENTS": {
|
||||||
|
"SELECT": "Choose files",
|
||||||
|
"HELP_TEXT": "Drag and drop files here or choose files to attach"
|
||||||
|
},
|
||||||
"SUBMIT": "Send message",
|
"SUBMIT": "Send message",
|
||||||
"CANCEL": "Cancel",
|
"CANCEL": "Cancel",
|
||||||
"SUCCESS_MESSAGE": "Message sent!",
|
"SUCCESS_MESSAGE": "Message sent!",
|
||||||
|
|||||||
72
app/javascript/dashboard/mixins/fileUploadMixin.js
Normal file
72
app/javascript/dashboard/mixins/fileUploadMixin.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import {
|
||||||
|
MAXIMUM_FILE_UPLOAD_SIZE,
|
||||||
|
MAXIMUM_FILE_UPLOAD_SIZE_TWILIO_SMS_CHANNEL,
|
||||||
|
} from 'shared/constants/messages';
|
||||||
|
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||||
|
import { DirectUpload } from 'activestorage';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
onFileUpload(file) {
|
||||||
|
if (this.globalConfig.directUploadsEnabled) {
|
||||||
|
this.onDirectFileUpload(file);
|
||||||
|
} else {
|
||||||
|
this.onIndirectFileUpload(file);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDirectFileUpload(file) {
|
||||||
|
const MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE = this.isATwilioSMSChannel
|
||||||
|
? MAXIMUM_FILE_UPLOAD_SIZE_TWILIO_SMS_CHANNEL
|
||||||
|
: MAXIMUM_FILE_UPLOAD_SIZE;
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (checkFileSizeLimit(file, MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE)) {
|
||||||
|
const upload = new DirectUpload(
|
||||||
|
file.file,
|
||||||
|
`/api/v1/accounts/${this.accountId}/conversations/${this.currentChat.id}/direct_uploads`,
|
||||||
|
{
|
||||||
|
directUploadWillCreateBlobWithXHR: xhr => {
|
||||||
|
xhr.setRequestHeader(
|
||||||
|
'api_access_token',
|
||||||
|
this.currentUser.access_token
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
upload.create((error, blob) => {
|
||||||
|
if (error) {
|
||||||
|
this.showAlert(error);
|
||||||
|
} else {
|
||||||
|
this.attachFile({ file, blob });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.showAlert(
|
||||||
|
this.$t('CONVERSATION.FILE_SIZE_LIMIT', {
|
||||||
|
MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onIndirectFileUpload(file) {
|
||||||
|
const MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE = this.isATwilioSMSChannel
|
||||||
|
? MAXIMUM_FILE_UPLOAD_SIZE_TWILIO_SMS_CHANNEL
|
||||||
|
: MAXIMUM_FILE_UPLOAD_SIZE;
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (checkFileSizeLimit(file, MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE)) {
|
||||||
|
this.attachFile({ file });
|
||||||
|
} else {
|
||||||
|
this.showAlert(
|
||||||
|
this.$t('CONVERSATION.FILE_SIZE_LIMIT', {
|
||||||
|
MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
jest.mock('shared/helpers/FileHelper', () => ({
|
||||||
|
checkFileSizeLimit: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('activestorage', () => ({
|
||||||
|
DirectUpload: jest.fn().mockImplementation(() => ({
|
||||||
|
create: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('FileUploadMixin', () => {
|
||||||
|
let vm;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vm = new Vue(fileUploadMixin);
|
||||||
|
vm.isATwilioSMSChannel = false;
|
||||||
|
vm.globalConfig = {
|
||||||
|
directUploadsEnabled: true,
|
||||||
|
};
|
||||||
|
vm.accountId = 123;
|
||||||
|
vm.currentChat = {
|
||||||
|
id: 456,
|
||||||
|
};
|
||||||
|
vm.currentUser = {
|
||||||
|
access_token: 'token',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onDirectFileUpload when direct uploads are enabled', () => {
|
||||||
|
vm.onDirectFileUpload = jest.fn();
|
||||||
|
vm.onFileUpload({});
|
||||||
|
expect(vm.onDirectFileUpload).toHaveBeenCalledWith({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onIndirectFileUpload when direct uploads are disabled', () => {
|
||||||
|
vm.globalConfig.directUploadsEnabled = false;
|
||||||
|
vm.onIndirectFileUpload = jest.fn();
|
||||||
|
vm.onFileUpload({});
|
||||||
|
expect(vm.onIndirectFileUpload).toHaveBeenCalledWith({});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -268,6 +268,7 @@ export default {
|
|||||||
},
|
},
|
||||||
toggleConversationModal() {
|
toggleConversationModal() {
|
||||||
this.showConversationModal = !this.showConversationModal;
|
this.showConversationModal = !this.showConversationModal;
|
||||||
|
bus.$emit('newConversationModal', this.showConversationModal);
|
||||||
},
|
},
|
||||||
toggleDeleteModal() {
|
toggleDeleteModal() {
|
||||||
this.showDeleteModal = !this.showDeleteModal;
|
this.showDeleteModal = !this.showDeleteModal;
|
||||||
|
|||||||
@@ -11,7 +11,10 @@
|
|||||||
<label>
|
<label>
|
||||||
{{ $t('NEW_CONVERSATION.FORM.INBOX.LABEL') }}
|
{{ $t('NEW_CONVERSATION.FORM.INBOX.LABEL') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="multiselect-wrap--small">
|
<div
|
||||||
|
class="multiselect-wrap--small"
|
||||||
|
:class="{ 'has-multi-select-error': $v.targetInbox.$error }"
|
||||||
|
>
|
||||||
<multiselect
|
<multiselect
|
||||||
v-model="targetInbox"
|
v-model="targetInbox"
|
||||||
track-by="id"
|
track-by="id"
|
||||||
@@ -88,7 +91,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="w-full">
|
<div
|
||||||
|
class="w-full"
|
||||||
|
:class="{
|
||||||
|
'flex flex-col-reverse': hasWhatsappTemplates,
|
||||||
|
'gap-3': hasWhatsappTemplates && !hasAttachments,
|
||||||
|
}"
|
||||||
|
>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<canned-response
|
<canned-response
|
||||||
v-if="showCannedResponseMenu && hasSlashCommand"
|
v-if="showCannedResponseMenu && hasSlashCommand"
|
||||||
@@ -139,9 +148,52 @@
|
|||||||
{{ $t('NEW_CONVERSATION.FORM.MESSAGE.ERROR') }}
|
{{ $t('NEW_CONVERSATION.FORM.MESSAGE.ERROR') }}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<file-upload
|
||||||
|
ref="uploadAttachment"
|
||||||
|
input-id="newConversationAttachment"
|
||||||
|
:size="4096 * 4096"
|
||||||
|
:accept="allowedFileTypes"
|
||||||
|
:multiple="true"
|
||||||
|
:drop="true"
|
||||||
|
:drop-directory="false"
|
||||||
|
:data="{
|
||||||
|
direct_upload_url: '/rails/active_storage/direct_uploads',
|
||||||
|
direct_upload: true,
|
||||||
|
}"
|
||||||
|
@input-file="onFileUpload"
|
||||||
|
>
|
||||||
|
<woot-button
|
||||||
|
class-names="button--upload"
|
||||||
|
icon="attach"
|
||||||
|
emoji="📎"
|
||||||
|
color-scheme="secondary"
|
||||||
|
variant="smooth"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ $t('NEW_CONVERSATION.FORM.ATTACHMENTS.SELECT') }}
|
||||||
|
</woot-button>
|
||||||
|
<span
|
||||||
|
class="text-slate-500 ltr:ml-1 rtl:mr-1 font-medium text-xs dark:text-slate-400"
|
||||||
|
>
|
||||||
|
{{ $t('NEW_CONVERSATION.FORM.ATTACHMENTS.HELP_TEXT') }}
|
||||||
|
</span>
|
||||||
|
</file-upload>
|
||||||
|
<div
|
||||||
|
v-if="hasAttachments"
|
||||||
|
class="max-h-20 overflow-y-auto mb-4 mt-1.5"
|
||||||
|
>
|
||||||
|
<attachment-preview
|
||||||
|
class="[&>.preview-item]:dark:bg-slate-700 flex-row flex-wrap gap-x-3 gap-y-1"
|
||||||
|
:attachments="attachedFiles"
|
||||||
|
:remove-attachment="removeAttachment"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="!hasWhatsappTemplates"
|
v-if="!hasWhatsappTemplates"
|
||||||
class="flex flex-row justify-end gap-2 py-2 px-0 w-full"
|
class="flex flex-row justify-end gap-2 py-2 px-0 w-full"
|
||||||
@@ -153,6 +205,18 @@
|
|||||||
{{ $t('NEW_CONVERSATION.FORM.SUBMIT') }}
|
{{ $t('NEW_CONVERSATION.FORM.SUBMIT') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<transition name="modal-fade">
|
||||||
|
<div
|
||||||
|
v-show="$refs.uploadAttachment && $refs.uploadAttachment.dropActive"
|
||||||
|
class="flex top-0 bottom-0 z-30 gap-2 right-0 left-0 items-center justify-center flex-col absolute w-full h-full bg-white/80 dark:bg-slate-700/80"
|
||||||
|
>
|
||||||
|
<fluent-icon icon="cloud-backup" size="40" />
|
||||||
|
<h4 class="page-sub-title text-slate-600 dark:text-slate-200">
|
||||||
|
{{ $t('CONVERSATION.REPLYBOX.DRAG_DROP') }}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -169,6 +233,11 @@ import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
|||||||
import { ExceptionWithMessage } from 'shared/helpers/CustomErrors';
|
import { ExceptionWithMessage } from 'shared/helpers/CustomErrors';
|
||||||
import { getInboxSource } from 'dashboard/helper/inbox';
|
import { getInboxSource } from 'dashboard/helper/inbox';
|
||||||
import { required, requiredIf } from 'vuelidate/lib/validators';
|
import { required, requiredIf } from 'vuelidate/lib/validators';
|
||||||
|
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||||
|
import FileUpload from 'vue-upload-component';
|
||||||
|
import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview';
|
||||||
|
import { ALLOWED_FILE_TYPES } from 'shared/constants/messages';
|
||||||
|
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -178,8 +247,10 @@ export default {
|
|||||||
CannedResponse,
|
CannedResponse,
|
||||||
WhatsappTemplates,
|
WhatsappTemplates,
|
||||||
InboxDropdownItem,
|
InboxDropdownItem,
|
||||||
|
FileUpload,
|
||||||
|
AttachmentPreview,
|
||||||
},
|
},
|
||||||
mixins: [alertMixin],
|
mixins: [alertMixin, inboxMixin, fileUploadMixin],
|
||||||
props: {
|
props: {
|
||||||
contact: {
|
contact: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -201,6 +272,7 @@ export default {
|
|||||||
ccEmails: '',
|
ccEmails: '',
|
||||||
targetInbox: {},
|
targetInbox: {},
|
||||||
whatsappTemplateSelected: false,
|
whatsappTemplateSelected: false,
|
||||||
|
attachedFiles: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
validations: {
|
validations: {
|
||||||
@@ -219,8 +291,9 @@ export default {
|
|||||||
uiFlags: 'contacts/getUIFlags',
|
uiFlags: 'contacts/getUIFlags',
|
||||||
conversationsUiFlags: 'contactConversations/getUIFlags',
|
conversationsUiFlags: 'contactConversations/getUIFlags',
|
||||||
currentUser: 'getCurrentUser',
|
currentUser: 'getCurrentUser',
|
||||||
|
globalConfig: 'globalConfig/get',
|
||||||
}),
|
}),
|
||||||
emailMessagePayload() {
|
newMessagePayload() {
|
||||||
const payload = {
|
const payload = {
|
||||||
inboxId: this.targetInbox.id,
|
inboxId: this.targetInbox.id,
|
||||||
sourceId: this.targetInbox.sourceId,
|
sourceId: this.targetInbox.sourceId,
|
||||||
@@ -229,6 +302,12 @@ export default {
|
|||||||
mailSubject: this.subject,
|
mailSubject: this.subject,
|
||||||
assigneeId: this.currentUser.id,
|
assigneeId: this.currentUser.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.attachedFiles && this.attachedFiles.length) {
|
||||||
|
payload.files = [];
|
||||||
|
this.setAttachmentPayload(payload);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.ccEmails) {
|
if (this.ccEmails) {
|
||||||
payload.message.cc_emails = this.ccEmails;
|
payload.message.cc_emails = this.ccEmails;
|
||||||
}
|
}
|
||||||
@@ -284,6 +363,15 @@ export default {
|
|||||||
hasWhatsappTemplates() {
|
hasWhatsappTemplates() {
|
||||||
return !!this.selectedInbox.inbox?.message_templates;
|
return !!this.selectedInbox.inbox?.message_templates;
|
||||||
},
|
},
|
||||||
|
hasAttachments() {
|
||||||
|
return this.attachedFiles.length;
|
||||||
|
},
|
||||||
|
inbox() {
|
||||||
|
return this.targetInbox;
|
||||||
|
},
|
||||||
|
allowedFileTypes() {
|
||||||
|
return ALLOWED_FILE_TYPES;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
message(value) {
|
message(value) {
|
||||||
@@ -300,6 +388,33 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setAttachmentPayload(payload) {
|
||||||
|
this.attachedFiles.forEach(attachment => {
|
||||||
|
if (this.globalConfig.directUploadsEnabled) {
|
||||||
|
payload.files.push(attachment.blobSignedId);
|
||||||
|
} else {
|
||||||
|
payload.files.push(attachment.resource.file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
attachFile({ blob, file }) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file.file);
|
||||||
|
reader.onloadend = () => {
|
||||||
|
this.attachedFiles.push({
|
||||||
|
currentChatId: this.contact.id,
|
||||||
|
resource: blob || file,
|
||||||
|
isPrivate: this.isPrivate,
|
||||||
|
thumb: reader.result,
|
||||||
|
blobSignedId: blob ? blob.signed_id : undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
removeAttachment(itemIndex) {
|
||||||
|
this.attachedFiles = this.attachedFiles.filter(
|
||||||
|
(item, index) => itemIndex !== index
|
||||||
|
);
|
||||||
|
},
|
||||||
onCancel() {
|
onCancel() {
|
||||||
this.$emit('cancel');
|
this.$emit('cancel');
|
||||||
},
|
},
|
||||||
@@ -320,6 +435,10 @@ export default {
|
|||||||
message: { content, template_params: templateParams },
|
message: { content, template_params: templateParams },
|
||||||
assigneeId: this.currentUser.id,
|
assigneeId: this.currentUser.id,
|
||||||
};
|
};
|
||||||
|
if (this.attachedFiles && this.attachedFiles.length) {
|
||||||
|
payload.files = [];
|
||||||
|
this.setAttachmentPayload(payload);
|
||||||
|
}
|
||||||
return payload;
|
return payload;
|
||||||
},
|
},
|
||||||
onFormSubmit() {
|
onFormSubmit() {
|
||||||
@@ -327,7 +446,7 @@ export default {
|
|||||||
if (this.$v.$invalid) {
|
if (this.$v.$invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.createConversation(this.emailMessagePayload);
|
this.createConversation(this.newMessagePayload);
|
||||||
},
|
},
|
||||||
async createConversation(payload) {
|
async createConversation(payload) {
|
||||||
try {
|
try {
|
||||||
@@ -389,6 +508,18 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-uploads {
|
||||||
|
@apply text-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiselect-wrap--small.has-multi-select-error {
|
||||||
|
::v-deep {
|
||||||
|
.multiselect__tags {
|
||||||
|
@apply border-red-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep {
|
::v-deep {
|
||||||
.mention--box {
|
.mention--box {
|
||||||
@apply left-0 m-auto right-0 top-auto h-fit;
|
@apply left-0 m-auto right-0 top-auto h-fit;
|
||||||
|
|||||||
@@ -3,6 +3,34 @@ import * as types from '../mutation-types';
|
|||||||
import ContactAPI from '../../api/contacts';
|
import ContactAPI from '../../api/contacts';
|
||||||
import ConversationApi from '../../api/conversations';
|
import ConversationApi from '../../api/conversations';
|
||||||
|
|
||||||
|
export const createMessagePayload = (payload, message) => {
|
||||||
|
const { content, cc_emails, bcc_emails } = message;
|
||||||
|
payload.append('message[content]', content);
|
||||||
|
if (cc_emails) payload.append('message[cc_emails]', cc_emails);
|
||||||
|
if (bcc_emails) payload.append('message[bcc_emails]', bcc_emails);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createConversationPayload = ({ params, contactId, files }) => {
|
||||||
|
const { inboxId, message, sourceId, mailSubject, assigneeId } = params;
|
||||||
|
const payload = new FormData();
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
createMessagePayload(payload, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
files.forEach(file => payload.append('message[attachments][]', file));
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.append('inbox_id', inboxId);
|
||||||
|
payload.append('contact_id', contactId);
|
||||||
|
payload.append('source_id', sourceId);
|
||||||
|
payload.append('additional_attributes[mail_subject]', mailSubject);
|
||||||
|
payload.append('assignee_id', assigneeId);
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
};
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
records: {},
|
records: {},
|
||||||
uiFlags: {
|
uiFlags: {
|
||||||
@@ -24,29 +52,17 @@ export const actions = {
|
|||||||
commit(types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG, {
|
commit(types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG, {
|
||||||
isCreating: true,
|
isCreating: true,
|
||||||
});
|
});
|
||||||
const {
|
const { contactId, files } = params;
|
||||||
inboxId,
|
|
||||||
message,
|
|
||||||
contactId,
|
|
||||||
sourceId,
|
|
||||||
mailSubject,
|
|
||||||
assigneeId,
|
|
||||||
} = params;
|
|
||||||
try {
|
try {
|
||||||
const { data } = await ConversationApi.create({
|
const payload = createConversationPayload({ params, contactId, files });
|
||||||
inbox_id: inboxId,
|
|
||||||
contact_id: contactId,
|
const { data } = await ConversationApi.create(payload);
|
||||||
source_id: sourceId,
|
|
||||||
additional_attributes: {
|
|
||||||
mail_subject: mailSubject,
|
|
||||||
},
|
|
||||||
message,
|
|
||||||
assignee_id: assigneeId,
|
|
||||||
});
|
|
||||||
commit(types.default.ADD_CONTACT_CONVERSATION, {
|
commit(types.default.ADD_CONTACT_CONVERSATION, {
|
||||||
id: contactId,
|
id: contactId,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { actions } from '../../contactConversations';
|
import {
|
||||||
|
actions,
|
||||||
|
createMessagePayload,
|
||||||
|
createConversationPayload,
|
||||||
|
} from '../../contactConversations';
|
||||||
import * as types from '../../../mutation-types';
|
import * as types from '../../../mutation-types';
|
||||||
import conversationList from './fixtures';
|
import conversationList from './fixtures';
|
||||||
|
|
||||||
@@ -49,6 +53,35 @@ describe('#actions', () => {
|
|||||||
contactId: 4,
|
contactId: 4,
|
||||||
sourceId: 5,
|
sourceId: 5,
|
||||||
mailSubject: 'Mail Subject',
|
mailSubject: 'Mail Subject',
|
||||||
|
assigneeId: 6,
|
||||||
|
files: [],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG, { isCreating: true }],
|
||||||
|
|
||||||
|
[
|
||||||
|
types.default.ADD_CONTACT_CONVERSATION,
|
||||||
|
{ id: 4, data: conversationList[0] },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG,
|
||||||
|
{ isCreating: false },
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct actions with files if API is success', async () => {
|
||||||
|
axios.post.mockResolvedValue({ data: conversationList[0] });
|
||||||
|
await actions.create(
|
||||||
|
{ commit },
|
||||||
|
{
|
||||||
|
inboxId: 1,
|
||||||
|
message: { content: 'hi' },
|
||||||
|
contactId: 4,
|
||||||
|
sourceId: 5,
|
||||||
|
assigneeId: 6,
|
||||||
|
mailSubject: 'Mail Subject',
|
||||||
|
files: [new File([], 'file1')],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
@@ -74,6 +107,7 @@ describe('#actions', () => {
|
|||||||
inboxId: 1,
|
inboxId: 1,
|
||||||
message: { content: 'hi' },
|
message: { content: 'hi' },
|
||||||
contactId: 4,
|
contactId: 4,
|
||||||
|
assigneeId: 6,
|
||||||
sourceId: 5,
|
sourceId: 5,
|
||||||
mailSubject: 'Mail Subject',
|
mailSubject: 'Mail Subject',
|
||||||
}
|
}
|
||||||
@@ -87,5 +121,121 @@ describe('#actions', () => {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
it('sends correct actions with files if API is error', async () => {
|
||||||
|
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
actions.create(
|
||||||
|
{ commit },
|
||||||
|
{
|
||||||
|
inboxId: 1,
|
||||||
|
message: { content: 'hi' },
|
||||||
|
contactId: 4,
|
||||||
|
assigneeId: 6,
|
||||||
|
sourceId: 5,
|
||||||
|
mailSubject: 'Mail Subject',
|
||||||
|
files: [new File([], 'file1')],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).rejects.toThrow(Error);
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG, { isCreating: true }],
|
||||||
|
[
|
||||||
|
types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG,
|
||||||
|
{ isCreating: false },
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createMessagePayload', () => {
|
||||||
|
it('creates message payload with cc and bcc emails', () => {
|
||||||
|
const payload = new FormData();
|
||||||
|
const message = {
|
||||||
|
content: 'Test message content',
|
||||||
|
cc_emails: 'cc@example.com',
|
||||||
|
bcc_emails: 'bcc@example.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
createMessagePayload(payload, message);
|
||||||
|
|
||||||
|
expect(payload.get('message[content]')).toBe(message.content);
|
||||||
|
expect(payload.get('message[cc_emails]')).toBe(message.cc_emails);
|
||||||
|
expect(payload.get('message[bcc_emails]')).toBe(message.bcc_emails);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates message payload without cc and bcc emails', () => {
|
||||||
|
const payload = new FormData();
|
||||||
|
const message = {
|
||||||
|
content: 'Test message content',
|
||||||
|
};
|
||||||
|
|
||||||
|
createMessagePayload(payload, message);
|
||||||
|
|
||||||
|
expect(payload.get('message[content]')).toBe(message.content);
|
||||||
|
expect(payload.get('message[cc_emails]')).toBeNull();
|
||||||
|
expect(payload.get('message[bcc_emails]')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createConversationPayload', () => {
|
||||||
|
it('creates conversation payload with message and attachments', () => {
|
||||||
|
const options = {
|
||||||
|
params: {
|
||||||
|
inboxId: '1',
|
||||||
|
message: {
|
||||||
|
content: 'Test message content',
|
||||||
|
},
|
||||||
|
sourceId: '12',
|
||||||
|
mailSubject: 'Test Subject',
|
||||||
|
assigneeId: '123',
|
||||||
|
},
|
||||||
|
contactId: '23',
|
||||||
|
files: ['file1.pdf', 'file2.jpg'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const payload = createConversationPayload(options);
|
||||||
|
|
||||||
|
expect(payload.get('message[content]')).toBe(
|
||||||
|
options.params.message.content
|
||||||
|
);
|
||||||
|
expect(payload.get('inbox_id')).toBe(options.params.inboxId);
|
||||||
|
expect(payload.get('contact_id')).toBe(options.contactId);
|
||||||
|
expect(payload.get('source_id')).toBe(options.params.sourceId);
|
||||||
|
expect(payload.get('additional_attributes[mail_subject]')).toBe(
|
||||||
|
options.params.mailSubject
|
||||||
|
);
|
||||||
|
expect(payload.get('assignee_id')).toBe(options.params.assigneeId);
|
||||||
|
expect(payload.getAll('message[attachments][]')).toEqual(options.files);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates conversation payload with message and without attachments', () => {
|
||||||
|
const options = {
|
||||||
|
params: {
|
||||||
|
inboxId: '1',
|
||||||
|
message: {
|
||||||
|
content: 'Test message content',
|
||||||
|
},
|
||||||
|
sourceId: '12',
|
||||||
|
mailSubject: 'Test Subject',
|
||||||
|
assigneeId: '123',
|
||||||
|
},
|
||||||
|
contactId: '23',
|
||||||
|
};
|
||||||
|
|
||||||
|
const payload = createConversationPayload(options);
|
||||||
|
|
||||||
|
expect(payload.get('message[content]')).toBe(
|
||||||
|
options.params.message.content
|
||||||
|
);
|
||||||
|
expect(payload.get('inbox_id')).toBe(options.params.inboxId);
|
||||||
|
expect(payload.get('contact_id')).toBe(options.contactId);
|
||||||
|
expect(payload.get('source_id')).toBe(options.params.sourceId);
|
||||||
|
expect(payload.get('additional_attributes[mail_subject]')).toBe(
|
||||||
|
options.params.mailSubject
|
||||||
|
);
|
||||||
|
expect(payload.get('assignee_id')).toBe(options.params.assigneeId);
|
||||||
|
expect(payload.getAll('message[attachments][]')).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ export const BUS_EVENTS = {
|
|||||||
ON_MESSAGE_LIST_SCROLL: 'ON_MESSAGE_LIST_SCROLL',
|
ON_MESSAGE_LIST_SCROLL: 'ON_MESSAGE_LIST_SCROLL',
|
||||||
WEBSOCKET_DISCONNECT: 'WEBSOCKET_DISCONNECT',
|
WEBSOCKET_DISCONNECT: 'WEBSOCKET_DISCONNECT',
|
||||||
SHOW_TOAST: 'newToastMessage',
|
SHOW_TOAST: 'newToastMessage',
|
||||||
|
NEW_CONVERSATION_MODAL: 'newConversationModal',
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user