Merge branch 'develop' into feat/ui-lib
This commit is contained in:
+3
@@ -41,6 +41,9 @@ const getTemplateType = template => {
|
||||
if (template.template_type === TWILIO_CONTENT_TEMPLATE_TYPES.QUICK_REPLY) {
|
||||
return t('CONTENT_TEMPLATES.PICKER.TYPES.QUICK_REPLY');
|
||||
}
|
||||
if (template.template_type === TWILIO_CONTENT_TEMPLATE_TYPES.CALL_TO_ACTION) {
|
||||
return t('CONTENT_TEMPLATES.PICKER.TYPES.CALL_TO_ACTION');
|
||||
}
|
||||
return t('CONTENT_TEMPLATES.PICKER.TYPES.TEXT');
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ export default {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="conversation-details-wrap flex flex-col min-w-0 w-full bg-n-background relative"
|
||||
class="conversation-details-wrap flex flex-col min-w-0 w-full bg-n-surface-1 relative"
|
||||
:class="{
|
||||
'border-l rtl:border-l-0 rtl:border-r border-n-weak': !isOnExpandedLayout,
|
||||
}"
|
||||
@@ -104,7 +104,7 @@ export default {
|
||||
<woot-tabs
|
||||
v-if="dashboardApps.length && currentChat.id"
|
||||
:index="activeIndex"
|
||||
class="-mt-px border-t border-t-n-background"
|
||||
class="h-10"
|
||||
@change="onDashboardAppTabChange"
|
||||
>
|
||||
<woot-tabs-item
|
||||
@@ -114,7 +114,6 @@ export default {
|
||||
:name="tab.name"
|
||||
:show-badge="false"
|
||||
is-compact
|
||||
class="[&_a]:pt-1"
|
||||
/>
|
||||
</woot-tabs>
|
||||
<div v-show="!activeIndex" class="flex h-full min-h-0 m-0">
|
||||
|
||||
@@ -236,9 +236,8 @@ const deleteConversation = () => {
|
||||
<div
|
||||
class="relative flex items-start flex-grow-0 flex-shrink-0 w-auto max-w-full py-0 border-t-0 border-b-0 border-l-0 border-r-0 border-transparent border-solid cursor-pointer conversation hover:bg-n-alpha-1 dark:hover:bg-n-alpha-3 group"
|
||||
:class="{
|
||||
'active animate-card-select bg-n-alpha-1 dark:bg-n-alpha-3 border-n-weak':
|
||||
isActiveChat,
|
||||
'bg-n-slate-2 dark:bg-n-slate-3': selected,
|
||||
'active animate-card-select bg-n-background border-n-weak': isActiveChat,
|
||||
'bg-n-slate-2': selected,
|
||||
'px-0': compact,
|
||||
'px-3': !compact,
|
||||
}"
|
||||
|
||||
@@ -95,7 +95,7 @@ const hasSlaPolicyId = computed(() => props.chat?.sla_policy_id);
|
||||
<template>
|
||||
<div
|
||||
ref="conversationHeader"
|
||||
class="flex flex-col gap-3 items-center justify-between flex-1 w-full min-w-0 xl:flex-row px-3 py-2 border-b bg-n-background border-n-weak h-24 xl:h-12"
|
||||
class="flex flex-col gap-3 items-center justify-between flex-1 w-full min-w-0 xl:flex-row px-3 pt-3 pb-2 h-24 xl:h-12"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-start w-full xl:w-auto max-w-full min-w-0 xl:flex-1"
|
||||
|
||||
@@ -42,7 +42,7 @@ const closeContactPanel = () => {
|
||||
<template>
|
||||
<div
|
||||
v-on-click-outside="() => closeContactPanel()"
|
||||
class="bg-n-background h-full overflow-hidden flex flex-col fixed top-0 z-40 w-full max-w-sm transition-transform duration-300 ease-in-out ltr:right-0 rtl:left-0 md:static md:w-[320px] md:min-w-[320px] ltr:border-l rtl:border-r border-n-weak 2xl:min-w-[360px] 2xl:w-[360px] shadow-lg md:shadow-none"
|
||||
class="bg-n-surface-2 h-full overflow-hidden flex flex-col fixed top-0 z-40 w-full max-w-sm transition-transform duration-300 ease-in-out ltr:right-0 rtl:left-0 md:static md:w-[320px] md:min-w-[320px] ltr:border-l rtl:border-r border-n-weak 2xl:min-w-[360px] 2xl:w-[360px] shadow-lg md:shadow-none"
|
||||
:class="[
|
||||
{
|
||||
'md:flex': activeTab === 0,
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import CopilotEditor from 'dashboard/components/widgets/WootWriter/CopilotEditor.vue';
|
||||
import CaptainLoader from 'dashboard/components/widgets/conversation/copilot/CaptainLoader.vue';
|
||||
|
||||
defineProps({
|
||||
showCopilotEditor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isGeneratingContent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
generatedContent: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isPopout: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
'focus',
|
||||
'blur',
|
||||
'clearSelection',
|
||||
'contentReady',
|
||||
'send',
|
||||
]);
|
||||
|
||||
const copilotEditorContent = ref('');
|
||||
|
||||
const onFocus = () => {
|
||||
emit('focus');
|
||||
};
|
||||
|
||||
const onBlur = () => {
|
||||
emit('blur');
|
||||
};
|
||||
|
||||
const clearEditorSelection = () => {
|
||||
emit('clearSelection');
|
||||
};
|
||||
|
||||
const onSend = () => {
|
||||
emit('send', copilotEditorContent.value);
|
||||
copilotEditorContent.value = '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition
|
||||
mode="out-in"
|
||||
enter-active-class="transition-all duration-300 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-2 scale-[0.98]"
|
||||
enter-to-class="opacity-100 translate-y-0 scale-100"
|
||||
leave-active-class="transition-all duration-200 ease-in"
|
||||
leave-from-class="opacity-100 translate-y-0 scale-100"
|
||||
leave-to-class="opacity-0 translate-y-2 scale-[0.98]"
|
||||
@after-enter="emit('contentReady')"
|
||||
>
|
||||
<CopilotEditor
|
||||
v-if="showCopilotEditor && !isGeneratingContent"
|
||||
key="copilot-editor"
|
||||
v-model="copilotEditorContent"
|
||||
class="copilot-editor"
|
||||
:generated-content="generatedContent"
|
||||
:min-height="4"
|
||||
:enabled-menu-options="[]"
|
||||
:is-popout="isPopout"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@clear-selection="clearEditorSelection"
|
||||
@send="onSend"
|
||||
/>
|
||||
<div
|
||||
v-else-if="isGeneratingContent"
|
||||
key="loading-state"
|
||||
class="bg-n-iris-5 rounded min-h-16 w-full mb-4 p-4 flex items-start"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<CaptainLoader class="text-n-iris-10 size-4" />
|
||||
<span class="text-sm text-n-iris-10">
|
||||
{{ $t('CONVERSATION.REPLYBOX.COPILOT_THINKING') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.copilot-editor {
|
||||
.ProseMirror-menubar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -58,7 +58,7 @@ export default {
|
||||
) {
|
||||
return 'h-full overflow-auto w-full';
|
||||
}
|
||||
return 'flex-1 min-w-0 px-0 flex flex-col items-center justify-center h-full';
|
||||
return 'flex-1 min-w-0 px-0 flex flex-col items-center justify-center h-full bg-n-surface-1';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ const openProfileSettings = () => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="my-0 mx-4 px-1 flex max-h-[8vh] items-baseline justify-between hover:bg-n-slate-1 border border-dashed border-n-weak rounded-sm overflow-auto"
|
||||
class="my-0 px-1 flex max-h-[8vh] items-baseline justify-between hover:bg-n-slate-1 border border-dashed border-n-weak rounded-sm overflow-auto"
|
||||
>
|
||||
<p class="w-fit !m-0">
|
||||
{{ $t('CONVERSATION.FOOTER.MESSAGE_SIGNATURE_NOT_CONFIGURED') }}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script>
|
||||
import { ref, provide } from 'vue';
|
||||
// composable
|
||||
import { useConfig } from 'dashboard/composables/useConfig';
|
||||
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||
import { useAI } from 'dashboard/composables/useAI';
|
||||
import { useLabelSuggestions } from 'dashboard/composables/useLabelSuggestions';
|
||||
import { useSnakeCase } from 'dashboard/composables/useTransformKeys';
|
||||
|
||||
// components
|
||||
@@ -49,7 +48,6 @@ export default {
|
||||
setup() {
|
||||
const isPopOutReplyBox = ref(false);
|
||||
const conversationPanelRef = ref(null);
|
||||
const { isEnterprise } = useConfig();
|
||||
|
||||
const keyboardEvents = {
|
||||
Escape: {
|
||||
@@ -62,21 +60,18 @@ export default {
|
||||
useKeyboardEvents(keyboardEvents);
|
||||
|
||||
const {
|
||||
isAIIntegrationEnabled,
|
||||
captainTasksEnabled,
|
||||
isLabelSuggestionFeatureEnabled,
|
||||
fetchIntegrationsIfRequired,
|
||||
fetchLabelSuggestions,
|
||||
} = useAI();
|
||||
getLabelSuggestions,
|
||||
} = useLabelSuggestions();
|
||||
|
||||
provide('contextMenuElementTarget', conversationPanelRef);
|
||||
|
||||
return {
|
||||
isEnterprise,
|
||||
isPopOutReplyBox,
|
||||
isAIIntegrationEnabled,
|
||||
captainTasksEnabled,
|
||||
getLabelSuggestions,
|
||||
isLabelSuggestionFeatureEnabled,
|
||||
fetchIntegrationsIfRequired,
|
||||
fetchLabelSuggestions,
|
||||
conversationPanelRef,
|
||||
};
|
||||
},
|
||||
@@ -105,8 +100,8 @@ export default {
|
||||
shouldShowLabelSuggestions() {
|
||||
return (
|
||||
this.isOpen &&
|
||||
this.isEnterprise &&
|
||||
this.isAIIntegrationEnabled &&
|
||||
this.captainTasksEnabled &&
|
||||
this.isLabelSuggestionFeatureEnabled &&
|
||||
!this.messageSentSinceOpened
|
||||
);
|
||||
},
|
||||
@@ -291,24 +286,15 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isEnterprise) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Early exit if conversation already has labels - no need to suggest more
|
||||
const existingLabels = this.currentChat?.labels || [];
|
||||
if (existingLabels.length > 0) return;
|
||||
|
||||
// method available in mixin, need to ensure that integrations are present
|
||||
await this.fetchIntegrationsIfRequired();
|
||||
|
||||
if (!this.isLabelSuggestionFeatureEnabled) {
|
||||
if (!this.captainTasksEnabled || !this.isLabelSuggestionFeatureEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.labelSuggestions = await this.fetchLabelSuggestions({
|
||||
conversationId: this.currentChat.id,
|
||||
});
|
||||
this.labelSuggestions = await this.getLabelSuggestions();
|
||||
|
||||
// once the labels are fetched, we need to scroll to bottom
|
||||
// but we need to wait for the DOM to be updated
|
||||
@@ -516,7 +502,7 @@ export default {
|
||||
class="flex relative flex-col"
|
||||
:class="{
|
||||
'modal-mask': isPopOutReplyBox,
|
||||
'bg-n-background': !isPopOutReplyBox,
|
||||
'bg-n-surface-1': !isPopOutReplyBox,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -29,7 +29,7 @@ defineProps({
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="h-full w-full bg-n-background border border-n-weak rounded-lg p-4 flex flex-col"
|
||||
class="h-full w-full bg-n-surface-2 border border-n-weak rounded-lg p-4 flex flex-col"
|
||||
>
|
||||
<div class="flex-1 flex items-center justify-center">
|
||||
<img :src="imageSrc" :alt="imageAlt" class="h-36 w-auto mx-auto" />
|
||||
|
||||
@@ -7,14 +7,14 @@ import { useTrack } from 'dashboard/composables';
|
||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
|
||||
import CannedResponse from './CannedResponse.vue';
|
||||
import ReplyToMessage from './ReplyToMessage.vue';
|
||||
import ResizableTextArea from 'shared/components/ResizableTextArea.vue';
|
||||
import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview.vue';
|
||||
import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel.vue';
|
||||
import ReplyEmailHead from './ReplyEmailHead.vue';
|
||||
import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue';
|
||||
import CopilotReplyBottomPanel from 'dashboard/components/widgets/WootWriter/CopilotReplyBottomPanel.vue';
|
||||
import ArticleSearchPopover from 'dashboard/routes/dashboard/helpcenter/components/ArticleSearch/SearchPopover.vue';
|
||||
import CopilotEditorSection from './CopilotEditorSection.vue';
|
||||
import MessageSignatureMissingAlert from './MessageSignatureMissingAlert.vue';
|
||||
import ReplyBoxBanner from './ReplyBoxBanner.vue';
|
||||
import QuotedEmailPreview from './QuotedEmailPreview.vue';
|
||||
@@ -23,6 +23,7 @@ import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vu
|
||||
import AudioRecorder from 'dashboard/components/widgets/WootWriter/AudioRecorder.vue';
|
||||
import { AUDIO_FORMATS } from 'shared/constants/messages';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
import { CMD_AI_ASSIST } from 'dashboard/helper/commandbar/events';
|
||||
import {
|
||||
getMessageVariables,
|
||||
getUndefinedVariablesInMessage,
|
||||
@@ -46,8 +47,10 @@ import {
|
||||
appendSignature,
|
||||
removeSignature,
|
||||
getEffectiveChannelType,
|
||||
extractTextFromMarkdown,
|
||||
} from 'dashboard/helper/editorHelper';
|
||||
import { useCopilotReply } from 'dashboard/composables/useCopilotReply';
|
||||
import { useKbd } from 'dashboard/composables/utils/useKbd';
|
||||
import { isFileTypeAllowedForChannel } from 'shared/helpers/FileHelper';
|
||||
|
||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
@@ -72,8 +75,8 @@ export default {
|
||||
WhatsappTemplates,
|
||||
WootMessageEditor,
|
||||
QuotedEmailPreview,
|
||||
ResizableTextArea,
|
||||
CannedResponse,
|
||||
CopilotEditorSection,
|
||||
CopilotReplyBottomPanel,
|
||||
},
|
||||
mixins: [inboxMixin, fileUploadMixin, keyboardEventListenerMixins],
|
||||
props: {
|
||||
@@ -93,6 +96,8 @@ export default {
|
||||
} = useUISettings();
|
||||
|
||||
const replyEditor = useTemplateRef('replyEditor');
|
||||
const copilot = useCopilotReply();
|
||||
const shortcutKey = useKbd(['$mod', '+', 'enter']);
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
@@ -101,6 +106,8 @@ export default {
|
||||
setQuotedReplyFlagForInbox,
|
||||
fetchQuotedReplyFlagFromUISettings,
|
||||
replyEditor,
|
||||
copilot,
|
||||
shortcutKey,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
@@ -114,8 +121,6 @@ export default {
|
||||
recordingAudioState: '',
|
||||
recordingAudioDurationText: '',
|
||||
replyType: REPLY_EDITOR_MODES.REPLY,
|
||||
mentionSearchKey: '',
|
||||
hasSlashCommand: false,
|
||||
bccEmails: '',
|
||||
ccEmails: '',
|
||||
toEmails: '',
|
||||
@@ -148,9 +153,6 @@ export default {
|
||||
if (!senderId) return {};
|
||||
return this.$store.getters['contacts/getContact'](senderId);
|
||||
},
|
||||
isRichEditorEnabled() {
|
||||
return this.isAWebWidgetInbox || this.isAnEmailChannel || this.isAPIInbox;
|
||||
},
|
||||
shouldShowReplyToMessage() {
|
||||
return (
|
||||
this.inReplyTo?.id &&
|
||||
@@ -276,7 +278,7 @@ export default {
|
||||
sendMessageText = this.$t('CONVERSATION.REPLYBOX.CREATE');
|
||||
}
|
||||
const keyLabel = this.isEditorHotKeyEnabled('cmd_enter')
|
||||
? '(⌘ + ↵)'
|
||||
? `(${this.shortcutKey})`
|
||||
: '(↵)';
|
||||
return `${sendMessageText} ${keyLabel}`;
|
||||
},
|
||||
@@ -409,18 +411,8 @@ export default {
|
||||
!!this.quotedEmailText
|
||||
);
|
||||
},
|
||||
showRichContentEditor() {
|
||||
if (this.isOnPrivateNote || this.isRichEditorEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
// ensure that the signature is plain text depending on `showRichContentEditor`
|
||||
signatureToApply() {
|
||||
return this.showRichContentEditor
|
||||
? this.messageSignature
|
||||
: extractTextFromMarkdown(this.messageSignature);
|
||||
isDefaultEditorMode() {
|
||||
return !this.showAudioRecorderEditor && !this.copilot.isActive.value;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
@@ -431,6 +423,8 @@ export default {
|
||||
// This prevents overwriting user input (e.g., CC/BCC fields) when performing actions
|
||||
// like self-assign or other updates that do not actually change the conversation context
|
||||
this.setCCAndToEmailsFromLastChat();
|
||||
// Reset Copilot editor state (includes cancelling ongoing generation)
|
||||
this.copilot.reset();
|
||||
}
|
||||
|
||||
if (this.isOnPrivateNote) {
|
||||
@@ -464,25 +458,7 @@ export default {
|
||||
this.resetRecorderAndClearAttachments();
|
||||
}
|
||||
},
|
||||
message(updatedMessage) {
|
||||
// Check if the message starts with a slash.
|
||||
const bodyWithoutSignature = removeSignature(
|
||||
updatedMessage,
|
||||
this.signatureToApply
|
||||
);
|
||||
const startsWithSlash = bodyWithoutSignature.startsWith('/');
|
||||
|
||||
// Determine if the user is potentially typing a slash command.
|
||||
// This is true if the message starts with a slash and the rich content editor is not active.
|
||||
this.hasSlashCommand = startsWithSlash && !this.showRichContentEditor;
|
||||
this.showMentions = this.hasSlashCommand;
|
||||
|
||||
// If a slash command is active, extract the command text after the slash.
|
||||
// If not, reset the mentionSearchKey.
|
||||
this.mentionSearchKey = this.hasSlashCommand
|
||||
? bodyWithoutSignature.substring(1)
|
||||
: '';
|
||||
|
||||
message() {
|
||||
// Autosave the current message draft.
|
||||
this.doAutoSaveDraft();
|
||||
},
|
||||
@@ -518,6 +494,7 @@ export default {
|
||||
this.onNewConversationModalActive
|
||||
);
|
||||
emitter.on(BUS_EVENTS.INSERT_INTO_NORMAL_EDITOR, this.addIntoEditor);
|
||||
emitter.on(CMD_AI_ASSIST, this.executeCopilotAction);
|
||||
},
|
||||
unmounted() {
|
||||
document.removeEventListener('paste', this.onPaste);
|
||||
@@ -528,24 +505,19 @@ export default {
|
||||
BUS_EVENTS.NEW_CONVERSATION_MODAL,
|
||||
this.onNewConversationModalActive
|
||||
);
|
||||
emitter.off(CMD_AI_ASSIST, this.executeCopilotAction);
|
||||
},
|
||||
methods: {
|
||||
handleInsert(article) {
|
||||
const { url, title } = article;
|
||||
if (this.isRichEditorEnabled) {
|
||||
// Removing empty lines from the title
|
||||
const lines = title.split('\n');
|
||||
const nonEmptyLines = lines.filter(line => line.trim() !== '');
|
||||
const filteredMarkdown = nonEmptyLines.join(' ');
|
||||
emitter.emit(
|
||||
BUS_EVENTS.INSERT_INTO_RICH_EDITOR,
|
||||
`[${filteredMarkdown}](${url})`
|
||||
);
|
||||
} else {
|
||||
this.addIntoEditor(
|
||||
`${this.$t('CONVERSATION.REPLYBOX.INSERT_READ_MORE')} ${url}`
|
||||
);
|
||||
}
|
||||
// Removing empty lines from the title
|
||||
const lines = title.split('\n');
|
||||
const nonEmptyLines = lines.filter(line => line.trim() !== '');
|
||||
const filteredMarkdown = nonEmptyLines.join(' ');
|
||||
emitter.emit(
|
||||
BUS_EVENTS.INSERT_INTO_RICH_EDITOR,
|
||||
`[${filteredMarkdown}](${url})`
|
||||
);
|
||||
|
||||
useTrack(CONVERSATION_EVENTS.INSERT_ARTICLE_LINK);
|
||||
},
|
||||
@@ -614,26 +586,14 @@ export default {
|
||||
if (this.isPrivate) {
|
||||
return message;
|
||||
}
|
||||
if (this.showRichContentEditor) {
|
||||
const effectiveChannelType = getEffectiveChannelType(
|
||||
this.channelType,
|
||||
this.inbox?.medium || ''
|
||||
);
|
||||
return this.sendWithSignature
|
||||
? appendSignature(
|
||||
message,
|
||||
this.messageSignature,
|
||||
effectiveChannelType
|
||||
)
|
||||
: removeSignature(
|
||||
message,
|
||||
this.messageSignature,
|
||||
effectiveChannelType
|
||||
);
|
||||
}
|
||||
|
||||
const effectiveChannelType = getEffectiveChannelType(
|
||||
this.channelType,
|
||||
this.inbox?.medium || ''
|
||||
);
|
||||
return this.sendWithSignature
|
||||
? appendSignature(message, this.signatureToApply)
|
||||
: removeSignature(message, this.signatureToApply);
|
||||
? appendSignature(message, this.messageSignature, effectiveChannelType)
|
||||
: removeSignature(message, this.messageSignature, effectiveChannelType);
|
||||
},
|
||||
removeFromDraft() {
|
||||
if (this.conversationIdByRoute) {
|
||||
@@ -649,7 +609,6 @@ export default {
|
||||
Escape: {
|
||||
action: () => {
|
||||
this.hideEmojiPicker();
|
||||
this.hideMentions();
|
||||
},
|
||||
allowOnFocusedInput: true,
|
||||
},
|
||||
@@ -672,7 +631,9 @@ export default {
|
||||
},
|
||||
'$mod+Enter': {
|
||||
action: () => {
|
||||
if (this.isAValidEvent('cmd_enter')) {
|
||||
if (this.copilot.isActive.value && this.isFocused) {
|
||||
this.onSubmitCopilotReply();
|
||||
} else if (this.isAValidEvent('cmd_enter')) {
|
||||
this.onSendReply();
|
||||
}
|
||||
},
|
||||
@@ -692,20 +653,34 @@ export default {
|
||||
},
|
||||
onPaste(e) {
|
||||
// Don't handle paste if compose new conversation modal is open
|
||||
if (this.newConversationModalActive) {
|
||||
return;
|
||||
}
|
||||
const data = e.clipboardData.files;
|
||||
if (!this.showRichContentEditor && data.length !== 0) {
|
||||
this.$refs.messageInput?.$el?.blur();
|
||||
}
|
||||
if (!data.length || !data[0]) {
|
||||
return;
|
||||
}
|
||||
data.forEach(file => {
|
||||
const { name, type, size } = file;
|
||||
this.onFileUpload({ name, type, size, file: file });
|
||||
});
|
||||
if (this.newConversationModalActive) return;
|
||||
|
||||
// Filter valid files (non-zero size)
|
||||
Array.from(e.clipboardData.files)
|
||||
.filter(file => file.size > 0)
|
||||
.filter(file => {
|
||||
const isAllowed = isFileTypeAllowedForChannel(file, {
|
||||
channelType: this.channelType || this.inbox?.channel_type,
|
||||
medium: this.inbox?.medium,
|
||||
conversationType: this.conversationType,
|
||||
isInstagramChannel: this.isAnInstagramChannel,
|
||||
isOnPrivateNote: this.isOnPrivateNote,
|
||||
});
|
||||
|
||||
if (!isAllowed) {
|
||||
useAlert(
|
||||
this.$t('CONVERSATION.FILE_TYPE_NOT_SUPPORTED', {
|
||||
fileName: file.name,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return isAllowed;
|
||||
})
|
||||
.forEach(file => {
|
||||
const { name, type, size } = file;
|
||||
this.onFileUpload({ name, type, size, file });
|
||||
});
|
||||
},
|
||||
toggleUserMention(currentMentionState) {
|
||||
this.showUserMentions = currentMentionState;
|
||||
@@ -832,19 +807,15 @@ export default {
|
||||
// if signature is enabled, append it to the message
|
||||
// appendSignature ensures that the signature is not duplicated
|
||||
// so we don't need to check if the signature is already present
|
||||
if (this.showRichContentEditor) {
|
||||
const effectiveChannelType = getEffectiveChannelType(
|
||||
this.channelType,
|
||||
this.inbox?.medium || ''
|
||||
);
|
||||
message = appendSignature(
|
||||
message,
|
||||
this.messageSignature,
|
||||
effectiveChannelType
|
||||
);
|
||||
} else {
|
||||
message = appendSignature(message, this.signatureToApply);
|
||||
}
|
||||
const effectiveChannelType = getEffectiveChannelType(
|
||||
this.channelType,
|
||||
this.inbox?.medium || ''
|
||||
);
|
||||
message = appendSignature(
|
||||
message,
|
||||
this.messageSignature,
|
||||
effectiveChannelType
|
||||
);
|
||||
}
|
||||
|
||||
const updatedMessage = replaceVariablesInMessage({
|
||||
@@ -868,52 +839,33 @@ export default {
|
||||
});
|
||||
if (canReply || this.isAWhatsAppChannel || this.isAPIInbox)
|
||||
this.replyType = mode;
|
||||
if (this.showRichContentEditor) {
|
||||
if (this.isRecordingAudio) {
|
||||
this.toggleAudioRecorder();
|
||||
}
|
||||
return;
|
||||
if (this.isRecordingAudio) {
|
||||
this.toggleAudioRecorder();
|
||||
}
|
||||
this.$nextTick(() => this.$refs.messageInput.focus());
|
||||
},
|
||||
clearEditorSelection() {
|
||||
this.updateEditorSelectionWith = '';
|
||||
},
|
||||
insertIntoTextEditor(text, selectionStart, selectionEnd) {
|
||||
const { message } = this;
|
||||
const newMessage =
|
||||
message.slice(0, selectionStart) +
|
||||
text +
|
||||
message.slice(selectionEnd, message.length);
|
||||
this.message = newMessage;
|
||||
},
|
||||
addIntoEditor(content) {
|
||||
if (this.showRichContentEditor) {
|
||||
this.updateEditorSelectionWith = content;
|
||||
this.onFocus();
|
||||
}
|
||||
if (!this.showRichContentEditor) {
|
||||
const { selectionStart, selectionEnd } = this.$refs.messageInput.$el;
|
||||
this.insertIntoTextEditor(content, selectionStart, selectionEnd);
|
||||
}
|
||||
this.updateEditorSelectionWith = content;
|
||||
this.onFocus();
|
||||
},
|
||||
executeCopilotAction(action, data) {
|
||||
this.copilot.execute(action, data);
|
||||
},
|
||||
clearMessage() {
|
||||
this.message = '';
|
||||
if (this.sendWithSignature && !this.isPrivate) {
|
||||
// if signature is enabled, append it to the message
|
||||
if (this.showRichContentEditor) {
|
||||
const effectiveChannelType = getEffectiveChannelType(
|
||||
this.channelType,
|
||||
this.inbox?.medium || ''
|
||||
);
|
||||
this.message = appendSignature(
|
||||
this.message,
|
||||
this.messageSignature,
|
||||
effectiveChannelType
|
||||
);
|
||||
} else {
|
||||
this.message = appendSignature(this.message, this.signatureToApply);
|
||||
}
|
||||
const effectiveChannelType = getEffectiveChannelType(
|
||||
this.channelType,
|
||||
this.inbox?.medium || ''
|
||||
);
|
||||
this.message = appendSignature(
|
||||
this.message,
|
||||
this.messageSignature,
|
||||
effectiveChannelType
|
||||
);
|
||||
}
|
||||
this.attachedFiles = [];
|
||||
this.isRecordingAudio = false;
|
||||
@@ -948,9 +900,6 @@ export default {
|
||||
this.toggleEmojiPicker();
|
||||
}
|
||||
},
|
||||
hideMentions() {
|
||||
this.showMentions = false;
|
||||
},
|
||||
onTypingOn() {
|
||||
this.toggleTyping('on');
|
||||
},
|
||||
@@ -1169,6 +1118,9 @@ export default {
|
||||
togglePopout() {
|
||||
this.$emit('update:popOutReplyBox', !this.popOutReplyBox);
|
||||
},
|
||||
onSubmitCopilotReply() {
|
||||
this.message = this.copilot.accept();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1179,11 +1131,17 @@ export default {
|
||||
<ReplyTopPanel
|
||||
:mode="replyType"
|
||||
:is-reply-restricted="isReplyRestricted"
|
||||
:disabled="
|
||||
(copilot.isActive.value && copilot.isButtonDisabled.value) ||
|
||||
showAudioRecorderEditor
|
||||
"
|
||||
:is-message-length-reaching-threshold="isMessageLengthReachingThreshold"
|
||||
:characters-remaining="charactersRemaining"
|
||||
:popout-reply-box="popOutReplyBox"
|
||||
@set-reply-mode="setReplyMode"
|
||||
@toggle-popout="togglePopout"
|
||||
@toggle-copilot="copilot.toggleEditor"
|
||||
@execute-copilot-action="executeCopilotAction"
|
||||
/>
|
||||
<ArticleSearchPopover
|
||||
v-if="showArticleSearchPopover && connectedPortalSlug"
|
||||
@@ -1191,141 +1149,167 @@ export default {
|
||||
@insert="handleInsert"
|
||||
@close="onSearchPopoverClose"
|
||||
/>
|
||||
<div class="reply-box__top">
|
||||
<ReplyToMessage
|
||||
v-if="shouldShowReplyToMessage"
|
||||
:message="inReplyTo"
|
||||
@dismiss="resetReplyToMessage"
|
||||
/>
|
||||
<CannedResponse
|
||||
v-if="showMentions && hasSlashCommand"
|
||||
v-on-clickaway="hideMentions"
|
||||
class="normal-editor__canned-box"
|
||||
:search-key="mentionSearchKey"
|
||||
@replace="replaceText"
|
||||
/>
|
||||
<EmojiInput
|
||||
v-if="showEmojiPicker"
|
||||
v-on-clickaway="hideEmojiPicker"
|
||||
:class="{
|
||||
'emoji-dialog--expanded': isOnExpandedLayout || popOutReplyBox,
|
||||
}"
|
||||
:on-click="addIntoEditor"
|
||||
/>
|
||||
<ReplyEmailHead
|
||||
v-if="showReplyHead"
|
||||
v-model:cc-emails="ccEmails"
|
||||
v-model:bcc-emails="bccEmails"
|
||||
v-model:to-emails="toEmails"
|
||||
/>
|
||||
<AudioRecorder
|
||||
v-if="showAudioRecorderEditor"
|
||||
ref="audioRecorderInput"
|
||||
:audio-record-format="audioRecordFormat"
|
||||
@recorder-progress-changed="onRecordProgressChanged"
|
||||
@finish-record="onFinishRecorder"
|
||||
@play="recordingAudioState = 'playing'"
|
||||
@pause="recordingAudioState = 'paused'"
|
||||
/>
|
||||
<ResizableTextArea
|
||||
v-else-if="!showRichContentEditor"
|
||||
ref="messageInput"
|
||||
v-model="message"
|
||||
class="rounded-none input"
|
||||
:placeholder="messagePlaceHolder"
|
||||
:min-height="4"
|
||||
:signature="signatureToApply"
|
||||
allow-signature
|
||||
:send-with-signature="sendWithSignature"
|
||||
@typing-off="onTypingOff"
|
||||
@typing-on="onTypingOn"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
<WootMessageEditor
|
||||
v-else
|
||||
v-model="message"
|
||||
:editor-id="editorStateId"
|
||||
class="input popover-prosemirror-menu"
|
||||
:is-private="isOnPrivateNote"
|
||||
:placeholder="messagePlaceHolder"
|
||||
:update-selection-with="updateEditorSelectionWith"
|
||||
:min-height="4"
|
||||
enable-variables
|
||||
:variables="messageVariables"
|
||||
:signature="messageSignature"
|
||||
allow-signature
|
||||
:channel-type="channelType"
|
||||
:medium="inbox.medium"
|
||||
@typing-off="onTypingOff"
|
||||
@typing-on="onTypingOn"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@toggle-user-mention="toggleUserMention"
|
||||
@toggle-canned-menu="toggleCannedMenu"
|
||||
@toggle-variables-menu="toggleVariablesMenu"
|
||||
@clear-selection="clearEditorSelection"
|
||||
/>
|
||||
<QuotedEmailPreview
|
||||
v-if="shouldShowQuotedPreview"
|
||||
:quoted-email-text="quotedEmailText"
|
||||
:preview-text="quotedEmailPreviewText"
|
||||
@toggle="toggleQuotedReply"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="hasAttachments && !showAudioRecorderEditor"
|
||||
class="attachment-preview-box"
|
||||
@paste="onPaste"
|
||||
<Transition
|
||||
mode="out-in"
|
||||
enter-active-class="transition-all duration-300 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-2 scale-[0.98]"
|
||||
enter-to-class="opacity-100 translate-y-0 scale-100"
|
||||
leave-active-class="transition-all duration-200 ease-in"
|
||||
leave-from-class="opacity-100 translate-y-0 scale-100"
|
||||
leave-to-class="opacity-0 translate-y-2 scale-[0.98]"
|
||||
>
|
||||
<AttachmentPreview
|
||||
class="flex-col mt-4"
|
||||
:attachments="attachedFiles"
|
||||
@remove-attachment="removeAttachment"
|
||||
<div :key="copilot.editorTransitionKey.value" class="reply-box__top">
|
||||
<ReplyToMessage
|
||||
v-if="shouldShowReplyToMessage"
|
||||
:message="inReplyTo"
|
||||
@dismiss="resetReplyToMessage"
|
||||
/>
|
||||
<EmojiInput
|
||||
v-if="showEmojiPicker"
|
||||
v-on-clickaway="hideEmojiPicker"
|
||||
:class="{
|
||||
'emoji-dialog--expanded': isOnExpandedLayout || popOutReplyBox,
|
||||
}"
|
||||
:on-click="addIntoEditor"
|
||||
/>
|
||||
<ReplyEmailHead
|
||||
v-if="showReplyHead && isDefaultEditorMode"
|
||||
v-model:cc-emails="ccEmails"
|
||||
v-model:bcc-emails="bccEmails"
|
||||
v-model:to-emails="toEmails"
|
||||
/>
|
||||
<AudioRecorder
|
||||
v-if="showAudioRecorderEditor"
|
||||
ref="audioRecorderInput"
|
||||
:audio-record-format="audioRecordFormat"
|
||||
@recorder-progress-changed="onRecordProgressChanged"
|
||||
@finish-record="onFinishRecorder"
|
||||
@play="recordingAudioState = 'playing'"
|
||||
@pause="recordingAudioState = 'paused'"
|
||||
/>
|
||||
<CopilotEditorSection
|
||||
v-if="copilot.isActive.value && !showAudioRecorderEditor"
|
||||
:show-copilot-editor="copilot.showEditor.value"
|
||||
:is-generating-content="copilot.isGenerating.value"
|
||||
:generated-content="copilot.generatedContent.value"
|
||||
:is-popout="popOutReplyBox"
|
||||
:placeholder="$t('CONVERSATION.FOOTER.COPILOT_MSG_INPUT')"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@clear-selection="clearEditorSelection"
|
||||
@close="copilot.showEditor.value = false"
|
||||
@content-ready="copilot.setContentReady"
|
||||
@send="copilot.sendFollowUp"
|
||||
/>
|
||||
<WootMessageEditor
|
||||
v-else-if="!showAudioRecorderEditor"
|
||||
v-model="message"
|
||||
:editor-id="editorStateId"
|
||||
class="input popover-prosemirror-menu"
|
||||
:is-private="isOnPrivateNote"
|
||||
:placeholder="messagePlaceHolder"
|
||||
:update-selection-with="updateEditorSelectionWith"
|
||||
:min-height="4"
|
||||
enable-variables
|
||||
:variables="messageVariables"
|
||||
:signature="messageSignature"
|
||||
allow-signature
|
||||
:channel-type="channelType"
|
||||
:medium="inbox.medium"
|
||||
@typing-off="onTypingOff"
|
||||
@typing-on="onTypingOn"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@toggle-user-mention="toggleUserMention"
|
||||
@toggle-canned-menu="toggleCannedMenu"
|
||||
@toggle-variables-menu="toggleVariablesMenu"
|
||||
@clear-selection="clearEditorSelection"
|
||||
@execute-copilot-action="executeCopilotAction"
|
||||
/>
|
||||
|
||||
<QuotedEmailPreview
|
||||
v-if="shouldShowQuotedPreview && isDefaultEditorMode"
|
||||
:quoted-email-text="quotedEmailText"
|
||||
:preview-text="quotedEmailPreviewText"
|
||||
class="mb-2"
|
||||
@toggle="toggleQuotedReply"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="hasAttachments && isDefaultEditorMode"
|
||||
class="bg-transparent py-0 mb-2"
|
||||
@paste="onPaste"
|
||||
>
|
||||
<AttachmentPreview
|
||||
class="mt-2"
|
||||
:attachments="attachedFiles"
|
||||
@remove-attachment="removeAttachment"
|
||||
/>
|
||||
</div>
|
||||
<MessageSignatureMissingAlert
|
||||
v-if="
|
||||
isSignatureEnabledForInbox &&
|
||||
!isSignatureAvailable &&
|
||||
isDefaultEditorMode
|
||||
"
|
||||
class="mb-2"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
mode="out-in"
|
||||
enter-active-class="transition-all duration-300 ease-out"
|
||||
enter-from-class="opacity-0 translate-y-2 scale-[0.98]"
|
||||
enter-to-class="opacity-100 translate-y-0 scale-100"
|
||||
leave-active-class="transition-all duration-200 ease-in"
|
||||
leave-from-class="opacity-100 translate-y-0 scale-100"
|
||||
leave-to-class="opacity-0 translate-y-2 scale-[0.98]"
|
||||
>
|
||||
<CopilotReplyBottomPanel
|
||||
v-if="copilot.isActive.value"
|
||||
key="copilot-bottom-panel"
|
||||
:is-generating-content="copilot.isButtonDisabled.value"
|
||||
@submit="onSubmitCopilotReply"
|
||||
@cancel="copilot.reset"
|
||||
/>
|
||||
</div>
|
||||
<MessageSignatureMissingAlert
|
||||
v-if="isSignatureEnabledForInbox && !isSignatureAvailable"
|
||||
/>
|
||||
<ReplyBottomPanel
|
||||
:conversation-id="conversationId"
|
||||
:enable-multiple-file-upload="enableMultipleFileUpload"
|
||||
:enable-whats-app-templates="showWhatsappTemplates"
|
||||
:enable-content-templates="showContentTemplates"
|
||||
:inbox="inbox"
|
||||
:is-on-private-note="isOnPrivateNote"
|
||||
:is-recording-audio="isRecordingAudio"
|
||||
:is-send-disabled="isReplyButtonDisabled"
|
||||
:is-note="isPrivate"
|
||||
:on-file-upload="onFileUpload"
|
||||
:on-send="onSendReply"
|
||||
:conversation-type="conversationType"
|
||||
:recording-audio-duration-text="recordingAudioDurationText"
|
||||
:recording-audio-state="recordingAudioState"
|
||||
:send-button-text="replyButtonLabel"
|
||||
:show-audio-recorder="showAudioRecorder"
|
||||
:show-emoji-picker="showEmojiPicker"
|
||||
:show-file-upload="showFileUpload"
|
||||
:show-quoted-reply-toggle="shouldShowQuotedReplyToggle"
|
||||
:quoted-reply-enabled="quotedReplyPreference"
|
||||
:toggle-audio-recorder-play-pause="toggleAudioRecorderPlayPause"
|
||||
:toggle-audio-recorder="toggleAudioRecorder"
|
||||
:toggle-emoji-picker="toggleEmojiPicker"
|
||||
:message="message"
|
||||
:portal-slug="connectedPortalSlug"
|
||||
:new-conversation-modal-active="newConversationModalActive"
|
||||
allow-signature
|
||||
allow-emoji
|
||||
allow-ai-assist
|
||||
allow-video-call
|
||||
allow-audio-recorder
|
||||
allow-file-upload
|
||||
@select-whatsapp-template="openWhatsappTemplateModal"
|
||||
@select-content-template="openContentTemplateModal"
|
||||
@replace-text="replaceText"
|
||||
@toggle-insert-article="toggleInsertArticle"
|
||||
@toggle-quoted-reply="toggleQuotedReply"
|
||||
/>
|
||||
<ReplyBottomPanel
|
||||
v-else
|
||||
key="reply-bottom-panel"
|
||||
:conversation-id="conversationId"
|
||||
:enable-multiple-file-upload="enableMultipleFileUpload"
|
||||
:enable-whats-app-templates="showWhatsappTemplates"
|
||||
:enable-content-templates="showContentTemplates"
|
||||
:inbox="inbox"
|
||||
:is-on-private-note="isOnPrivateNote"
|
||||
:is-recording-audio="isRecordingAudio"
|
||||
:is-send-disabled="isReplyButtonDisabled"
|
||||
:is-note="isPrivate"
|
||||
:on-file-upload="onFileUpload"
|
||||
:on-send="onSendReply"
|
||||
:conversation-type="conversationType"
|
||||
:recording-audio-duration-text="recordingAudioDurationText"
|
||||
:recording-audio-state="recordingAudioState"
|
||||
:send-button-text="replyButtonLabel"
|
||||
:show-audio-recorder="showAudioRecorder"
|
||||
:show-emoji-picker="showEmojiPicker"
|
||||
:show-file-upload="showFileUpload"
|
||||
:show-quoted-reply-toggle="shouldShowQuotedReplyToggle"
|
||||
:quoted-reply-enabled="quotedReplyPreference"
|
||||
:toggle-audio-recorder-play-pause="toggleAudioRecorderPlayPause"
|
||||
:toggle-audio-recorder="toggleAudioRecorder"
|
||||
:toggle-emoji-picker="toggleEmojiPicker"
|
||||
:message="message"
|
||||
:portal-slug="connectedPortalSlug"
|
||||
:new-conversation-modal-active="newConversationModalActive"
|
||||
@select-whatsapp-template="openWhatsappTemplateModal"
|
||||
@select-content-template="openContentTemplateModal"
|
||||
@replace-text="replaceText"
|
||||
@toggle-insert-article="toggleInsertArticle"
|
||||
@toggle-quoted-reply="toggleQuotedReply"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<WhatsappTemplates
|
||||
:inbox-id="inbox.id"
|
||||
:show="showWhatsAppTemplatesModal"
|
||||
@@ -1355,13 +1339,7 @@ export default {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
.attachment-preview-box {
|
||||
@apply bg-transparent py-0 px-4;
|
||||
}
|
||||
|
||||
.reply-box {
|
||||
transition: height 2s cubic-bezier(0.37, 0, 0.63, 1);
|
||||
|
||||
@apply relative mb-2 mx-2 border border-n-weak rounded-xl bg-n-solid-1;
|
||||
|
||||
&.is-private {
|
||||
@@ -1375,10 +1353,6 @@ export default {
|
||||
|
||||
.reply-box__top {
|
||||
@apply relative py-0 px-4 -mt-px;
|
||||
|
||||
textarea {
|
||||
@apply shadow-none outline-none border-transparent bg-transparent m-0 max-h-60 min-h-[3rem] pt-4 pb-0 px-0 resize-none;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-dialog {
|
||||
@@ -1398,9 +1372,4 @@ export default {
|
||||
@apply ltr:left-1 rtl:right-1 -bottom-2;
|
||||
}
|
||||
}
|
||||
|
||||
.normal-editor__canned-box {
|
||||
width: calc(100% - 2 * 1rem);
|
||||
left: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -78,14 +78,6 @@ const filterTypes = [
|
||||
filterOperators: OPERATOR_TYPES_1,
|
||||
attributeModel: 'additional',
|
||||
},
|
||||
{
|
||||
attributeKey: 'country_code',
|
||||
attributeI18nKey: 'COUNTRY_NAME',
|
||||
inputType: 'search_select',
|
||||
dataType: 'text',
|
||||
filterOperators: OPERATOR_TYPES_1,
|
||||
attributeModel: 'additional',
|
||||
},
|
||||
{
|
||||
attributeKey: 'referer',
|
||||
attributeI18nKey: 'REFERER_LINK',
|
||||
@@ -171,10 +163,6 @@ export const filterAttributeGroups = [
|
||||
key: 'browser_language',
|
||||
i18nKey: 'BROWSER_LANGUAGE',
|
||||
},
|
||||
{
|
||||
key: 'country_code',
|
||||
i18nKey: 'COUNTRY_NAME',
|
||||
},
|
||||
{
|
||||
key: 'referer',
|
||||
i18nKey: 'REFERER_LINK',
|
||||
|
||||
+4
@@ -515,6 +515,10 @@ const languages = [
|
||||
name: 'Portuguese',
|
||||
id: 'pt',
|
||||
},
|
||||
{
|
||||
name: 'Portuguese (Brazil)',
|
||||
id: 'pt_BR',
|
||||
},
|
||||
{
|
||||
name: 'Punjabi',
|
||||
id: 'pa',
|
||||
|
||||
+7
-7
@@ -4,7 +4,7 @@ import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
|
||||
|
||||
// composables
|
||||
import { useAI } from 'dashboard/composables/useAI';
|
||||
import { useCaptain } from 'dashboard/composables/useCaptain';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
|
||||
// store & api
|
||||
@@ -13,7 +13,7 @@ import { mapGetters } from 'vuex';
|
||||
// utils & constants
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||
import { OPEN_AI_EVENTS } from '../../../../helper/AnalyticsHelper/events';
|
||||
import { CAPTAIN_EVENTS } from '../../../../helper/AnalyticsHelper/events';
|
||||
|
||||
export default {
|
||||
name: 'LabelSuggestion',
|
||||
@@ -33,9 +33,9 @@ export default {
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { isAIIntegrationEnabled } = useAI();
|
||||
const { captainTasksEnabled } = useCaptain();
|
||||
|
||||
return { isAIIntegrationEnabled };
|
||||
return { captainTasksEnabled };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
},
|
||||
shouldShowSuggestions() {
|
||||
if (this.isDismissed) return false;
|
||||
if (!this.isAIIntegrationEnabled) return false;
|
||||
if (!this.captainTasksEnabled) return false;
|
||||
|
||||
return this.preparedLabels.length && this.chatLabels.length === 0;
|
||||
},
|
||||
@@ -114,7 +114,7 @@ export default {
|
||||
|
||||
// dismiss this once the values are set
|
||||
this.isDismissed = true;
|
||||
this.trackLabelEvent(OPEN_AI_EVENTS.DISMISS_LABEL_SUGGESTION);
|
||||
this.trackLabelEvent(CAPTAIN_EVENTS.LABEL_SUGGESTION_DISMISSED);
|
||||
},
|
||||
isConversationDismissed() {
|
||||
return LocalStorage.getFlag(
|
||||
@@ -132,7 +132,7 @@ export default {
|
||||
conversationId: this.conversationId,
|
||||
labels: labelsToAdd,
|
||||
});
|
||||
this.trackLabelEvent(OPEN_AI_EVENTS.APPLY_LABEL_SUGGESTION);
|
||||
this.trackLabelEvent(CAPTAIN_EVENTS.LABEL_SUGGESTION_APPLIED);
|
||||
},
|
||||
trackLabelEvent(event) {
|
||||
const payload = {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup>
|
||||
import Icon from 'next/icon/Icon.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Icon v-once icon="i-woot-captain" class="jumping-logo" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.jumping-logo {
|
||||
transform-origin: center bottom;
|
||||
animation: jump 1s cubic-bezier(0.28, 0.84, 0.42, 1) infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes jump {
|
||||
0% {
|
||||
transform: translateY(0) scale(1, 1);
|
||||
}
|
||||
20% {
|
||||
transform: translateY(0) scale(1.05, 0.95);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-5px) scale(0.95, 1.05);
|
||||
}
|
||||
80% {
|
||||
transform: translateY(0) scale(1.02, 0.98);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0) scale(1, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+1
-1
@@ -60,7 +60,7 @@ const onClickTabChange = index => {
|
||||
<div class="flex flex-col h-auto overflow-auto">
|
||||
<div class="flex flex-col px-8 pb-4 mt-1">
|
||||
<woot-tabs
|
||||
class="ltr:[&>ul]:pl-0 rtl:[&>ul]:pr-0"
|
||||
class="ltr:[&>ul]:pl-0 rtl:[&>ul]:pr-0 h-10"
|
||||
:index="selectedTabIndex"
|
||||
@change="onClickTabChange"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user