Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
994519bb2e | ||
|
|
31e7663258 | ||
|
|
3d24fa2a2d | ||
|
|
00ea51abd4 | ||
|
|
f803cb98c7 | ||
|
|
755556e2d7 | ||
|
|
a0fbfbe38f | ||
|
|
b998101a4c | ||
|
|
6577b40f90 |
@@ -3,7 +3,6 @@ import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||
import FileUpload from 'vue-upload-component';
|
||||
import * as ActiveStorage from 'activestorage';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import {
|
||||
ALLOWED_FILE_TYPES,
|
||||
@@ -18,7 +17,6 @@ import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
name: 'ReplyBottomPanel',
|
||||
components: { FileUpload, VideoCallButton, AIAssistanceButton },
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
@@ -36,13 +34,6 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// inbox prop is used in /mixins/inboxMixin,
|
||||
// remove this props when refactoring to composable if not needed
|
||||
// eslint-disable-next-line vue/no-unused-properties
|
||||
inbox: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
showFileUpload: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -111,6 +102,22 @@ export default {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isALineChannel: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isATwilioWhatsAppChannel: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isAWebWidgetInbox: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isAPIInbox: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { setSignatureFlagForInbox, fetchSignatureFlagFromUISettings } =
|
||||
@@ -211,7 +218,6 @@ export default {
|
||||
return !this.isOnPrivateNote;
|
||||
},
|
||||
sendWithSignature() {
|
||||
// channelType is sourced from inboxMixin
|
||||
return this.fetchSignatureFlagFromUISettings(this.channelType);
|
||||
},
|
||||
signatureToggleTooltip() {
|
||||
|
||||
@@ -6,7 +6,7 @@ import MessagePreview from './MessagePreview.vue';
|
||||
import router from '../../../routes';
|
||||
import { frontendURL, conversationUrl } from '../../../helper/URLHelper';
|
||||
import InboxName from '../InboxName.vue';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { useInbox } from 'shared/composables/useInbox';
|
||||
import ConversationContextMenu from './contextMenu/Index.vue';
|
||||
import TimeAgo from 'dashboard/components/ui/TimeAgo.vue';
|
||||
import CardLabels from './conversationCardComponents/CardLabels.vue';
|
||||
@@ -24,7 +24,6 @@ export default {
|
||||
PriorityMark,
|
||||
SLACardLabel,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
activeLabel: {
|
||||
type: String,
|
||||
@@ -67,6 +66,14 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { inbox_id: inboxId } = props.chat;
|
||||
const { inboxBadge, inbox } = useInbox({ inboxId });
|
||||
return {
|
||||
inboxBadge,
|
||||
inbox,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hovered: false,
|
||||
@@ -121,12 +128,6 @@ export default {
|
||||
return getLastMessage(this.chat);
|
||||
},
|
||||
|
||||
inbox() {
|
||||
const { inbox_id: inboxId } = this.chat;
|
||||
const stateInbox = this.$store.getters['inboxes/getInbox'](inboxId);
|
||||
return stateInbox;
|
||||
},
|
||||
|
||||
showInboxName() {
|
||||
return (
|
||||
!this.hideInboxName &&
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script>
|
||||
import { ref } from 'vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||
import agentMixin from '../../../mixins/agentMixin.js';
|
||||
import BackButton from '../BackButton.vue';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { useInbox } from 'shared/composables/useInbox';
|
||||
import InboxName from '../InboxName.vue';
|
||||
import MoreActions from './MoreActions.vue';
|
||||
import Thumbnail from '../Thumbnail.vue';
|
||||
@@ -22,7 +24,7 @@ export default {
|
||||
SLACardLabel,
|
||||
Linear,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
mixins: [agentMixin],
|
||||
props: {
|
||||
chat: {
|
||||
type: Object,
|
||||
@@ -42,12 +44,20 @@ export default {
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const conversationHeaderActionsRef = ref(null);
|
||||
const { inbox, inboxBadge } = useInbox({ inboxId: props.chat.inbox_id });
|
||||
const keyboardEvents = {
|
||||
'Alt+KeyO': {
|
||||
action: () => emit('contactPanelToggle'),
|
||||
},
|
||||
};
|
||||
useKeyboardEvents(keyboardEvents);
|
||||
useKeyboardEvents(keyboardEvents, conversationHeaderActionsRef);
|
||||
|
||||
return {
|
||||
conversationHeaderActionsRef,
|
||||
inbox,
|
||||
inboxBadge,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
@@ -102,10 +112,6 @@ export default {
|
||||
: this.$t('CONVERSATION.HEADER.OPEN')
|
||||
} ${this.$t('CONVERSATION.HEADER.DETAILS')}`;
|
||||
},
|
||||
inbox() {
|
||||
const { inbox_id: inboxId } = this.chat;
|
||||
return this.$store.getters['inboxes/getInbox'](inboxId);
|
||||
},
|
||||
hasMultipleInboxes() {
|
||||
return this.$store.getters['inboxes/getInboxes'].length > 1;
|
||||
},
|
||||
@@ -175,6 +181,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref="conversationHeaderActionsRef"
|
||||
class="flex items-center gap-2 overflow-hidden text-xs conversation--header--actions text-ellipsis whitespace-nowrap"
|
||||
>
|
||||
<InboxName v-if="hasMultipleInboxes" :inbox="inbox" />
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ref } from 'vue';
|
||||
// composable
|
||||
import { useConfig } from 'dashboard/composables/useConfig';
|
||||
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||
import { useAI } from 'dashboard/composables/useAI';
|
||||
|
||||
// components
|
||||
import ReplyBox from './ReplyBox.vue';
|
||||
@@ -15,7 +14,9 @@ import Banner from 'dashboard/components/ui/Banner.vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
// mixins
|
||||
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
|
||||
import { useInbox, INBOX_FEATURES } from 'shared/composables/useInbox';
|
||||
|
||||
import aiMixin from 'dashboard/mixins/aiMixin';
|
||||
|
||||
// utils
|
||||
import { getTypingUsersText } from '../../../helper/commons';
|
||||
@@ -40,7 +41,7 @@ export default {
|
||||
Banner,
|
||||
ConversationLabelSuggestion,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
mixins: [aiMixin],
|
||||
props: {
|
||||
isContactPanelOpen: {
|
||||
type: Boolean,
|
||||
@@ -52,6 +53,17 @@ export default {
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const {
|
||||
inbox,
|
||||
isAWhatsAppChannel,
|
||||
isAPIInbox,
|
||||
is360DialogWhatsAppChannel,
|
||||
isAWebWidgetInbox,
|
||||
isAFacebookInbox,
|
||||
isAnEmailChannel,
|
||||
inboxHasFeature,
|
||||
} = useInbox({ getCurrentChat: true });
|
||||
const conversationFooterRef = ref(null);
|
||||
const isPopOutReplyBox = ref(false);
|
||||
const { isEnterprise } = useConfig();
|
||||
|
||||
@@ -69,24 +81,22 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
useKeyboardEvents(keyboardEvents);
|
||||
|
||||
const {
|
||||
isAIIntegrationEnabled,
|
||||
isLabelSuggestionFeatureEnabled,
|
||||
fetchIntegrationsIfRequired,
|
||||
fetchLabelSuggestions,
|
||||
} = useAI();
|
||||
useKeyboardEvents(keyboardEvents, conversationFooterRef);
|
||||
|
||||
return {
|
||||
isEnterprise,
|
||||
conversationFooterRef,
|
||||
isPopOutReplyBox,
|
||||
closePopOutReplyBox,
|
||||
showPopOutReplyBox,
|
||||
isAIIntegrationEnabled,
|
||||
isLabelSuggestionFeatureEnabled,
|
||||
fetchIntegrationsIfRequired,
|
||||
fetchLabelSuggestions,
|
||||
inbox,
|
||||
isAWhatsAppChannel,
|
||||
isAPIInbox,
|
||||
is360DialogWhatsAppChannel,
|
||||
isAWebWidgetInbox,
|
||||
isAFacebookInbox,
|
||||
isAnEmailChannel,
|
||||
inboxHasFeature,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
@@ -121,9 +131,6 @@ export default {
|
||||
inboxId() {
|
||||
return this.currentChat.inbox_id;
|
||||
},
|
||||
inbox() {
|
||||
return this.$store.getters['inboxes/getInbox'](this.inboxId);
|
||||
},
|
||||
typingUsersList() {
|
||||
const userList = this.$store.getters[
|
||||
'conversationTypingStatus/getUserList'
|
||||
@@ -527,6 +534,7 @@ export default {
|
||||
/>
|
||||
</ul>
|
||||
<div
|
||||
ref="conversationFooterRef"
|
||||
class="conversation-footer"
|
||||
:class="{ 'modal-mask': isPopOutReplyBox }"
|
||||
>
|
||||
|
||||
@@ -17,6 +17,7 @@ import Banner from 'dashboard/components/ui/Banner.vue';
|
||||
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||
import WootAudioRecorder from 'dashboard/components/widgets/WootWriter/AudioRecorder.vue';
|
||||
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
||||
import { AUDIO_FORMATS } from 'shared/constants/messages';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
import {
|
||||
@@ -26,7 +27,7 @@ import {
|
||||
} from '@chatwoot/utils';
|
||||
import WhatsappTemplates from './WhatsappTemplates/Modal.vue';
|
||||
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
||||
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
|
||||
import { useInbox, INBOX_FEATURES } from 'shared/composables/useInbox';
|
||||
import { trimContent, debounce } from '@chatwoot/utils';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||
@@ -60,7 +61,7 @@ export default {
|
||||
MessageSignatureMissingAlert,
|
||||
ArticleSearchPopover,
|
||||
},
|
||||
mixins: [inboxMixin, fileUploadMixin, keyboardEventListenerMixins],
|
||||
mixins: [messageFormatterMixin, fileUploadMixin, keyboardEventListenerMixins],
|
||||
props: {
|
||||
popoutReplyBox: {
|
||||
type: Boolean,
|
||||
@@ -68,6 +69,22 @@ export default {
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const {
|
||||
is360DialogWhatsAppChannel,
|
||||
isAPIInbox,
|
||||
isAWhatsAppChannel,
|
||||
isATwitterInbox,
|
||||
isAFacebookInbox,
|
||||
isASmsInbox,
|
||||
isAWebWidgetInbox,
|
||||
isAnEmailChannel,
|
||||
isATelegramChannel,
|
||||
isALineChannel,
|
||||
channelType,
|
||||
isAWhatsAppCloudChannel,
|
||||
isATwilioWhatsAppChannel,
|
||||
inbox,
|
||||
} = useInbox({ getCurrentChat: true });
|
||||
const {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
@@ -80,6 +97,20 @@ export default {
|
||||
updateUISettings,
|
||||
isEditorHotKeyEnabled,
|
||||
fetchSignatureFlagFromUISettings,
|
||||
is360DialogWhatsAppChannel,
|
||||
isAPIInbox,
|
||||
isAWhatsAppChannel,
|
||||
isATwitterInbox,
|
||||
isAFacebookInbox,
|
||||
isASmsInbox,
|
||||
isAWebWidgetInbox,
|
||||
isAnEmailChannel,
|
||||
isATelegramChannel,
|
||||
isALineChannel,
|
||||
channelType,
|
||||
isAWhatsAppCloudChannel,
|
||||
isATwilioWhatsAppChannel,
|
||||
inbox,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
@@ -189,9 +220,6 @@ export default {
|
||||
inboxId() {
|
||||
return this.currentChat.inbox_id;
|
||||
},
|
||||
inbox() {
|
||||
return this.$store.getters['inboxes/getInbox'](this.inboxId);
|
||||
},
|
||||
messagePlaceHolder() {
|
||||
return this.isPrivate
|
||||
? this.$t('CONVERSATION.FOOTER.PRIVATE_MSG_INPUT')
|
||||
@@ -1202,6 +1230,12 @@ export default {
|
||||
:message="message"
|
||||
:portal-slug="connectedPortalSlug"
|
||||
:new-conversation-modal-active="newConversationModalActive"
|
||||
:is-a-line-channel="isALineChannel"
|
||||
:is-a-twilio-whatsapp-channel="isATwilioWhatsAppChannel"
|
||||
:is-a-web-widget-inbox="isAWebWidgetInbox"
|
||||
:is-an-email-channel="isAnEmailChannel"
|
||||
:is-api-inbox="isAPIInbox"
|
||||
:channel-type="channelType"
|
||||
@selectWhatsappTemplate="openWhatsappTemplateModal"
|
||||
@toggleEditor="toggleRichContentEditor"
|
||||
@replaceText="replaceText"
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script>
|
||||
import { MESSAGE_TYPE, MESSAGE_STATUS } from 'shared/constants/messages';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { useInbox } from 'shared/composables/useInbox';
|
||||
import { messageTimestamp } from 'shared/helpers/timeHelper';
|
||||
|
||||
export default {
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
sender: {
|
||||
type: Object,
|
||||
@@ -55,10 +54,33 @@ export default {
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const {
|
||||
inbox,
|
||||
isAWhatsAppChannel,
|
||||
isATwilioChannel,
|
||||
isAFacebookInbox,
|
||||
isASmsInbox,
|
||||
isATelegramChannel,
|
||||
isALineChannel,
|
||||
isAWebWidgetInbox,
|
||||
isAPIInbox,
|
||||
isAnEmailChannel,
|
||||
} = useInbox({ inboxId: props.inboxId });
|
||||
return {
|
||||
inbox,
|
||||
isAWhatsAppChannel,
|
||||
isATwilioChannel,
|
||||
isAFacebookInbox,
|
||||
isASmsInbox,
|
||||
isATelegramChannel,
|
||||
isALineChannel,
|
||||
isAWebWidgetInbox,
|
||||
isAPIInbox,
|
||||
isAnEmailChannel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
inbox() {
|
||||
return this.$store.getters['inboxes/getInbox'](this.inboxId);
|
||||
},
|
||||
isIncoming() {
|
||||
return MESSAGE_TYPE.INCOMING === this.messageType;
|
||||
},
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script>
|
||||
import DyteVideoCall from './integrations/Dyte.vue';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { useInbox } from 'shared/composables/useInbox';
|
||||
|
||||
export default {
|
||||
components: { DyteVideoCall },
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
messageId: {
|
||||
type: [String, Number],
|
||||
@@ -19,14 +18,20 @@ export default {
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { isAPIInbox, isAWebWidgetInbox } = useInbox({
|
||||
inboxId: props.inboxId,
|
||||
});
|
||||
return {
|
||||
isAPIInbox,
|
||||
isAWebWidgetInbox,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showDyteIntegration() {
|
||||
const isEnabledOnTheInbox = this.isAPIInbox || this.isAWebWidgetInbox;
|
||||
return isEnabledOnTheInbox && this.contentAttributes.type === 'dyte';
|
||||
},
|
||||
inbox() {
|
||||
return this.$store.getters['inboxes/getInbox'](this.inboxId);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -9,6 +9,7 @@ const FEATURE_HELP_URLS = {
|
||||
custom_attributes: 'https://chwt.app/hc/custom-attributes',
|
||||
dashboard_apps: 'https://chwt.app/hc/dashboard-apps',
|
||||
help_center: 'https://chwt.app/hc/help-center',
|
||||
inboxes: 'https://chwt.app/hc/inboxes',
|
||||
integrations: 'https://chwt.app/hc/integrations',
|
||||
labels: 'https://chwt.app/hc/labels',
|
||||
macros: 'https://chwt.app/hc/macros',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
||||
import { INBOX_TYPES } from 'shared/composables/useInbox';
|
||||
|
||||
export const getInboxSource = (type, phoneNumber, inbox) => {
|
||||
switch (type) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"INBOX_MGMT": {
|
||||
"HEADER": "Inboxes",
|
||||
"SIDEBAR_TXT": "<p><b>Inbox</b></p> <p> When you connect a website or a facebook Page to Chatwoot, it is called an <b>Inbox</b>. You can have unlimited inboxes in your Chatwoot account. </p><p> Click on <b>Add Inbox</b> to connect a website or a Facebook Page. </p><p> In the Dashboard, you can see all the conversations from all your inboxes in a single place and respond to them under the `Conversations` tab. </p><p> You can also see conversations specific to an inbox by clicking on the inbox name on the left pane of the dashboard. </p>",
|
||||
"DESCRIPTION": "A channel is the mode of communication your customer chooses to interact with you. An inbox is where you manage interactions for a specific channel. It can include communications from various sources such as email, live chat, and social media.",
|
||||
"LEARN_MORE": "Learn more about inboxes",
|
||||
"RECONNECTION_REQUIRED": "Your inbox is disconnected. You won't receive new messages until you reauthorize it.",
|
||||
"CLICK_TO_RECONNECT": "Click here to reconnect.",
|
||||
"LIST": {
|
||||
@@ -745,6 +746,18 @@
|
||||
"MICROSOFT": "Microsoft",
|
||||
"GOOGLE": "Google",
|
||||
"OTHER_PROVIDERS": "Other Providers"
|
||||
},
|
||||
"CHANNELS": {
|
||||
"MESSENGER": "Messenger",
|
||||
"WEB_WIDGET": "Website",
|
||||
"TWITTER_PROFILE": "Twitter",
|
||||
"TWILIO_SMS": "Twilio SMS",
|
||||
"WHATSAPP": "WhatsApp",
|
||||
"SMS": "SMS",
|
||||
"EMAIL": "Email",
|
||||
"TELEGRAM": "Telegram",
|
||||
"LINE": "Line",
|
||||
"API": "API Channel"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -9,12 +9,11 @@ import CannedResponse from 'dashboard/components/widgets/conversation/CannedResp
|
||||
import MessageSignatureMissingAlert from 'dashboard/components/widgets/conversation/MessageSignatureMissingAlert';
|
||||
import InboxDropdownItem from 'dashboard/components/widgets/InboxDropdownItem.vue';
|
||||
import WhatsappTemplates from './WhatsappTemplates.vue';
|
||||
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
||||
import { ExceptionWithMessage } from 'shared/helpers/CustomErrors';
|
||||
import { getInboxSource } from 'dashboard/helper/inbox';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { required, requiredIf } from '@vuelidate/validators';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { useInbox, INBOX_TYPES } from 'shared/composables/useInbox';
|
||||
import FileUpload from 'vue-upload-component';
|
||||
import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview';
|
||||
import { ALLOWED_FILE_TYPES } from 'shared/constants/messages';
|
||||
@@ -36,7 +35,7 @@ export default {
|
||||
AttachmentPreview,
|
||||
MessageSignatureMissingAlert,
|
||||
},
|
||||
mixins: [inboxMixin, fileUploadMixin],
|
||||
mixins: [fileUploadMixin],
|
||||
props: {
|
||||
contact: {
|
||||
type: Object,
|
||||
@@ -47,12 +46,18 @@ export default {
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
setup(props) {
|
||||
const { channelType } = useInbox({ inboxObj: props.targetInbox });
|
||||
const { fetchSignatureFlagFromUISettings, setSignatureFlagForInbox } =
|
||||
useUISettings();
|
||||
const v$ = useVuelidate();
|
||||
|
||||
return { fetchSignatureFlagFromUISettings, setSignatureFlagForInbox, v$ };
|
||||
return {
|
||||
fetchSignatureFlagFromUISettings,
|
||||
setSignatureFlagForInbox,
|
||||
v$,
|
||||
channelType,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,219 +1,177 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
<script setup>
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import { useAdmin } from 'dashboard/composables/useAdmin';
|
||||
import { useAccount } from 'dashboard/composables/useAccount';
|
||||
import Settings from './Settings.vue';
|
||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
import SettingsLayout from '../SettingsLayout.vue';
|
||||
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
import ChannelName from './components/ChannelName.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Settings,
|
||||
},
|
||||
mixins: [globalConfigMixin],
|
||||
setup() {
|
||||
const { isAdmin } = useAdmin();
|
||||
const { accountScopedUrl } = useAccount();
|
||||
return {
|
||||
isAdmin,
|
||||
accountScopedUrl,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: {},
|
||||
showSettings: false,
|
||||
showDeletePopup: false,
|
||||
selectedInbox: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
inboxesList: 'inboxes/getInboxes',
|
||||
globalConfig: 'globalConfig/get',
|
||||
}),
|
||||
// Delete Modal
|
||||
deleteConfirmText() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.YES')} ${
|
||||
this.selectedInbox.name
|
||||
}`;
|
||||
},
|
||||
deleteRejectText() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.NO')} ${
|
||||
this.selectedInbox.name
|
||||
}`;
|
||||
},
|
||||
confirmDeleteMessage() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${
|
||||
this.selectedInbox.name
|
||||
}?`;
|
||||
},
|
||||
confirmPlaceHolderText() {
|
||||
return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.PLACE_HOLDER', {
|
||||
inboxName: this.selectedInbox.name,
|
||||
})}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
twilioChannelName(item) {
|
||||
const { medium = '' } = item;
|
||||
if (medium === 'whatsapp') return 'WhatsApp';
|
||||
return 'Twilio SMS';
|
||||
},
|
||||
openSettings(inbox) {
|
||||
this.showSettings = true;
|
||||
this.selectedInbox = inbox;
|
||||
},
|
||||
closeSettings() {
|
||||
this.showSettings = false;
|
||||
this.selectedInbox = {};
|
||||
},
|
||||
async deleteInbox({ id }) {
|
||||
try {
|
||||
await this.$store.dispatch('inboxes/delete', id);
|
||||
useAlert(this.$t('INBOX_MGMT.DELETE.API.SUCCESS_MESSAGE'));
|
||||
} catch (error) {
|
||||
useAlert(this.$t('INBOX_MGMT.DELETE.API.ERROR_MESSAGE'));
|
||||
}
|
||||
},
|
||||
const getters = useStoreGetters();
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
const { isAdmin } = useAdmin();
|
||||
|
||||
confirmDeletion() {
|
||||
this.deleteInbox(this.selectedInbox);
|
||||
this.closeDelete();
|
||||
},
|
||||
openDelete(inbox) {
|
||||
this.showDeletePopup = true;
|
||||
this.selectedInbox = inbox;
|
||||
},
|
||||
closeDelete() {
|
||||
this.showDeletePopup = false;
|
||||
this.selectedInbox = {};
|
||||
},
|
||||
},
|
||||
const showDeletePopup = ref(false);
|
||||
const selectedInbox = ref({});
|
||||
|
||||
const inboxesList = computed(() => getters['inboxes/getInboxes'].value);
|
||||
const uiFlags = computed(() => getters['inboxes/getUIFlags'].value);
|
||||
|
||||
const deleteConfirmText = computed(
|
||||
() => `${t('INBOX_MGMT.DELETE.CONFIRM.YES')} ${selectedInbox.value.name}`
|
||||
);
|
||||
|
||||
const deleteRejectText = computed(
|
||||
() => `${t('INBOX_MGMT.DELETE.CONFIRM.NO')} ${selectedInbox.value.name}`
|
||||
);
|
||||
|
||||
const confirmDeleteMessage = computed(
|
||||
() => `${t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${selectedInbox.value.name}?`
|
||||
);
|
||||
const confirmPlaceHolderText = computed(
|
||||
() =>
|
||||
`${t('INBOX_MGMT.DELETE.CONFIRM.PLACE_HOLDER', {
|
||||
inboxName: selectedInbox.value.name,
|
||||
})}`
|
||||
);
|
||||
|
||||
const deleteInbox = async ({ id }) => {
|
||||
try {
|
||||
await store.dispatch('inboxes/delete', id);
|
||||
useAlert(t('INBOX_MGMT.DELETE.API.SUCCESS_MESSAGE'));
|
||||
} catch (error) {
|
||||
useAlert(t('INBOX_MGMT.DELETE.API.ERROR_MESSAGE'));
|
||||
}
|
||||
};
|
||||
const closeDelete = () => {
|
||||
showDeletePopup.value = false;
|
||||
selectedInbox.value = {};
|
||||
};
|
||||
|
||||
const confirmDeletion = () => {
|
||||
deleteInbox(selectedInbox.value);
|
||||
closeDelete();
|
||||
};
|
||||
const openDelete = inbox => {
|
||||
showDeletePopup.value = true;
|
||||
selectedInbox.value = inbox;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-1 overflow-auto">
|
||||
<div class="flex flex-row gap-4 p-8">
|
||||
<div class="w-full lg:w-3/5">
|
||||
<p
|
||||
v-if="!inboxesList.length"
|
||||
class="flex flex-col items-center justify-center h-full"
|
||||
>
|
||||
{{ $t('INBOX_MGMT.LIST.404') }}
|
||||
<SettingsLayout
|
||||
:no-records-found="!inboxesList.length"
|
||||
:no-records-message="$t('INBOX_MGMT.LIST.404')"
|
||||
:is-loading="uiFlags.isFetching"
|
||||
>
|
||||
<template #header>
|
||||
<BaseSettingsHeader
|
||||
:title="$t('INBOX_MGMT.HEADER')"
|
||||
:description="$t('INBOX_MGMT.DESCRIPTION')"
|
||||
:link-text="$t('INBOX_MGMT.LEARN_MORE')"
|
||||
feature-name="inboxes"
|
||||
>
|
||||
<template #actions>
|
||||
<router-link
|
||||
v-if="isAdmin"
|
||||
:to="accountScopedUrl('settings/inboxes/new')"
|
||||
class="button nice rounded-md"
|
||||
:to="{ name: 'settings_inbox_new' }"
|
||||
>
|
||||
<fluent-icon icon="add-circle" />
|
||||
{{ $t('SETTINGS.INBOXES.NEW_INBOX') }}
|
||||
</router-link>
|
||||
</p>
|
||||
|
||||
<table v-if="inboxesList.length" class="woot-table">
|
||||
<tbody>
|
||||
<tr v-for="item in inboxesList" :key="item.id">
|
||||
<td>
|
||||
<img
|
||||
v-if="item.avatar_url"
|
||||
class="woot-thumbnail"
|
||||
:src="item.avatar_url"
|
||||
alt="No Page Image"
|
||||
</template>
|
||||
</BaseSettingsHeader>
|
||||
</template>
|
||||
<template #body>
|
||||
<table
|
||||
class="min-w-full overflow-x-auto divide-y divide-slate-75 dark:divide-slate-700"
|
||||
>
|
||||
<tbody
|
||||
class="divide-y divide-slate-25 dark:divide-slate-800 flex-1 text-slate-700 dark:text-slate-100"
|
||||
>
|
||||
<tr v-for="inbox in inboxesList" :key="inbox.id">
|
||||
<td class="py-4 ltr:pr-4 rtl:pl-4">
|
||||
<div class="flex items-center flex-row gap-4">
|
||||
<Thumbnail
|
||||
v-if="inbox.avatar_url"
|
||||
class="bg-black-50 dark:bg-black-800 rounded-full p-2 ring ring-opacity-20 dark:ring-opacity-80 ring-black-100 dark:ring-black-900 border border-slate-100 dark:border-slate-700/50 shadow-sm"
|
||||
:src="inbox.avatar_url"
|
||||
:username="inbox.name"
|
||||
size="48px"
|
||||
/>
|
||||
<img
|
||||
<div
|
||||
v-else
|
||||
class="woot-thumbnail"
|
||||
src="~dashboard/assets/images/flag.svg"
|
||||
alt="No Page Image"
|
||||
/>
|
||||
</td>
|
||||
<!-- Short Code -->
|
||||
<td>
|
||||
<span class="agent-name">{{ item.name }}</span>
|
||||
<span v-if="item.channel_type === 'Channel::FacebookPage'">
|
||||
{{ 'Facebook' }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::WebWidget'">
|
||||
{{ 'Website' }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::TwitterProfile'">
|
||||
{{ 'Twitter' }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::TwilioSms'">
|
||||
{{ twilioChannelName(item) }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::Whatsapp'">
|
||||
{{ 'Whatsapp' }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::Sms'">
|
||||
{{ 'Sms' }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::Email'">
|
||||
{{ 'Email' }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::Telegram'">
|
||||
{{ 'Telegram' }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::Line'">
|
||||
{{ 'Line' }}
|
||||
</span>
|
||||
<span v-if="item.channel_type === 'Channel::Api'">
|
||||
{{ globalConfig.apiChannelName || 'API' }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<td>
|
||||
<div class="button-wrapper">
|
||||
<router-link
|
||||
:to="accountScopedUrl(`settings/inboxes/${item.id}`)"
|
||||
class="w-12 h-12 bg-black-50 dark:bg-black-800 rounded-full p-2 ring ring-opacity-20 dark:ring-opacity-80 ring-black-100 dark:ring-black-900 border border-slate-100 dark:border-slate-700/50 shadow-sm block"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 24 24"
|
||||
class="opacity-80 p-1"
|
||||
>
|
||||
<woot-button
|
||||
v-if="isAdmin"
|
||||
v-tooltip.top="$t('INBOX_MGMT.SETTINGS')"
|
||||
variant="smooth"
|
||||
size="tiny"
|
||||
icon="settings"
|
||||
color-scheme="secondary"
|
||||
class-names="grey-btn"
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M1 12c0-5.185 0-7.778 1.61-9.39C4.223 1 6.816 1 12 1s7.778 0 9.39 1.61C23 4.223 23 6.816 23 12s0 7.778-1.61 9.39C19.777 23 17.184 23 12 23s-7.778 0-9.39-1.61C1 19.777 1 17.184 1 12"
|
||||
opacity=".35"
|
||||
/>
|
||||
</router-link>
|
||||
|
||||
<woot-button
|
||||
v-if="isAdmin"
|
||||
v-tooltip.top="$t('INBOX_MGMT.DELETE.BUTTON_TEXT')"
|
||||
variant="smooth"
|
||||
color-scheme="alert"
|
||||
size="tiny"
|
||||
class-names="grey-btn"
|
||||
:is-loading="loading[item.id]"
|
||||
icon="dismiss-circle"
|
||||
@click="openDelete(item)"
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M2.61 21.389c1.612 1.61 4.205 1.61 9.39 1.61s7.778 0 9.39-1.61c1.492-1.493 1.601-3.829 1.61-8.29h-3.476c-.996 0-1.494 0-1.931.202c-.438.201-.762.58-1.41 1.335l-.666.777c-.648.756-.972 1.134-1.41 1.335s-.935.202-1.93.202h-.353c-.996 0-1.494 0-1.931-.202c-.438-.2-.762-.579-1.41-1.335l-.666-.777c-.648-.756-.972-1.134-1.41-1.335s-.935-.201-1.93-.201H1c.008 4.46.118 6.796 1.61 8.289"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<span class="block font-medium capitalize">
|
||||
{{ inbox.name }}
|
||||
</span>
|
||||
<ChannelName
|
||||
:channel-type="inbox.channel_type"
|
||||
:medium="inbox.medium"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<div class="hidden w-1/3 lg:block">
|
||||
<span
|
||||
v-dompurify-html="
|
||||
useInstallationName(
|
||||
$t('INBOX_MGMT.SIDEBAR_TXT'),
|
||||
globalConfig.installationName
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Settings
|
||||
v-if="showSettings"
|
||||
:show.sync="showSettings"
|
||||
:on-close="closeSettings"
|
||||
:inbox="selectedInbox"
|
||||
/>
|
||||
<td class="py-4">
|
||||
<div class="flex gap-1 justify-end">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'settings_inbox_show',
|
||||
params: { inboxId: inbox.id },
|
||||
}"
|
||||
>
|
||||
<woot-button
|
||||
v-if="isAdmin"
|
||||
v-tooltip.top="$t('INBOX_MGMT.SETTINGS')"
|
||||
variant="smooth"
|
||||
size="tiny"
|
||||
icon="settings"
|
||||
color-scheme="secondary"
|
||||
class-names="grey-btn"
|
||||
/>
|
||||
</router-link>
|
||||
|
||||
<woot-button
|
||||
v-if="isAdmin"
|
||||
v-tooltip.top="$t('INBOX_MGMT.DELETE.BUTTON_TEXT')"
|
||||
variant="smooth"
|
||||
color-scheme="alert"
|
||||
size="tiny"
|
||||
class-names="grey-btn"
|
||||
icon="dismiss-circle"
|
||||
@click="openDelete(inbox)"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<woot-confirm-delete-modal
|
||||
v-if="showDeletePopup"
|
||||
@@ -227,5 +185,5 @@ export default {
|
||||
@onConfirm="confirmDeletion"
|
||||
@onClose="closeDelete"
|
||||
/>
|
||||
</div>
|
||||
</SettingsLayout>
|
||||
</template>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useAlert } from 'dashboard/composables';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import SettingIntroBanner from 'dashboard/components/widgets/SettingIntroBanner.vue';
|
||||
import SettingsSection from '../../../../components/SettingsSection.vue';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { useInbox } from 'shared/composables/useInbox';
|
||||
import FacebookReauthorize from './facebook/Reauthorize.vue';
|
||||
import PreChatFormSettings from './PreChatForm/Settings.vue';
|
||||
import WeeklyAvailability from './components/WeeklyAvailability.vue';
|
||||
@@ -33,9 +33,42 @@ export default {
|
||||
SenderNameExamplePreview,
|
||||
MicrosoftReauthorize,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
setup() {
|
||||
return { v$: useVuelidate() };
|
||||
const {
|
||||
isAMicrosoftInbox,
|
||||
isAGoogleInbox,
|
||||
isAPIInbox,
|
||||
isATwitterInbox,
|
||||
isAFacebookInbox,
|
||||
isAWebWidgetInbox,
|
||||
isATwilioChannel,
|
||||
isALineChannel,
|
||||
isAnEmailChannel,
|
||||
isATwilioSMSChannel,
|
||||
isASmsInbox,
|
||||
isATwilioWhatsAppChannel,
|
||||
isAWhatsAppCloudChannel,
|
||||
is360DialogWhatsAppChannel,
|
||||
isAWhatsAppChannel,
|
||||
} = useInbox({ inboxId: this.currentInboxId });
|
||||
return {
|
||||
v$: useVuelidate(),
|
||||
isAMicrosoftInbox,
|
||||
isAGoogleInbox,
|
||||
isAPIInbox,
|
||||
isATwitterInbox,
|
||||
isAFacebookInbox,
|
||||
isAWebWidgetInbox,
|
||||
isATwilioChannel,
|
||||
isALineChannel,
|
||||
isAnEmailChannel,
|
||||
isATwilioSMSChannel,
|
||||
isASmsInbox,
|
||||
isATwilioWhatsAppChannel,
|
||||
isAWhatsAppCloudChannel,
|
||||
is360DialogWhatsAppChannel,
|
||||
isAWhatsAppChannel,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<script setup>
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useStoreGetters } from 'dashboard/composables/store';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
channelType: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
medium: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const getters = useStoreGetters();
|
||||
const { t } = useI18n();
|
||||
const globalConfig = computed(() => getters['globalConfig/get'].value);
|
||||
|
||||
const i18nMap = {
|
||||
'Channel::FacebookPage': 'MESSENGER',
|
||||
'Channel::WebWidget': 'WEB_WIDGET',
|
||||
'Channel::TwitterProfile': 'TWITTER_PROFILE',
|
||||
'Channel::TwilioSms': 'TWILIO_SMS',
|
||||
'Channel::Whatsapp': 'WHATSAPP',
|
||||
'Channel::Sms': 'SMS',
|
||||
'Channel::Email': 'EMAIL',
|
||||
'Channel::Telegram': 'TELEGRAM',
|
||||
'Channel::Line': 'LINE',
|
||||
'Channel::Api': 'API',
|
||||
};
|
||||
|
||||
const twilioChannelName = () => {
|
||||
if (props.medium === 'whatsapp') {
|
||||
return t(`INBOX_MGMT.CHANNELS.WHATSAPP`);
|
||||
}
|
||||
return t(`INBOX_MGMT.CHANNELS.TWILIO_SMS`);
|
||||
};
|
||||
|
||||
const readableChannelName = computed(() => {
|
||||
if (props.channelType === 'Channel::Api') {
|
||||
return globalConfig.value.apiChannelName || t('INBOX_MGMT.CHANNELS.API');
|
||||
}
|
||||
if (props.channelType === 'Channel::TwilioSms') {
|
||||
return twilioChannelName();
|
||||
}
|
||||
return t(`INBOX_MGMT.CHANNELS.${i18nMap[props.channelType]}`);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span>
|
||||
{{ readableChannelName }}
|
||||
</span>
|
||||
</template>
|
||||
+11
-2
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { useInbox } from 'shared/composables/useInbox';
|
||||
import SettingsSection from 'dashboard/components/SettingsSection.vue';
|
||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||
import BusinessDay from './BusinessDay.vue';
|
||||
@@ -23,13 +23,22 @@ export default {
|
||||
BusinessDay,
|
||||
WootMessageEditor,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
inbox: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { isATwilioChannel, isATwitterInbox, isAFacebookInbox } = useInbox({
|
||||
inboxObj: props.inbox,
|
||||
});
|
||||
return {
|
||||
isATwitterInbox,
|
||||
isAFacebookInbox,
|
||||
isATwilioChannel,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isBusinessHoursEnabled: false,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* eslint arrow-body-style: 0 */
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
import channelFactory from './channel-factory';
|
||||
|
||||
const SettingsContent = () => import('../Wrapper.vue');
|
||||
const SettingWrapper = () => import('../SettingsWrapper.vue');
|
||||
const InboxHome = () => import('./Index.vue');
|
||||
const Settings = () => import('./Settings.vue');
|
||||
const InboxChannel = () => import('./InboxChannels.vue');
|
||||
@@ -12,6 +12,24 @@ const FinishSetup = () => import('./FinishSetup.vue');
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/settings/inboxes'),
|
||||
component: SettingWrapper,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirect: 'list',
|
||||
},
|
||||
{
|
||||
path: 'list',
|
||||
name: 'settings_inbox_list',
|
||||
component: InboxHome,
|
||||
meta: {
|
||||
permissions: ['administrator'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/settings/inboxes'),
|
||||
component: SettingsContent,
|
||||
@@ -26,18 +44,6 @@ export default {
|
||||
};
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirect: 'list',
|
||||
},
|
||||
{
|
||||
path: 'list',
|
||||
name: 'settings_inbox_list',
|
||||
component: InboxHome,
|
||||
meta: {
|
||||
permissions: ['administrator'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
component: InboxChannel,
|
||||
|
||||
+19
-4
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { useInbox } from 'shared/composables/useInbox';
|
||||
import SettingsSection from '../../../../../components/SettingsSection.vue';
|
||||
import ImapSettings from '../ImapSettings.vue';
|
||||
import SmtpSettings from '../SmtpSettings.vue';
|
||||
@@ -13,15 +13,30 @@ export default {
|
||||
ImapSettings,
|
||||
SmtpSettings,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
inbox: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
return { v$: useVuelidate() };
|
||||
setup(props) {
|
||||
const {
|
||||
isAPIInbox,
|
||||
isAWebWidgetInbox,
|
||||
isATwilioChannel,
|
||||
isALineChannel,
|
||||
isAnEmailChannel,
|
||||
} = useInbox({
|
||||
inboxObj: props.inbox,
|
||||
});
|
||||
return {
|
||||
v$: useVuelidate(),
|
||||
isAPIInbox,
|
||||
isAWebWidgetInbox,
|
||||
isATwilioChannel,
|
||||
isALineChannel,
|
||||
isAnEmailChannel,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
|
||||
import * as types from '../mutation-types';
|
||||
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
||||
import { INBOX_TYPES } from 'shared/composables/useInbox';
|
||||
import InboxesAPI from '../../api/inboxes';
|
||||
import WebChannel from '../../api/channel/webChannel';
|
||||
import FBChannel from '../../api/channel/fbChannel';
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { useInbox } from '../useInbox';
|
||||
|
||||
// Mock the useStore composable
|
||||
vi.mock('dashboard/composables/store', () => ({
|
||||
useStore: () => ({
|
||||
getters: {
|
||||
'inboxes/getInbox': vi.fn(id => ({
|
||||
id,
|
||||
channel_type: 'Channel::WebWidget',
|
||||
})),
|
||||
getSelectedChat: {
|
||||
id: 1,
|
||||
inbox_id: 1,
|
||||
channel_type: 'Channel::WebWidget',
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useInbox', () => {
|
||||
const createInbox = (channelType, additionalConfig = {}) => ({
|
||||
channel_type: channelType,
|
||||
...additionalConfig,
|
||||
});
|
||||
|
||||
it('returns the correct channel type', () => {
|
||||
const inbox = createInbox('Channel::WebWidget');
|
||||
const { channelType } = useInbox({ inboxObj: inbox });
|
||||
expect(channelType.value).toBe('Channel::WebWidget');
|
||||
});
|
||||
|
||||
it('isAPIInbox returns true if channel type is API', () => {
|
||||
const inbox = createInbox('Channel::Api');
|
||||
const { isAPIInbox } = useInbox({ inboxObj: inbox });
|
||||
expect(isAPIInbox.value).toBe(true);
|
||||
});
|
||||
|
||||
it('isATwitterInbox returns true if channel type is twitter', () => {
|
||||
const inbox = createInbox('Channel::TwitterProfile');
|
||||
const { isATwitterInbox } = useInbox({ inboxObj: inbox });
|
||||
expect(isATwitterInbox.value).toBe(true);
|
||||
});
|
||||
|
||||
it('isAFacebookInbox returns true if channel type is Facebook', () => {
|
||||
const inbox = createInbox('Channel::FacebookPage');
|
||||
const { isAFacebookInbox } = useInbox({ inboxObj: inbox });
|
||||
expect(isAFacebookInbox.value).toBe(true);
|
||||
});
|
||||
|
||||
it('isAWebWidgetInbox returns true if channel type is WebWidget', () => {
|
||||
const inbox = createInbox('Channel::WebWidget');
|
||||
const { isAWebWidgetInbox } = useInbox({ inboxObj: inbox });
|
||||
expect(isAWebWidgetInbox.value).toBe(true);
|
||||
});
|
||||
|
||||
it('isASmsInbox returns true if channel type is sms', () => {
|
||||
const inbox = createInbox('Channel::Sms');
|
||||
const { isASmsInbox } = useInbox({ inboxObj: inbox });
|
||||
expect(isASmsInbox.value).toBe(true);
|
||||
});
|
||||
|
||||
it('isASmsInbox returns true if channel type is twilio sms', () => {
|
||||
const inbox = createInbox('Channel::TwilioSms', { medium: 'sms' });
|
||||
const { isASmsInbox } = useInbox({ inboxObj: inbox });
|
||||
expect(isASmsInbox.value).toBe(true);
|
||||
});
|
||||
|
||||
describe('isATwilioSMSChannel', () => {
|
||||
it('returns true if channel type is Twilio and medium is SMS', () => {
|
||||
const inbox = createInbox('Channel::TwilioSms', { medium: 'sms' });
|
||||
const { isATwilioSMSChannel } = useInbox({ inboxObj: inbox });
|
||||
expect(isATwilioSMSChannel.value).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if channel type is Twilio but medium is not SMS', () => {
|
||||
const inbox = createInbox('Channel::TwilioSms', { medium: 'whatsapp' });
|
||||
const { isATwilioSMSChannel } = useInbox({ inboxObj: inbox });
|
||||
expect(isATwilioSMSChannel.value).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false if channel type is not Twilio but medium is SMS', () => {
|
||||
const inbox = createInbox('Channel::NotTwilio', { medium: 'sms' });
|
||||
const { isATwilioSMSChannel } = useInbox({ inboxObj: inbox });
|
||||
expect(isATwilioSMSChannel.value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Badges', () => {
|
||||
it('inboxBadge returns correct badge for Telegram', () => {
|
||||
const inbox = createInbox('Channel::Telegram');
|
||||
const { inboxBadge } = useInbox({ inboxObj: inbox });
|
||||
expect(inboxBadge.value).toBe('Channel::Telegram');
|
||||
});
|
||||
|
||||
it('inboxBadge returns correct badge for WhatsApp channel', () => {
|
||||
const inbox = createInbox('Channel::Whatsapp');
|
||||
const { inboxBadge } = useInbox({ inboxObj: inbox });
|
||||
expect(inboxBadge.value).toBe('whatsapp');
|
||||
});
|
||||
|
||||
it('inboxBadge returns the twitterBadge when isATwitterInbox is true', () => {
|
||||
const inbox = createInbox('Channel::TwitterProfile');
|
||||
const { inboxBadge } = useInbox({ inboxObj: inbox });
|
||||
expect(inboxBadge.value).toBe('twitter-dm');
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhatsApp channel', () => {
|
||||
it('returns correct whatsAppAPIProvider', () => {
|
||||
const inbox = createInbox('Channel::Whatsapp', {
|
||||
provider: 'whatsapp_cloud',
|
||||
});
|
||||
const { whatsAppAPIProvider } = useInbox({ inboxObj: inbox });
|
||||
expect(whatsAppAPIProvider.value).toBe('whatsapp_cloud');
|
||||
});
|
||||
|
||||
it('isAWhatsAppCloudChannel returns true if channel type is WhatsApp and provider is whatsapp_cloud', () => {
|
||||
const inbox = createInbox('Channel::Whatsapp', {
|
||||
provider: 'whatsapp_cloud',
|
||||
});
|
||||
const { isAWhatsAppCloudChannel } = useInbox({ inboxObj: inbox });
|
||||
expect(isAWhatsAppCloudChannel.value).toBe(true);
|
||||
});
|
||||
|
||||
it('is360DialogWhatsAppChannel returns true if channel type is WhatsApp and provider is default', () => {
|
||||
const inbox = createInbox('Channel::Whatsapp', { provider: 'default' });
|
||||
const { is360DialogWhatsAppChannel } = useInbox({ inboxObj: inbox });
|
||||
expect(is360DialogWhatsAppChannel.value).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#inboxHasFeature', () => {
|
||||
it('detects the correct feature', () => {
|
||||
const inbox = createInbox('Channel::Telegram');
|
||||
const { inboxHasFeature } = useInbox({ inboxObj: inbox });
|
||||
expect(inboxHasFeature('replyTo')).toBe(true);
|
||||
expect(inboxHasFeature('feature-does-not-exist')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for feature not included', () => {
|
||||
const inbox = createInbox('Channel::Sms');
|
||||
const { inboxHasFeature } = useInbox({ inboxObj: inbox });
|
||||
expect(inboxHasFeature('replyTo')).toBe(false);
|
||||
expect(inboxHasFeature('feature-does-not-exist')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,180 @@
|
||||
import { computed, ref } from 'vue';
|
||||
import { useStore } from 'dashboard/composables/store';
|
||||
|
||||
export const INBOX_TYPES = {
|
||||
WEB: 'Channel::WebWidget',
|
||||
FB: 'Channel::FacebookPage',
|
||||
TWITTER: 'Channel::TwitterProfile',
|
||||
TWILIO: 'Channel::TwilioSms',
|
||||
WHATSAPP: 'Channel::Whatsapp',
|
||||
API: 'Channel::Api',
|
||||
EMAIL: 'Channel::Email',
|
||||
TELEGRAM: 'Channel::Telegram',
|
||||
LINE: 'Channel::Line',
|
||||
SMS: 'Channel::Sms',
|
||||
};
|
||||
|
||||
export const INBOX_FEATURES = {
|
||||
REPLY_TO: 'replyTo',
|
||||
REPLY_TO_OUTGOING: 'replyToOutgoing',
|
||||
};
|
||||
|
||||
export const INBOX_FEATURE_MAP = {
|
||||
[INBOX_FEATURES.REPLY_TO]: [
|
||||
INBOX_TYPES.FB,
|
||||
INBOX_TYPES.WEB,
|
||||
INBOX_TYPES.TWITTER,
|
||||
INBOX_TYPES.WHATSAPP,
|
||||
INBOX_TYPES.TELEGRAM,
|
||||
INBOX_TYPES.API,
|
||||
],
|
||||
[INBOX_FEATURES.REPLY_TO_OUTGOING]: [
|
||||
INBOX_TYPES.WEB,
|
||||
INBOX_TYPES.TWITTER,
|
||||
INBOX_TYPES.WHATSAPP,
|
||||
INBOX_TYPES.TELEGRAM,
|
||||
INBOX_TYPES.API,
|
||||
],
|
||||
};
|
||||
|
||||
// useInbox(inboxId) {}
|
||||
// useChatInbox {
|
||||
// chat = getCurrentChat
|
||||
// return useInbox(chat.inboxId)
|
||||
// }
|
||||
|
||||
export function useInbox({ inboxObj, inboxId, chat, getCurrentChat }) {
|
||||
const store = useStore();
|
||||
const inbox = ref(null);
|
||||
if (inboxObj) {
|
||||
inbox.value = inboxObj;
|
||||
}
|
||||
if (inboxId) {
|
||||
inbox.value = store.getters['inboxes/getInbox'](inboxId);
|
||||
}
|
||||
if (getCurrentChat) {
|
||||
inbox.value = store.getters.getSelectedChat;
|
||||
}
|
||||
const channelType = computed(() => inbox.value.channel_type);
|
||||
const whatsAppAPIProvider = computed(() => inbox.value.provider || '');
|
||||
|
||||
const isAPIInbox = computed(() => channelType.value === INBOX_TYPES.API);
|
||||
const isATwitterInbox = computed(
|
||||
() => channelType.value === INBOX_TYPES.TWITTER
|
||||
);
|
||||
const isAFacebookInbox = computed(() => channelType.value === INBOX_TYPES.FB);
|
||||
const isAWebWidgetInbox = computed(
|
||||
() => channelType.value === INBOX_TYPES.WEB
|
||||
);
|
||||
const isATwilioChannel = computed(
|
||||
() => channelType.value === INBOX_TYPES.TWILIO
|
||||
);
|
||||
const isALineChannel = computed(() => channelType.value === INBOX_TYPES.LINE);
|
||||
const isAnEmailChannel = computed(
|
||||
() => channelType.value === INBOX_TYPES.EMAIL
|
||||
);
|
||||
const isATelegramChannel = computed(
|
||||
() => channelType.value === INBOX_TYPES.TELEGRAM
|
||||
);
|
||||
|
||||
const isATwilioSMSChannel = computed(() => {
|
||||
const medium = inbox.value.medium || '';
|
||||
return isATwilioChannel.value && medium === 'sms';
|
||||
});
|
||||
|
||||
const isAMicrosoftInbox = computed(
|
||||
() => isAnEmailChannel.value && inbox.value.provider === 'microsoft'
|
||||
);
|
||||
|
||||
const isAGoogleInbox = computed(
|
||||
() => isAnEmailChannel.value && inbox.value.provider === 'google'
|
||||
);
|
||||
|
||||
const isASmsInbox = computed(
|
||||
() => channelType.value === INBOX_TYPES.SMS || isATwilioSMSChannel.value
|
||||
);
|
||||
|
||||
const isATwilioWhatsAppChannel = computed(() => {
|
||||
const medium = inbox.value.medium || '';
|
||||
return isATwilioChannel.value && medium === 'whatsapp';
|
||||
});
|
||||
|
||||
const isAWhatsAppCloudChannel = computed(
|
||||
() =>
|
||||
channelType.value === INBOX_TYPES.WHATSAPP &&
|
||||
whatsAppAPIProvider.value === 'whatsapp_cloud'
|
||||
);
|
||||
|
||||
const is360DialogWhatsAppChannel = computed(
|
||||
() =>
|
||||
channelType.value === INBOX_TYPES.WHATSAPP &&
|
||||
whatsAppAPIProvider.value === 'default'
|
||||
);
|
||||
|
||||
const chatAdditionalAttributes = computed(() => {
|
||||
return chat?.additional_attributes || {};
|
||||
});
|
||||
|
||||
const isTwitterInboxTweet = computed(
|
||||
() => chatAdditionalAttributes.value.type === 'tweet'
|
||||
);
|
||||
|
||||
const twilioBadge = computed(
|
||||
() => `${isATwilioSMSChannel.value ? 'sms' : 'whatsapp'}`
|
||||
);
|
||||
|
||||
const twitterBadge = computed(
|
||||
() => `${isTwitterInboxTweet.value ? 'twitter-tweet' : 'twitter-dm'}`
|
||||
);
|
||||
|
||||
const facebookBadge = computed(
|
||||
() => chatAdditionalAttributes.value.type || 'facebook'
|
||||
);
|
||||
|
||||
const isAWhatsAppChannel = computed(
|
||||
() =>
|
||||
channelType.value === INBOX_TYPES.WHATSAPP ||
|
||||
isATwilioWhatsAppChannel.value
|
||||
);
|
||||
|
||||
const inboxBadge = computed(() => {
|
||||
if (isATwitterInbox.value) return twitterBadge.value;
|
||||
if (isAFacebookInbox.value) return facebookBadge.value;
|
||||
if (isATwilioChannel.value) return twilioBadge.value;
|
||||
if (isAWhatsAppChannel.value) return 'whatsapp';
|
||||
return channelType.value;
|
||||
});
|
||||
|
||||
function inboxHasFeature(feature) {
|
||||
return INBOX_FEATURE_MAP[feature]?.includes(channelType.value) ?? false;
|
||||
}
|
||||
|
||||
return {
|
||||
channelType,
|
||||
whatsAppAPIProvider,
|
||||
isAMicrosoftInbox,
|
||||
isAGoogleInbox,
|
||||
isAPIInbox,
|
||||
inbox,
|
||||
isATwitterInbox,
|
||||
isAFacebookInbox,
|
||||
isAWebWidgetInbox,
|
||||
isATwilioChannel,
|
||||
isALineChannel,
|
||||
isAnEmailChannel,
|
||||
isATelegramChannel,
|
||||
isATwilioSMSChannel,
|
||||
isASmsInbox,
|
||||
isATwilioWhatsAppChannel,
|
||||
isAWhatsAppCloudChannel,
|
||||
is360DialogWhatsAppChannel,
|
||||
chatAdditionalAttributes,
|
||||
isTwitterInboxTweet,
|
||||
twilioBadge,
|
||||
twitterBadge,
|
||||
facebookBadge,
|
||||
inboxBadge,
|
||||
isAWhatsAppChannel,
|
||||
inboxHasFeature,
|
||||
};
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
export const INBOX_TYPES = {
|
||||
WEB: 'Channel::WebWidget',
|
||||
FB: 'Channel::FacebookPage',
|
||||
TWITTER: 'Channel::TwitterProfile',
|
||||
TWILIO: 'Channel::TwilioSms',
|
||||
WHATSAPP: 'Channel::Whatsapp',
|
||||
API: 'Channel::Api',
|
||||
EMAIL: 'Channel::Email',
|
||||
TELEGRAM: 'Channel::Telegram',
|
||||
LINE: 'Channel::Line',
|
||||
SMS: 'Channel::Sms',
|
||||
};
|
||||
|
||||
export const INBOX_FEATURES = {
|
||||
REPLY_TO: 'replyTo',
|
||||
REPLY_TO_OUTGOING: 'replyToOutgoing',
|
||||
};
|
||||
|
||||
// This is a single source of truth for inbox features
|
||||
// This is used to check if a feature is available for a particular inbox or not
|
||||
export const INBOX_FEATURE_MAP = {
|
||||
[INBOX_FEATURES.REPLY_TO]: [
|
||||
INBOX_TYPES.FB,
|
||||
INBOX_TYPES.WEB,
|
||||
INBOX_TYPES.TWITTER,
|
||||
INBOX_TYPES.WHATSAPP,
|
||||
INBOX_TYPES.TELEGRAM,
|
||||
INBOX_TYPES.API,
|
||||
],
|
||||
[INBOX_FEATURES.REPLY_TO_OUTGOING]: [
|
||||
INBOX_TYPES.WEB,
|
||||
INBOX_TYPES.TWITTER,
|
||||
INBOX_TYPES.WHATSAPP,
|
||||
INBOX_TYPES.TELEGRAM,
|
||||
INBOX_TYPES.API,
|
||||
],
|
||||
};
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
channelType() {
|
||||
return this.inbox.channel_type;
|
||||
},
|
||||
whatsAppAPIProvider() {
|
||||
return this.inbox.provider || '';
|
||||
},
|
||||
isAMicrosoftInbox() {
|
||||
return this.isAnEmailChannel && this.inbox.provider === 'microsoft';
|
||||
},
|
||||
isAGoogleInbox() {
|
||||
return this.isAnEmailChannel && this.inbox.provider === 'google';
|
||||
},
|
||||
isAPIInbox() {
|
||||
return this.channelType === INBOX_TYPES.API;
|
||||
},
|
||||
isATwitterInbox() {
|
||||
return this.channelType === INBOX_TYPES.TWITTER;
|
||||
},
|
||||
isAFacebookInbox() {
|
||||
return this.channelType === INBOX_TYPES.FB;
|
||||
},
|
||||
isAWebWidgetInbox() {
|
||||
return this.channelType === INBOX_TYPES.WEB;
|
||||
},
|
||||
isATwilioChannel() {
|
||||
return this.channelType === INBOX_TYPES.TWILIO;
|
||||
},
|
||||
isALineChannel() {
|
||||
return this.channelType === INBOX_TYPES.LINE;
|
||||
},
|
||||
isAnEmailChannel() {
|
||||
return this.channelType === INBOX_TYPES.EMAIL;
|
||||
},
|
||||
isATelegramChannel() {
|
||||
return this.channelType === INBOX_TYPES.TELEGRAM;
|
||||
},
|
||||
isATwilioSMSChannel() {
|
||||
const { medium: medium = '' } = this.inbox;
|
||||
return this.isATwilioChannel && medium === 'sms';
|
||||
},
|
||||
isASmsInbox() {
|
||||
return this.channelType === INBOX_TYPES.SMS || this.isATwilioSMSChannel;
|
||||
},
|
||||
isATwilioWhatsAppChannel() {
|
||||
const { medium: medium = '' } = this.inbox;
|
||||
return this.isATwilioChannel && medium === 'whatsapp';
|
||||
},
|
||||
isAWhatsAppCloudChannel() {
|
||||
return (
|
||||
this.channelType === INBOX_TYPES.WHATSAPP &&
|
||||
this.whatsAppAPIProvider === 'whatsapp_cloud'
|
||||
);
|
||||
},
|
||||
is360DialogWhatsAppChannel() {
|
||||
return (
|
||||
this.channelType === INBOX_TYPES.WHATSAPP &&
|
||||
this.whatsAppAPIProvider === 'default'
|
||||
);
|
||||
},
|
||||
chatAdditionalAttributes() {
|
||||
const { additional_attributes: additionalAttributes } = this.chat || {};
|
||||
return additionalAttributes || {};
|
||||
},
|
||||
isTwitterInboxTweet() {
|
||||
return this.chatAdditionalAttributes.type === 'tweet';
|
||||
},
|
||||
twilioBadge() {
|
||||
return `${this.isATwilioSMSChannel ? 'sms' : 'whatsapp'}`;
|
||||
},
|
||||
twitterBadge() {
|
||||
return `${this.isTwitterInboxTweet ? 'twitter-tweet' : 'twitter-dm'}`;
|
||||
},
|
||||
facebookBadge() {
|
||||
return this.chatAdditionalAttributes.type || 'facebook';
|
||||
},
|
||||
inboxBadge() {
|
||||
let badgeKey = '';
|
||||
if (this.isATwitterInbox) {
|
||||
badgeKey = this.twitterBadge;
|
||||
} else if (this.isAFacebookInbox) {
|
||||
badgeKey = this.facebookBadge;
|
||||
} else if (this.isATwilioChannel) {
|
||||
badgeKey = this.twilioBadge;
|
||||
} else if (this.isAWhatsAppChannel) {
|
||||
badgeKey = 'whatsapp';
|
||||
}
|
||||
return badgeKey || this.channelType;
|
||||
},
|
||||
isAWhatsAppChannel() {
|
||||
return (
|
||||
this.channelType === INBOX_TYPES.WHATSAPP ||
|
||||
this.isATwilioWhatsAppChannel
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
inboxHasFeature(feature) {
|
||||
return INBOX_FEATURE_MAP[feature]?.includes(this.channelType) ?? false;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,286 +0,0 @@
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import inboxMixin from '../inboxMixin';
|
||||
|
||||
function getComponentConfigForInbox(channelType, additionalConfig = {}) {
|
||||
return {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return {
|
||||
inbox: {
|
||||
channel_type: channelType,
|
||||
...additionalConfig,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getComponentConfigForChat(chat) {
|
||||
return {
|
||||
render() {},
|
||||
mixins: [inboxMixin],
|
||||
data() {
|
||||
return {
|
||||
chat,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('inboxMixin', () => {
|
||||
it('returns the correct channel type', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::WebWidget');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.channelType).toBe('Channel::WebWidget');
|
||||
});
|
||||
|
||||
it('isAPIInbox returns true if channel type is API', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Api');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAPIInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isATwitterInbox returns true if channel type is twitter', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwitterProfile');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwitterInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isAFacebookInbox returns true if channel type is Facebook', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::FacebookPage');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAFacebookInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isAWebWidgetInbox returns true if channel type is Facebook', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::WebWidget');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAWebWidgetInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isASmsInbox returns true if channel type is sms', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Sms');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isASmsInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isASmsInbox returns true if channel type is twilio sms', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||
medium: 'sms',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isASmsInbox).toBe(true);
|
||||
});
|
||||
|
||||
it('isALineChannel returns true if channel type is Line', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Line');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isALineChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('isATelegramChannel returns true if channel type is Telegram', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Telegram');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATelegramChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('isATwilioChannel returns true if channel type is Twilio', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwilioSms');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioChannel).toBe(true);
|
||||
});
|
||||
|
||||
describe('isATwilioSMSChannel', () => {
|
||||
it('returns true if channel type is Twilio and medium is SMS', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||
medium: 'sms',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if channel type is Twilio but medium is not SMS', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||
medium: 'whatsapp',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false if channel type is not Twilio but medium is SMS', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::NotTwilio', {
|
||||
medium: 'sms',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false if neither channel type is Twilio nor medium is SMS', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::NotTwilio', {
|
||||
medium: 'not_sms',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false if channel type is Twilio but medium is empty', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||
medium: undefined,
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('isAnEmailChannel returns true if channel type is email', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Email');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAnEmailChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('isTwitterInboxTweet returns true if Twitter channel type is tweet', () => {
|
||||
const Component = getComponentConfigForChat({
|
||||
channel_type: 'Channel::TwitterProfile',
|
||||
additional_attributes: {
|
||||
type: 'tweet',
|
||||
},
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isTwitterInboxTweet).toBe(true);
|
||||
});
|
||||
|
||||
it('twilioBadge returns string sms if channel type is Twilio and medium is sms', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||
medium: 'sms',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
||||
expect(wrapper.vm.twilioBadge).toBe('sms');
|
||||
});
|
||||
|
||||
it('twitterBadge returns string twitter-tweet if Twitter channel type is tweet', () => {
|
||||
const Component = getComponentConfigForChat({
|
||||
id: 1,
|
||||
additional_attributes: {
|
||||
type: 'tweet',
|
||||
},
|
||||
});
|
||||
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isTwitterInboxTweet).toBe(true);
|
||||
expect(wrapper.vm.twitterBadge).toBe('twitter-tweet');
|
||||
});
|
||||
|
||||
describe('Badges', () => {
|
||||
it('inboxBadge returns string Channel::Telegram if isATwilioChannel and isATwitterInbox is false', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Telegram');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isATwilioChannel).toBe(false);
|
||||
expect(wrapper.vm.isATwitterInbox).toBe(false);
|
||||
expect(wrapper.vm.channelType).toBe('Channel::Telegram');
|
||||
});
|
||||
|
||||
it('inboxBadge returns correct badge for WhatsApp channel', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Whatsapp');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.inboxBadge).toBe('whatsapp');
|
||||
});
|
||||
|
||||
it('inboxBadge returns the twitterBadge when isATwitterInbox is true', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwitterProfile');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.inboxBadge).toBe('twitter-dm');
|
||||
});
|
||||
|
||||
it('inboxBadge returns the facebookBadge when isAFacebookInbox is true', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::FacebookPage');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.inboxBadge).toBe('facebook');
|
||||
});
|
||||
|
||||
it('inboxBadge returns the twilioBadge when isATwilioChannel is true', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||
medium: 'sms',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.inboxBadge).toBe('sms');
|
||||
});
|
||||
|
||||
it('inboxBadge returns "whatsapp" when isAWhatsAppChannel is true', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Whatsapp');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.inboxBadge).toBe('whatsapp');
|
||||
});
|
||||
|
||||
it('inboxBadge returns the channelType when no specific condition is true', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Email');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.inboxBadge).toBe('Channel::Email');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#inboxHasFeature', () => {
|
||||
it('detects the correct feature', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Telegram');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.inboxHasFeature('replyTo')).toBe(true);
|
||||
expect(wrapper.vm.inboxHasFeature('feature-does-not-exist')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for feature not included', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Sms');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.inboxHasFeature('replyTo')).toBe(false);
|
||||
expect(wrapper.vm.inboxHasFeature('feature-does-not-exist')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhatsApp channel', () => {
|
||||
it('returns correct whatsAppAPIProvider', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
||||
provider: 'whatsapp_cloud',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.whatsAppAPIProvider).toBe('whatsapp_cloud');
|
||||
});
|
||||
|
||||
it('returns empty whatsAppAPIProvider if nothing is present', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
||||
provider: undefined,
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.whatsAppAPIProvider).toBe('');
|
||||
});
|
||||
|
||||
it('isAWhatsAppCloudChannel returns true if channel type is WhatsApp and provider is whatsapp_cloud', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
||||
provider: 'whatsapp_cloud',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAWhatsAppCloudChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('is360DialogWhatsAppChannel returns true if channel type is WhatsApp and provider is default', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
||||
provider: 'default',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.is360DialogWhatsAppChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('isAWhatsAppChannel returns true if channel type is WhatsApp', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::Whatsapp');
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAWhatsAppChannel).toBe(true);
|
||||
});
|
||||
|
||||
it('isAWhatsAppChannel returns true if channel type is Twilio and medium is WhatsApp', () => {
|
||||
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||
medium: 'whatsapp',
|
||||
});
|
||||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isAWhatsAppChannel).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { mapGetters, mapActions } from 'vuex';
|
||||
import { setHeader } from 'widget/helpers/axios';
|
||||
import addHours from 'date-fns/addHours';
|
||||
import { IFrameHelper, RNHelper } from 'widget/helpers/utils';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from './mixins/configMixin';
|
||||
import availabilityMixin from 'widget/mixins/availability';
|
||||
import { getLocale } from './helpers/urlParamsHelper';
|
||||
import { isEmptyObject } from 'widget/helpers/utils';
|
||||
@@ -27,13 +27,7 @@ export default {
|
||||
components: {
|
||||
Spinner,
|
||||
},
|
||||
mixins: [availabilityMixin, routerMixin, darkModeMixin],
|
||||
setup() {
|
||||
const { shouldShowPreChatForm } = useConfig();
|
||||
return {
|
||||
shouldShowPreChatForm,
|
||||
};
|
||||
},
|
||||
mixins: [availabilityMixin, configMixin, routerMixin, darkModeMixin],
|
||||
data() {
|
||||
return {
|
||||
isMobile: false,
|
||||
|
||||
@@ -8,8 +8,8 @@ import VideoBubble from 'widget/components/VideoBubble.vue';
|
||||
import FileBubble from 'widget/components/FileBubble.vue';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import { MESSAGE_TYPE } from 'widget/helpers/constants';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import { useMessage } from '../composables/useMessage';
|
||||
import configMixin from '../mixins/configMixin';
|
||||
import messageMixin from '../mixins/messageMixin';
|
||||
import { isASubmittedFormMessage } from 'shared/helpers/MessageTypeHelper';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
||||
import ReplyToChip from 'widget/components/ReplyToChip.vue';
|
||||
@@ -28,7 +28,7 @@ export default {
|
||||
MessageReplyButton,
|
||||
ReplyToChip,
|
||||
},
|
||||
mixins: [darkModeMixin],
|
||||
mixins: [configMixin, messageMixin, darkModeMixin],
|
||||
props: {
|
||||
message: {
|
||||
type: Object,
|
||||
@@ -39,19 +39,6 @@ export default {
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { messageContentAttributes, hasAttachments } = useMessage(
|
||||
props.message
|
||||
);
|
||||
const { useInboxAvatarForBot, inboxAvatarUrl, channelConfig } = useConfig();
|
||||
return {
|
||||
messageContentAttributes,
|
||||
hasAttachments,
|
||||
useInboxAvatarForBot,
|
||||
inboxAvatarUrl,
|
||||
channelConfig,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasImageError: false,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mapGetters } from 'vuex';
|
||||
|
||||
import ChatAttachmentButton from 'widget/components/ChatAttachment.vue';
|
||||
import ChatSendButton from 'widget/components/ChatSendButton.vue';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from '../mixins/configMixin';
|
||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||
import ResizableTextArea from 'shared/components/ResizableTextArea.vue';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
FluentIcon,
|
||||
ResizableTextArea,
|
||||
},
|
||||
mixins: [darkModeMixin],
|
||||
mixins: [configMixin, darkModeMixin],
|
||||
props: {
|
||||
onSendMessage: {
|
||||
type: Function,
|
||||
@@ -30,13 +30,7 @@ export default {
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { hasEmojiPickerEnabled, hasAttachmentsEnabled } = useConfig();
|
||||
return {
|
||||
hasEmojiPickerEnabled,
|
||||
hasAttachmentsEnabled,
|
||||
};
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
userInput: '',
|
||||
|
||||
@@ -4,13 +4,13 @@ import { IFrameHelper, RNHelper } from 'widget/helpers/utils';
|
||||
import { popoutChatWindow } from '../helpers/popoutHelper';
|
||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from 'widget/mixins/configMixin';
|
||||
import { CONVERSATION_STATUS } from 'shared/constants/messages';
|
||||
|
||||
export default {
|
||||
name: 'HeaderActions',
|
||||
components: { FluentIcon },
|
||||
mixins: [darkModeMixin],
|
||||
mixins: [configMixin, darkModeMixin],
|
||||
props: {
|
||||
showPopoutButton: {
|
||||
type: Boolean,
|
||||
@@ -21,12 +21,6 @@ export default {
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { hasEndConversationEnabled } = useConfig();
|
||||
return {
|
||||
hasEndConversationEnabled,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
conversationAttributes: 'conversationAttributes/getConversationParams',
|
||||
|
||||
@@ -8,14 +8,14 @@ import { getRegexp } from 'shared/helpers/Validators';
|
||||
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
|
||||
import routerMixin from 'widget/mixins/routerMixin';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from 'widget/mixins/configMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CustomButton,
|
||||
Spinner,
|
||||
},
|
||||
mixins: [routerMixin, darkModeMixin, messageFormatterMixin],
|
||||
mixins: [routerMixin, darkModeMixin, configMixin],
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
@@ -23,10 +23,8 @@ export default {
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { preChatFormEnabled } = useConfig();
|
||||
const { formatMessage } = useMessageFormatter();
|
||||
return {
|
||||
preChatFormEnabled,
|
||||
formatMessage,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mapGetters } from 'vuex';
|
||||
import { getContrastingTextColor } from '@chatwoot/utils';
|
||||
import nextAvailabilityTime from 'widget/mixins/nextAvailabilityTime';
|
||||
import AvailableAgents from 'widget/components/AvailableAgents.vue';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from 'widget/mixins/configMixin';
|
||||
import availabilityMixin from 'widget/mixins/availability';
|
||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||
import { IFrameHelper } from 'widget/helpers/utils';
|
||||
@@ -15,7 +15,7 @@ export default {
|
||||
AvailableAgents,
|
||||
FluentIcon,
|
||||
},
|
||||
mixins: [nextAvailabilityTime, availabilityMixin],
|
||||
mixins: [configMixin, nextAvailabilityTime, availabilityMixin],
|
||||
props: {
|
||||
availableAgents: {
|
||||
type: Array,
|
||||
@@ -26,12 +26,7 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { channelConfig } = useConfig();
|
||||
return {
|
||||
channelConfig,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
widgetColor: 'appConfig/getWidgetColor',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from '../mixins/configMixin';
|
||||
import { isEmptyObject } from 'widget/helpers/utils';
|
||||
import {
|
||||
ON_CAMPAIGN_MESSAGE_CLICK,
|
||||
@@ -11,7 +11,7 @@ import darkModeMixin from 'widget/mixins/darkModeMixin';
|
||||
export default {
|
||||
name: 'UnreadMessage',
|
||||
components: { Thumbnail },
|
||||
mixins: [messageFormatterMixin, darkModeMixin],
|
||||
mixins: [configMixin, darkModeMixin],
|
||||
props: {
|
||||
message: {
|
||||
type: String,
|
||||
@@ -31,12 +31,8 @@ export default {
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { useInboxAvatarForBot, inboxAvatarUrl, channelConfig } = useConfig();
|
||||
const { formatMessage } = useMessageFormatter();
|
||||
return {
|
||||
useInboxAvatarForBot,
|
||||
inboxAvatarUrl,
|
||||
channelConfig,
|
||||
formatMessage,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import configMixin from '../mixins/configMixin';
|
||||
import { ON_UNREAD_MESSAGE_CLICK } from '../constants/widgetBusEvents';
|
||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||
import UnreadMessage from 'widget/components/UnreadMessage.vue';
|
||||
@@ -11,6 +12,7 @@ export default {
|
||||
FluentIcon,
|
||||
UnreadMessage,
|
||||
},
|
||||
mixins: [configMixin],
|
||||
props: {
|
||||
messages: {
|
||||
type: Array,
|
||||
|
||||
@@ -3,7 +3,7 @@ import Banner from '../Banner.vue';
|
||||
import Branding from 'shared/components/Branding.vue';
|
||||
import ChatHeader from '../ChatHeader.vue';
|
||||
import ChatHeaderExpanded from '../ChatHeaderExpanded.vue';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from '../../mixins/configMixin';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { IFrameHelper } from 'widget/helpers/utils';
|
||||
|
||||
@@ -14,12 +14,7 @@ export default {
|
||||
ChatHeader,
|
||||
ChatHeaderExpanded,
|
||||
},
|
||||
setup() {
|
||||
const { channelConfig } = useConfig();
|
||||
return {
|
||||
channelConfig,
|
||||
};
|
||||
},
|
||||
mixins: [configMixin],
|
||||
data() {
|
||||
return {
|
||||
showPopoutButton: false,
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
import { useConfig } from '../useConfig';
|
||||
|
||||
describe('useConfig composable', () => {
|
||||
beforeEach(() => {
|
||||
global.window.chatwootWebChannel = {
|
||||
avatarUrl: 'https://test.url',
|
||||
hasAConnectedAgentBot: 'AgentBot',
|
||||
enabledFeatures: [
|
||||
'emoji_picker',
|
||||
'attachments',
|
||||
'end_conversation',
|
||||
'use_inbox_avatar_for_bot',
|
||||
],
|
||||
preChatFormOptions: {
|
||||
pre_chat_message: '',
|
||||
pre_chat_fields: [
|
||||
{
|
||||
label: 'Email Id',
|
||||
name: 'emailAddress',
|
||||
type: 'email',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: 'Full name',
|
||||
name: 'fullName',
|
||||
type: 'text',
|
||||
field_type: 'standard',
|
||||
required: true,
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
preChatFormEnabled: true,
|
||||
};
|
||||
});
|
||||
|
||||
it('should return correct values for computed properties', () => {
|
||||
const {
|
||||
useInboxAvatarForBot,
|
||||
hasAConnectedAgentBot,
|
||||
inboxAvatarUrl,
|
||||
channelConfig,
|
||||
hasEmojiPickerEnabled,
|
||||
hasAttachmentsEnabled,
|
||||
hasEndConversationEnabled,
|
||||
preChatFormEnabled,
|
||||
preChatFormOptions,
|
||||
shouldShowPreChatForm,
|
||||
} = useConfig();
|
||||
|
||||
expect(useInboxAvatarForBot.value).toBe(true);
|
||||
expect(hasAConnectedAgentBot.value).toBe(true);
|
||||
expect(inboxAvatarUrl.value).toBe('https://test.url');
|
||||
|
||||
expect(channelConfig.value).toEqual({
|
||||
avatarUrl: 'https://test.url',
|
||||
hasAConnectedAgentBot: 'AgentBot',
|
||||
enabledFeatures: [
|
||||
'emoji_picker',
|
||||
'attachments',
|
||||
'end_conversation',
|
||||
'use_inbox_avatar_for_bot',
|
||||
],
|
||||
preChatFormOptions: {
|
||||
pre_chat_message: '',
|
||||
pre_chat_fields: [
|
||||
{
|
||||
label: 'Email Id',
|
||||
name: 'emailAddress',
|
||||
type: 'email',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: 'Full name',
|
||||
name: 'fullName',
|
||||
type: 'text',
|
||||
field_type: 'standard',
|
||||
required: true,
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
preChatFormEnabled: true,
|
||||
});
|
||||
expect(hasEmojiPickerEnabled.value).toBe(true);
|
||||
expect(hasAttachmentsEnabled.value).toBe(true);
|
||||
expect(hasEndConversationEnabled.value).toBe(true);
|
||||
expect(preChatFormEnabled.value).toBe(true);
|
||||
|
||||
expect(preChatFormOptions.value).toEqual({
|
||||
preChatMessage: '',
|
||||
preChatFields: [
|
||||
{
|
||||
label: 'Email Id',
|
||||
name: 'emailAddress',
|
||||
type: 'email',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: 'Full name',
|
||||
name: 'fullName',
|
||||
type: 'text',
|
||||
field_type: 'standard',
|
||||
required: true,
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(shouldShowPreChatForm.value).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
import { computed } from 'vue';
|
||||
|
||||
/**
|
||||
* Composable for handling config-related computations.
|
||||
* @returns {Object} An object containing computed properties for various configuration settings.
|
||||
*/
|
||||
export function useConfig() {
|
||||
/**
|
||||
* Computed property to get the channel config.
|
||||
* @returns {Object} The channel configuration object.
|
||||
*/
|
||||
const channelConfig = computed(() => window.chatwootWebChannel);
|
||||
|
||||
const createFeatureChecker = feature =>
|
||||
computed(() => channelConfig.value.enabledFeatures.includes(feature));
|
||||
|
||||
// Use the helper function for feature checks
|
||||
const useInboxAvatarForBot = createFeatureChecker('use_inbox_avatar_for_bot');
|
||||
const hasEmojiPickerEnabled = createFeatureChecker('emoji_picker');
|
||||
const hasAttachmentsEnabled = createFeatureChecker('attachments');
|
||||
const hasEndConversationEnabled = createFeatureChecker('end_conversation');
|
||||
|
||||
/**
|
||||
* Computed property to check if the agent bot is connected.
|
||||
* @returns {Boolean} True if the agent bot is connected, false otherwise.
|
||||
*/
|
||||
const hasAConnectedAgentBot = computed(
|
||||
() => !!channelConfig.value.hasAConnectedAgentBot
|
||||
);
|
||||
|
||||
/**
|
||||
* Computed property to get the inbox avatar URL.
|
||||
* @returns {String} The inbox avatar URL.
|
||||
*/
|
||||
const inboxAvatarUrl = computed(() => channelConfig.value.avatarUrl);
|
||||
|
||||
/**
|
||||
* Computed property to check if the pre-chat form is enabled.
|
||||
* @returns {Boolean} True if the pre-chat form is enabled, false otherwise.
|
||||
*/
|
||||
const preChatFormEnabled = computed(
|
||||
() => channelConfig.value.preChatFormEnabled
|
||||
);
|
||||
|
||||
/**
|
||||
* Computed property to get the pre-chat form options.
|
||||
* @returns {Object} An object containing pre-chat message and fields.
|
||||
*/
|
||||
const preChatFormOptions = computed(() => {
|
||||
const options = channelConfig.value.preChatFormOptions || {};
|
||||
const preChatMessage = options.pre_chat_message || '';
|
||||
const preChatFields = options.pre_chat_fields || [];
|
||||
return {
|
||||
preChatMessage,
|
||||
preChatFields,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Computed property to determine if the pre-chat form should be shown.
|
||||
* @returns {Boolean} True if the pre-chat form should be shown, false otherwise.
|
||||
*/
|
||||
const shouldShowPreChatForm = computed(() => {
|
||||
if (!preChatFormEnabled.value) return false;
|
||||
const { preChatFields } = preChatFormOptions.value;
|
||||
return preChatFields.some(field => field.enabled);
|
||||
});
|
||||
|
||||
return {
|
||||
useInboxAvatarForBot,
|
||||
hasAConnectedAgentBot,
|
||||
inboxAvatarUrl,
|
||||
channelConfig,
|
||||
hasEmojiPickerEnabled,
|
||||
hasAttachmentsEnabled,
|
||||
hasEndConversationEnabled,
|
||||
preChatFormEnabled,
|
||||
preChatFormOptions,
|
||||
shouldShowPreChatForm,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
export default {
|
||||
computed: {
|
||||
useInboxAvatarForBot() {
|
||||
return this.channelConfig.enabledFeatures.includes(
|
||||
'use_inbox_avatar_for_bot'
|
||||
);
|
||||
},
|
||||
hasAConnectedAgentBot() {
|
||||
return !!window.chatwootWebChannel.hasAConnectedAgentBot;
|
||||
},
|
||||
inboxAvatarUrl() {
|
||||
return window.chatwootWebChannel.avatarUrl;
|
||||
},
|
||||
channelConfig() {
|
||||
return window.chatwootWebChannel;
|
||||
},
|
||||
hasEmojiPickerEnabled() {
|
||||
return this.channelConfig.enabledFeatures.includes('emoji_picker');
|
||||
},
|
||||
hasAttachmentsEnabled() {
|
||||
return this.channelConfig.enabledFeatures.includes('attachments');
|
||||
},
|
||||
hasEndConversationEnabled() {
|
||||
return this.channelConfig.enabledFeatures.includes('end_conversation');
|
||||
},
|
||||
preChatFormEnabled() {
|
||||
return window.chatwootWebChannel.preChatFormEnabled;
|
||||
},
|
||||
preChatFormOptions() {
|
||||
let preChatMessage = '';
|
||||
const options = window.chatwootWebChannel.preChatFormOptions || {};
|
||||
preChatMessage = options.pre_chat_message;
|
||||
const { pre_chat_fields: preChatFields = [] } = options;
|
||||
return {
|
||||
preChatMessage,
|
||||
preChatFields,
|
||||
};
|
||||
},
|
||||
shouldShowPreChatForm() {
|
||||
const { preChatFields } = this.preChatFormOptions;
|
||||
// Check if at least one enabled field in pre-chat fields
|
||||
const hasEnabledFields =
|
||||
preChatFields.filter(field => field.enabled).length > 0;
|
||||
return this.preChatFormEnabled && hasEnabledFields;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
import { createWrapper } from '@vue/test-utils';
|
||||
import configMixin from '../configMixin';
|
||||
import Vue from 'vue';
|
||||
const preChatFields = [
|
||||
{
|
||||
label: 'Email Id',
|
||||
name: 'emailAddress',
|
||||
type: 'email',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: 'Full name',
|
||||
name: 'fullName',
|
||||
type: 'text',
|
||||
field_type: 'standard',
|
||||
required: true,
|
||||
enabled: true,
|
||||
},
|
||||
];
|
||||
global.chatwootWebChannel = {
|
||||
avatarUrl: 'https://test.url',
|
||||
hasAConnectedAgentBot: 'AgentBot',
|
||||
enabledFeatures: [
|
||||
'emoji_picker',
|
||||
'attachments',
|
||||
'end_conversation',
|
||||
'use_inbox_avatar_for_bot',
|
||||
],
|
||||
preChatFormOptions: { pre_chat_fields: preChatFields, pre_chat_message: '' },
|
||||
preChatFormEnabled: true,
|
||||
};
|
||||
|
||||
describe('configMixin', () => {
|
||||
test('returns config', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [configMixin],
|
||||
};
|
||||
const Constructor = Vue.extend(Component);
|
||||
const vm = new Constructor().$mount();
|
||||
const wrapper = createWrapper(vm);
|
||||
expect(wrapper.vm.hasEmojiPickerEnabled).toBe(true);
|
||||
expect(wrapper.vm.hasEndConversationEnabled).toBe(true);
|
||||
expect(wrapper.vm.hasAttachmentsEnabled).toBe(true);
|
||||
expect(wrapper.vm.hasAConnectedAgentBot).toBe(true);
|
||||
expect(wrapper.vm.useInboxAvatarForBot).toBe(true);
|
||||
expect(wrapper.vm.inboxAvatarUrl).toBe('https://test.url');
|
||||
|
||||
expect(wrapper.vm.channelConfig).toEqual({
|
||||
avatarUrl: 'https://test.url',
|
||||
hasAConnectedAgentBot: 'AgentBot',
|
||||
enabledFeatures: [
|
||||
'emoji_picker',
|
||||
'attachments',
|
||||
'end_conversation',
|
||||
'use_inbox_avatar_for_bot',
|
||||
],
|
||||
preChatFormOptions: {
|
||||
pre_chat_message: '',
|
||||
pre_chat_fields: preChatFields,
|
||||
},
|
||||
preChatFormEnabled: true,
|
||||
});
|
||||
expect(wrapper.vm.preChatFormOptions).toEqual({
|
||||
preChatMessage: '',
|
||||
preChatFields: preChatFields,
|
||||
});
|
||||
expect(wrapper.vm.preChatFormEnabled).toEqual(true);
|
||||
expect(wrapper.vm.shouldShowPreChatForm).toEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ import ArticleCardSkeletonLoader from 'widget/components/ArticleCardSkeletonLoad
|
||||
import { mapGetters } from 'vuex';
|
||||
import darkModeMixin from 'widget/mixins/darkModeMixin';
|
||||
import routerMixin from 'widget/mixins/routerMixin';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from 'widget/mixins/configMixin';
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
@@ -15,13 +15,7 @@ export default {
|
||||
TeamAvailability,
|
||||
ArticleCardSkeletonLoader,
|
||||
},
|
||||
mixins: [routerMixin, darkModeMixin],
|
||||
setup() {
|
||||
const { preChatFormEnabled } = useConfig();
|
||||
return {
|
||||
preChatFormEnabled,
|
||||
};
|
||||
},
|
||||
mixins: [configMixin, routerMixin, darkModeMixin],
|
||||
computed: {
|
||||
...mapGetters({
|
||||
availableAgents: 'agent/availableAgents',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import PreChatForm from '../components/PreChat/Form.vue';
|
||||
import { useConfig } from 'widget/composables/useConfig';
|
||||
import configMixin from '../mixins/configMixin';
|
||||
import routerMixin from '../mixins/routerMixin';
|
||||
import { isEmptyObject } from 'widget/helpers/utils';
|
||||
import { ON_CONVERSATION_CREATED } from '../constants/widgetBusEvents';
|
||||
@@ -9,13 +9,7 @@ export default {
|
||||
components: {
|
||||
PreChatForm,
|
||||
},
|
||||
mixins: [routerMixin],
|
||||
setup() {
|
||||
const { preChatFormOptions } = useConfig();
|
||||
return {
|
||||
preChatFormOptions,
|
||||
};
|
||||
},
|
||||
mixins: [configMixin, routerMixin],
|
||||
mounted() {
|
||||
this.$emitter.on(ON_CONVERSATION_CREATED, () => {
|
||||
// Redirect to messages page after conversation is created
|
||||
|
||||
Reference in New Issue
Block a user