feat: Add more AI options (#7502)
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
@@ -1,204 +1,80 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="isAIIntegrationEnabled" class="position-relative">
|
<div v-if="isAIIntegrationEnabled" class="position-relative">
|
||||||
<div v-if="!message">
|
<woot-button
|
||||||
<woot-button
|
v-tooltip.top-end="$t('INTEGRATION_SETTINGS.OPEN_AI.AI_ASSIST')"
|
||||||
v-if="isPrivateNote"
|
icon="wand"
|
||||||
v-tooltip.top-end="$t('INTEGRATION_SETTINGS.OPEN_AI.SUMMARY_TITLE')"
|
color-scheme="secondary"
|
||||||
icon="book-pulse"
|
variant="smooth"
|
||||||
color-scheme="secondary"
|
size="small"
|
||||||
variant="smooth"
|
@click="openAIAssist"
|
||||||
size="small"
|
/>
|
||||||
:is-loading="uiFlags.summarize"
|
<woot-modal
|
||||||
@click="processEvent('summarize')"
|
:show.sync="showAIAssistanceModal"
|
||||||
|
:on-close="hideAIAssistanceModal"
|
||||||
|
>
|
||||||
|
<AIAssistanceModal
|
||||||
|
:ai-option="aiOption"
|
||||||
|
@apply-text="insertText"
|
||||||
|
@close="hideAIAssistanceModal"
|
||||||
/>
|
/>
|
||||||
<woot-button
|
</woot-modal>
|
||||||
v-else
|
|
||||||
v-tooltip.top-end="$t('INTEGRATION_SETTINGS.OPEN_AI.REPLY_TITLE')"
|
|
||||||
icon="wand"
|
|
||||||
color-scheme="secondary"
|
|
||||||
variant="smooth"
|
|
||||||
size="small"
|
|
||||||
:is-loading="uiFlags.reply_suggestion"
|
|
||||||
@click="processEvent('reply_suggestion')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else>
|
|
||||||
<woot-button
|
|
||||||
v-tooltip.top-end="$t('INTEGRATION_SETTINGS.OPEN_AI.TITLE')"
|
|
||||||
icon="text-grammar-wand"
|
|
||||||
color-scheme="secondary"
|
|
||||||
variant="smooth"
|
|
||||||
size="small"
|
|
||||||
@click="toggleDropdown"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="showDropdown"
|
|
||||||
v-on-clickaway="closeDropdown"
|
|
||||||
class="dropdown-pane dropdown-pane--open ai-modal"
|
|
||||||
>
|
|
||||||
<h4 class="sub-block-title margin-top-1">
|
|
||||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.TITLE') }}
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.SUBTITLE') }}
|
|
||||||
</p>
|
|
||||||
<label>
|
|
||||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.TONE.TITLE') }}
|
|
||||||
</label>
|
|
||||||
<div class="tone__item">
|
|
||||||
<select v-model="activeTone" class="status--filter small">
|
|
||||||
<option v-for="tone in tones" :key="tone.key" :value="tone.key">
|
|
||||||
{{ tone.value }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer flex-container align-right">
|
|
||||||
<woot-button variant="clear" size="small" @click="closeDropdown">
|
|
||||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.BUTTONS.CANCEL') }}
|
|
||||||
</woot-button>
|
|
||||||
<woot-button
|
|
||||||
:is-loading="uiFlags.rephrase"
|
|
||||||
size="small"
|
|
||||||
@click="processEvent('rephrase')"
|
|
||||||
>
|
|
||||||
{{ buttonText }}
|
|
||||||
</woot-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mapGetters } from 'vuex';
|
||||||
import OpenAPI from 'dashboard/api/integrations/openapi';
|
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import AIAssistanceModal from './AIAssistanceModal.vue';
|
||||||
import aiMixin from 'dashboard/mixins/aiMixin';
|
import aiMixin from 'dashboard/mixins/aiMixin';
|
||||||
|
import { CMD_AI_ASSIST } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||||
import { buildHotKeys } from 'shared/helpers/KeyboardHelpers';
|
import { buildHotKeys } from 'shared/helpers/KeyboardHelpers';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [aiMixin, alertMixin, clickaway, eventListenerMixins],
|
components: {
|
||||||
props: {
|
AIAssistanceModal,
|
||||||
conversationId: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
isPrivateNote: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
uiFlags: {
|
|
||||||
rephrase: false,
|
|
||||||
reply_suggestion: false,
|
|
||||||
summarize: false,
|
|
||||||
},
|
|
||||||
showDropdown: false,
|
|
||||||
activeTone: 'professional',
|
|
||||||
initialMessage: '',
|
|
||||||
tones: [
|
|
||||||
{
|
|
||||||
key: 'professional',
|
|
||||||
value: this.$t(
|
|
||||||
'INTEGRATION_SETTINGS.OPEN_AI.TONE.OPTIONS.PROFESSIONAL'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'friendly',
|
|
||||||
value: this.$t('INTEGRATION_SETTINGS.OPEN_AI.TONE.OPTIONS.FRIENDLY'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
mixins: [aiMixin, eventListenerMixins],
|
||||||
|
data: () => ({
|
||||||
|
showAIAssistanceModal: false,
|
||||||
|
aiOption: '',
|
||||||
|
initialMessage: '',
|
||||||
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
buttonText() {
|
...mapGetters({
|
||||||
return this.uiFlags.isRephrasing
|
currentChat: 'getSelectedChat',
|
||||||
? this.$t('INTEGRATION_SETTINGS.OPEN_AI.BUTTONS.GENERATING')
|
}),
|
||||||
: this.$t('INTEGRATION_SETTINGS.OPEN_AI.BUTTONS.GENERATE');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
bus.$on(CMD_AI_ASSIST, this.onAIAssist);
|
||||||
|
this.initialMessage = this.draftMessage;
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onKeyDownHandler(event) {
|
onKeyDownHandler(event) {
|
||||||
const keyPattern = buildHotKeys(event);
|
const keyPattern = buildHotKeys(event);
|
||||||
const shouldRevertTheContent =
|
const shouldRevertTheContent =
|
||||||
['meta+z', 'ctrl+z'].includes(keyPattern) && !!this.initialMessage;
|
['meta+z', 'ctrl+z'].includes(keyPattern) && !!this.initialMessage;
|
||||||
|
|
||||||
if (shouldRevertTheContent) {
|
if (shouldRevertTheContent) {
|
||||||
this.$emit('replace-text', this.initialMessage);
|
this.$emit('replace-text', this.initialMessage);
|
||||||
this.initialMessage = '';
|
this.initialMessage = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleDropdown() {
|
hideAIAssistanceModal() {
|
||||||
this.showDropdown = !this.showDropdown;
|
this.showAIAssistanceModal = false;
|
||||||
},
|
},
|
||||||
closeDropdown() {
|
openAIAssist() {
|
||||||
this.showDropdown = false;
|
this.initialMessage = this.draftMessage;
|
||||||
|
const ninja = document.querySelector('ninja-keys');
|
||||||
|
ninja.open({ parent: 'ai_assist' });
|
||||||
},
|
},
|
||||||
async processEvent(type = 'rephrase') {
|
onAIAssist(option) {
|
||||||
this.uiFlags[type] = true;
|
this.aiOption = option;
|
||||||
try {
|
this.showAIAssistanceModal = true;
|
||||||
const result = await OpenAPI.processEvent({
|
},
|
||||||
hookId: this.hookId,
|
insertText(message) {
|
||||||
type,
|
this.$emit('replace-text', message);
|
||||||
content: this.message,
|
|
||||||
tone: this.activeTone,
|
|
||||||
conversationId: this.conversationId,
|
|
||||||
});
|
|
||||||
const {
|
|
||||||
data: { message: generatedMessage },
|
|
||||||
} = result;
|
|
||||||
this.initialMessage = this.message;
|
|
||||||
this.$emit('replace-text', generatedMessage || this.message);
|
|
||||||
this.closeDropdown();
|
|
||||||
this.recordAnalytics(type, { tone: this.activeTone });
|
|
||||||
} catch (error) {
|
|
||||||
this.showAlert(this.$t('INTEGRATION_SETTINGS.OPEN_AI.GENERATE_ERROR'));
|
|
||||||
} finally {
|
|
||||||
this.uiFlags[type] = false;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.ai-modal {
|
|
||||||
width: 400px;
|
|
||||||
right: 0;
|
|
||||||
left: 0;
|
|
||||||
padding: var(--space-normal);
|
|
||||||
bottom: 34px;
|
|
||||||
position: absolute;
|
|
||||||
span {
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
font-weight: var(--font-weight-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: var(--s-600);
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
margin-bottom: var(--space-smaller);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status--filter {
|
|
||||||
background-color: var(--color-background-light);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
height: var(--space-large);
|
|
||||||
padding: 0 var(--space-medium) 0 var(--space-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
gap: var(--space-smaller);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div class="column">
|
||||||
|
<woot-modal-header :header-title="headerTitle" />
|
||||||
|
<form class="row modal-content" @submit.prevent="applyText">
|
||||||
|
<div v-if="draftMessage" class="w-full">
|
||||||
|
<h4 class="sub-block-title margin-top-1 ">
|
||||||
|
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.DRAFT_TITLE') }}
|
||||||
|
</h4>
|
||||||
|
<p v-dompurify-html="formatMessage(draftMessage, false)" />
|
||||||
|
<h4 class="sub-block-title margin-top-1">
|
||||||
|
{{
|
||||||
|
$t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.GENERATED_TITLE')
|
||||||
|
}}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<AILoader v-if="isGenerating" />
|
||||||
|
<p v-else v-dompurify-html="formatMessage(generatedContent, false)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer justify-content-end w-full">
|
||||||
|
<woot-button variant="clear" @click.prevent="onClose">
|
||||||
|
{{
|
||||||
|
$t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.BUTTONS.CANCEL')
|
||||||
|
}}
|
||||||
|
</woot-button>
|
||||||
|
<woot-button :disabled="!generatedContent">
|
||||||
|
{{
|
||||||
|
$t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.BUTTONS.APPLY')
|
||||||
|
}}
|
||||||
|
</woot-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
||||||
|
import AILoader from './AILoader.vue';
|
||||||
|
import aiMixin from 'dashboard/mixins/aiMixin';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
AILoader,
|
||||||
|
},
|
||||||
|
mixins: [aiMixin, messageFormatterMixin],
|
||||||
|
props: {
|
||||||
|
aiOption: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
generatedContent: '',
|
||||||
|
isGenerating: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
appIntegrations: 'integrations/getAppIntegrations',
|
||||||
|
}),
|
||||||
|
headerTitle() {
|
||||||
|
const translationKey = this.aiOption?.toUpperCase();
|
||||||
|
return translationKey
|
||||||
|
? this.$t(`INTEGRATION_SETTINGS.OPEN_AI.WITH_AI`, {
|
||||||
|
option: this.$t(
|
||||||
|
`INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.${translationKey}`
|
||||||
|
),
|
||||||
|
})
|
||||||
|
: '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.generateAIContent(this.aiOption);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onClose() {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
|
||||||
|
async generateAIContent(type = 'rephrase') {
|
||||||
|
this.isGenerating = true;
|
||||||
|
this.generatedContent = await this.processEvent(type);
|
||||||
|
this.isGenerating = false;
|
||||||
|
},
|
||||||
|
applyText() {
|
||||||
|
this.recordAnalytics(this.aiOption);
|
||||||
|
this.$emit('apply-text', this.generatedContent);
|
||||||
|
this.onClose();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.modal-content {
|
||||||
|
padding-top: var(--space-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
67
app/javascript/dashboard/components/widgets/AILoader.vue
Normal file
67
app/javascript/dashboard/components/widgets/AILoader.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<div class="animation-container margin-top-1">
|
||||||
|
<div class="ai-typing--wrap ">
|
||||||
|
<fluent-icon icon="wand" size="14" class="ai-typing--icon" />
|
||||||
|
<label>
|
||||||
|
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.AI_WRITING') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<span class="loader" />
|
||||||
|
<span class="loader" />
|
||||||
|
<span class="loader" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script></script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.animation-container {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
.ai-typing--wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.ai-typing--icon {
|
||||||
|
color: var(--v-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: var(--space-smaller);
|
||||||
|
color: var(--v-400);
|
||||||
|
}
|
||||||
|
.loader {
|
||||||
|
display: inline-block;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
margin-right: var(--space-smaller);
|
||||||
|
margin-top: var(--space-slab);
|
||||||
|
background-color: var(--v-300);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: bubble-scale 1.2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader:nth-child(2) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader:nth-child(3) {
|
||||||
|
animation-delay: 0.8s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bubble-scale {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: scale(1.3);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -112,10 +112,6 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
conversationId: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -81,6 +81,12 @@ export const OPEN_AI_EVENTS = Object.freeze({
|
|||||||
SUMMARIZE: 'OpenAI: Used summarize',
|
SUMMARIZE: 'OpenAI: Used summarize',
|
||||||
REPLY_SUGGESTION: 'OpenAI: Used reply suggestion',
|
REPLY_SUGGESTION: 'OpenAI: Used reply suggestion',
|
||||||
REPHRASE: 'OpenAI: Used rephrase',
|
REPHRASE: 'OpenAI: Used rephrase',
|
||||||
|
FIX_SPELLING_AND_GRAMMAR: 'OpenAI: Used fix spelling and grammar',
|
||||||
|
SHORTEN: 'OpenAI: Used shorten',
|
||||||
|
EXPAND: 'OpenAI: Used expand',
|
||||||
|
MAKE_FRIENDLY: 'OpenAI: Used make friendly',
|
||||||
|
MAKE_FORMAL: 'OpenAI: Used make formal',
|
||||||
|
SIMPLIFY: 'OpenAI: Used simplify',
|
||||||
APPLY_LABEL_SUGGESTION: 'OpenAI: Apply label from suggestion',
|
APPLY_LABEL_SUGGESTION: 'OpenAI: Apply label from suggestion',
|
||||||
DISMISS_LABEL_SUGGESTION: 'OpenAI: Dismiss label suggestions',
|
DISMISS_LABEL_SUGGESTION: 'OpenAI: Dismiss label suggestions',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -110,7 +110,8 @@
|
|||||||
"SNOOZE_CONVERSATION": "Snooze Conversation",
|
"SNOOZE_CONVERSATION": "Snooze Conversation",
|
||||||
"ADD_LABEL": "Add label to the conversation",
|
"ADD_LABEL": "Add label to the conversation",
|
||||||
"REMOVE_LABEL": "Remove label from the conversation",
|
"REMOVE_LABEL": "Remove label from the conversation",
|
||||||
"SETTINGS": "Settings"
|
"SETTINGS": "Settings",
|
||||||
|
"AI_ASSIST": "AI Assist"
|
||||||
},
|
},
|
||||||
"COMMANDS": {
|
"COMMANDS": {
|
||||||
"GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard",
|
"GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard",
|
||||||
@@ -132,6 +133,7 @@
|
|||||||
"GO_TO_NOTIFICATIONS": "Go to Notifications",
|
"GO_TO_NOTIFICATIONS": "Go to Notifications",
|
||||||
"ADD_LABELS_TO_CONVERSATION": "Add label to the conversation",
|
"ADD_LABELS_TO_CONVERSATION": "Add label to the conversation",
|
||||||
"ASSIGN_AN_AGENT": "Assign an agent",
|
"ASSIGN_AN_AGENT": "Assign an agent",
|
||||||
|
"AI_ASSIST": "AI Assist",
|
||||||
"ASSIGN_PRIORITY": "Assign priority",
|
"ASSIGN_PRIORITY": "Assign priority",
|
||||||
"ASSIGN_A_TEAM": "Assign a team",
|
"ASSIGN_A_TEAM": "Assign a team",
|
||||||
"MUTE_CONVERSATION": "Mute conversation",
|
"MUTE_CONVERSATION": "Mute conversation",
|
||||||
|
|||||||
@@ -83,6 +83,28 @@
|
|||||||
"CREATE_ERROR": "There was an error creating a meeting link, please try again"
|
"CREATE_ERROR": "There was an error creating a meeting link, please try again"
|
||||||
},
|
},
|
||||||
"OPEN_AI": {
|
"OPEN_AI": {
|
||||||
|
"AI_ASSIST": "AI Assist",
|
||||||
|
"WITH_AI": " %{option} with AI ",
|
||||||
|
"OPTIONS": {
|
||||||
|
"REPLY_SUGGESTION": "Reply Suggestion",
|
||||||
|
"SUMMARIZE": "Summarize",
|
||||||
|
"REPHRASE": "Improve Writing",
|
||||||
|
"FIX_SPELLING_GRAMMAR": "Fix Spelling and Grammar",
|
||||||
|
"SHORTEN": "Shorten",
|
||||||
|
"EXPAND": "Expand",
|
||||||
|
"MAKE_FRIENDLY": "Change message tone to friendly",
|
||||||
|
"MAKE_FORMAL": "Use formal tone",
|
||||||
|
"SIMPLIFY": "Simplify"
|
||||||
|
},
|
||||||
|
"ASSISTANCE_MODAL": {
|
||||||
|
"DRAFT_TITLE": "Draft content",
|
||||||
|
"GENERATED_TITLE": "Generated content",
|
||||||
|
"AI_WRITING": "AI is writing",
|
||||||
|
"BUTTONS": {
|
||||||
|
"APPLY": "Use this suggestion",
|
||||||
|
"CANCEL": "Cancel"
|
||||||
|
}
|
||||||
|
},
|
||||||
"TITLE": "Improve With AI",
|
"TITLE": "Improve With AI",
|
||||||
"SUMMARY_TITLE": "Summary with AI",
|
"SUMMARY_TITLE": "Summary with AI",
|
||||||
"REPLY_TITLE": "Reply suggestion with AI",
|
"REPLY_TITLE": "Reply suggestion with AI",
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ export default {
|
|||||||
this.fetchIntegrationsIfRequired();
|
this.fetchIntegrationsIfRequired();
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({ appIntegrations: 'integrations/getAppIntegrations' }),
|
...mapGetters({
|
||||||
|
appIntegrations: 'integrations/getAppIntegrations',
|
||||||
|
currentChat: 'getSelectedChat',
|
||||||
|
replyMode: 'draftMessages/getReplyEditorMode',
|
||||||
|
}),
|
||||||
isAIIntegrationEnabled() {
|
isAIIntegrationEnabled() {
|
||||||
return this.appIntegrations.find(
|
return this.appIntegrations.find(
|
||||||
integration => integration.id === 'openai' && !!integration.hooks.length
|
integration => integration.id === 'openai' && !!integration.hooks.length
|
||||||
@@ -20,6 +24,15 @@ export default {
|
|||||||
integration => integration.id === 'openai' && !!integration.hooks.length
|
integration => integration.id === 'openai' && !!integration.hooks.length
|
||||||
).hooks[0].id;
|
).hooks[0].id;
|
||||||
},
|
},
|
||||||
|
draftMessage() {
|
||||||
|
return this.$store.getters['draftMessages/get'](this.draftKey);
|
||||||
|
},
|
||||||
|
draftKey() {
|
||||||
|
return `draft-${this.conversationId}-${this.replyMode}`;
|
||||||
|
},
|
||||||
|
conversationId() {
|
||||||
|
return this.currentChat?.id;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async fetchIntegrationsIfRequired() {
|
async fetchIntegrationsIfRequired() {
|
||||||
@@ -84,5 +97,22 @@ export default {
|
|||||||
.map(label => label.trim()) // trim the words
|
.map(label => label.trim()) // trim the words
|
||||||
.filter((label, index, self) => self.indexOf(label) === index); // remove any duplicates
|
.filter((label, index, self) => self.indexOf(label) === index); // remove any duplicates
|
||||||
},
|
},
|
||||||
|
async processEvent(type = 'rephrase') {
|
||||||
|
try {
|
||||||
|
const result = await OpenAPI.processEvent({
|
||||||
|
hookId: this.hookId,
|
||||||
|
type,
|
||||||
|
content: this.draftMessage,
|
||||||
|
conversationId: this.conversationId,
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
data: { message: generatedMessage },
|
||||||
|
} = result;
|
||||||
|
return generatedMessage;
|
||||||
|
} catch (error) {
|
||||||
|
this.showAlert(this.$t('INTEGRATION_SETTINGS.OPEN_AI.GENERATE_ERROR'));
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,3 +58,11 @@ export const ICON_PRIORITY_NONE = `<svg role="img" class="ninja-icon ninja-icon-
|
|||||||
<rect width="24" height="24" fill="#F1F5F8"/>
|
<rect width="24" height="24" fill="#F1F5F8"/>
|
||||||
<path d="M13.5686 8L11.1579 16.9562H10L12.4107 8H13.5686Z" fill="#446888"/>
|
<path d="M13.5686 8L11.1579 16.9562H10L12.4107 8H13.5686Z" fill="#446888"/>
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
|
export const ICON_AI_ASSIST = `<svg role="img" class="ninja-icon ninja-icon--fluent" width="18" height="18" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m13.314 7.565l-.136.126l-10.48 10.488a2.27 2.27 0 0 0 3.211 3.208L16.388 10.9a2.251 2.251 0 0 0-.001-3.182l-.157-.146a2.25 2.25 0 0 0-2.916-.007Zm-.848 2.961l1.088 1.088l-8.706 8.713a.77.77 0 1 1-1.089-1.088l8.707-8.713Zm4.386 4.48L16.75 15a.75.75 0 0 0-.743.648L16 15.75v.75h-.75a.75.75 0 0 0-.743.648l-.007.102c0 .38.282.694.648.743l.102.007H16v.75c0 .38.282.694.648.743l.102.007a.75.75 0 0 0 .743-.648l.007-.102V18h.75a.75.75 0 0 0 .743-.648L19 17.25a.75.75 0 0 0-.648-.743l-.102-.007h-.75v-.75a.75.75 0 0 0-.648-.743L16.75 15l.102.007Zm-1.553-6.254l.027.027a.751.751 0 0 1 0 1.061l-.711.713l-1.089-1.089l.73-.73a.75.75 0 0 1 1.043.018ZM6.852 5.007L6.75 5a.75.75 0 0 0-.743.648L6 5.75v.75h-.75a.75.75 0 0 0-.743.648L4.5 7.25c0 .38.282.693.648.743L5.25 8H6v.75c0 .38.282.693.648.743l.102.007a.75.75 0 0 0 .743-.648L7.5 8.75V8h.75a.75.75 0 0 0 .743-.648L9 7.25a.75.75 0 0 0-.648-.743L8.25 6.5H7.5v-.75a.75.75 0 0 0-.648-.743L6.75 5l.102.007Zm12-2L18.75 3a.75.75 0 0 0-.743.648L18 3.75v.75h-.75a.75.75 0 0 0-.743.648l-.007.102c0 .38.282.693.648.743L17.25 6H18v.75c0 .38.282.693.648.743l.102.007a.75.75 0 0 0 .743-.648l.007-.102V6h.75a.75.75 0 0 0 .743-.648L21 5.25a.75.75 0 0 0-.648-.743L20.25 4.5h-.75v-.75a.75.75 0 0 0-.648-.743L18.75 3l.102.007Z" fill="currentColor"/></svg>`;
|
||||||
|
export const ICON_AI_SUMMARY = `<svg role="img" class="ninja-icon ninja-icon--fluent" width="18" height="18" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M4 4.5A2.5 2.5 0 0 1 6.5 2H18a2.5 2.5 0 0 1 2.5 2.5v14.25a.75.75 0 0 1-.75.75H5.5a1 1 0 0 0 1 1h13.25a.75.75 0 0 1 0 1.5H6.5A2.5 2.5 0 0 1 4 19.5v-15ZM5.5 18H19V4.5a1 1 0 0 0-1-1H6.5a1 1 0 0 0-1 1V18Z" fill="currentColor"/></svg>`;
|
||||||
|
export const ICON_AI_SPELLING = `<svg role="img" class="ninja-icon ninja-icon--fluent" width="18" height="18" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M4.53 12.97a.75.75 0 0 0-1.06 1.06l4.5 4.5a.75.75 0 0 0 1.06 0l11-11a.75.75 0 0 0-1.06-1.06L8.5 16.94l-3.97-3.97Z" fill="currentColor"/></svg>`;
|
||||||
|
|
||||||
|
export const ICON_AI_EXPAND = `<svg role="img" class="ninja-icon ninja-icon--fluent" width="18" height="18" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.75 19.5h14.5a.75.75 0 0 0 .102-1.493L21.25 18H6.75a.75.75 0 0 0-.102 1.493l.102.007Zm0-15h14.5a.75.75 0 0 0 .102-1.493L21.25 3H6.75a.75.75 0 0 0-.102 1.493l.102.007Zm7 3.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5h-7.5ZM13 13.75a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5a.75.75 0 0 1-.75-.75Zm-2-2.25a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0Zm-4-2a.5.5 0 0 0-1 0V11H4.5a.5.5 0 0 0 0 1H6v1.5a.5.5 0 0 0 1 0V12h1.5a.5.5 0 0 0 0-1H7V9.5Z" fill="currentColor"/></svg>`;
|
||||||
|
export const ICON_AI_SHORTEN = `<svg role="img" class="ninja-icon ninja-icon--fluent" width="18" height="18" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.75 4.5h14.5a.75.75 0 0 0 .102-1.493L21.25 3H6.75a.75.75 0 0 0-.102 1.493l.102.007Zm0 15h14.5a.75.75 0 0 0 .102-1.493L21.25 18H6.75a.75.75 0 0 0-.102 1.493l.102.007Zm7-11.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5h-7.5ZM13 13.75a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5a.75.75 0 0 1-.75-.75Zm-2-2.25a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0Zm-2 0a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 .5-.5Z" fill="currentColor"/></svg>`;
|
||||||
|
export const ICON_AI_GRAMMAR = `<svg role="img" class="ninja-icon ninja-icon--fluent" width="18" height="18" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 17h7.522l-2 2H3a1 1 0 0 1-.117-1.993L3 17Zm0-2h7.848a1.75 1.75 0 0 1-.775-2H3l-.117.007A1 1 0 0 0 3 15Zm0-8h18l.117-.007A1 1 0 0 0 21 5H3l-.117.007A1 1 0 0 0 3 7Zm9.72 9.216a.75.75 0 1 1 1.06 1.06l-4.5 4.5a.75.75 0 1 1-1.06-1.06l4.5-4.5ZM3 9h10a1 1 0 0 1 .117 1.993L13 11H3a1 1 0 0 1-.117-1.993L3 9Zm13.5-1a.75.75 0 0 1 .744.658l.14 1.13a3.25 3.25 0 0 0 2.828 2.829l1.13.139a.75.75 0 0 1 0 1.488l-1.13.14a3.25 3.25 0 0 0-2.829 2.828l-.139 1.13a.75.75 0 0 1-1.488 0l-.14-1.13a3.25 3.25 0 0 0-2.828-2.829l-1.13-.139a.75.75 0 0 1 0-1.488l1.13-.14a3.25 3.25 0 0 0 2.829-2.828l.139-1.13A.75.75 0 0 1 16.5 8Z" fill="currentColor"/></svg>`;
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CMD_MUTE_CONVERSATION,
|
||||||
|
CMD_REOPEN_CONVERSATION,
|
||||||
|
CMD_RESOLVE_CONVERSATION,
|
||||||
|
CMD_SEND_TRANSCRIPT,
|
||||||
|
CMD_SNOOZE_CONVERSATION,
|
||||||
|
CMD_UNMUTE_CONVERSATION,
|
||||||
|
} from './commandBarBusEvents';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ICON_MUTE_CONVERSATION,
|
||||||
|
ICON_REOPEN_CONVERSATION,
|
||||||
|
ICON_RESOLVE_CONVERSATION,
|
||||||
|
ICON_SEND_TRANSCRIPT,
|
||||||
|
ICON_SNOOZE_CONVERSATION,
|
||||||
|
ICON_UNMUTE_CONVERSATION,
|
||||||
|
} from './CommandBarIcons';
|
||||||
|
|
||||||
|
const SNOOZE_OPTIONS = wootConstants.SNOOZE_OPTIONS;
|
||||||
|
|
||||||
|
export const OPEN_CONVERSATION_ACTIONS = [
|
||||||
|
{
|
||||||
|
id: 'resolve_conversation',
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.RESOLVE_CONVERSATION',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
||||||
|
icon: ICON_RESOLVE_CONVERSATION,
|
||||||
|
handler: () => bus.$emit(CMD_RESOLVE_CONVERSATION),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const SNOOZE_CONVERSATION_ACTIONS = [
|
||||||
|
{
|
||||||
|
id: 'snooze_conversation',
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.SNOOZE_CONVERSATION',
|
||||||
|
icon: ICON_SNOOZE_CONVERSATION,
|
||||||
|
children: Object.values(SNOOZE_OPTIONS),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: SNOOZE_OPTIONS.UNTIL_NEXT_REPLY,
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_REPLY',
|
||||||
|
parent: 'snooze_conversation',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
||||||
|
icon: ICON_SNOOZE_CONVERSATION,
|
||||||
|
handler: () =>
|
||||||
|
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_REPLY),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: SNOOZE_OPTIONS.AN_HOUR_FROM_NOW,
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.AN_HOUR_FROM_NOW',
|
||||||
|
parent: 'snooze_conversation',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
||||||
|
icon: ICON_SNOOZE_CONVERSATION,
|
||||||
|
handler: () =>
|
||||||
|
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.AN_HOUR_FROM_NOW),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: SNOOZE_OPTIONS.UNTIL_TOMORROW,
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.UNTIL_TOMORROW',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
||||||
|
parent: 'snooze_conversation',
|
||||||
|
icon: ICON_SNOOZE_CONVERSATION,
|
||||||
|
handler: () =>
|
||||||
|
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_TOMORROW),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: SNOOZE_OPTIONS.UNTIL_NEXT_WEEK,
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_WEEK',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
||||||
|
parent: 'snooze_conversation',
|
||||||
|
icon: ICON_SNOOZE_CONVERSATION,
|
||||||
|
handler: () =>
|
||||||
|
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_WEEK),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: SNOOZE_OPTIONS.UNTIL_NEXT_MONTH,
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_MONTH',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
||||||
|
parent: 'snooze_conversation',
|
||||||
|
icon: ICON_SNOOZE_CONVERSATION,
|
||||||
|
handler: () =>
|
||||||
|
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_MONTH),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME,
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.CUSTOM',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
||||||
|
parent: 'snooze_conversation',
|
||||||
|
icon: ICON_SNOOZE_CONVERSATION,
|
||||||
|
handler: () =>
|
||||||
|
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const RESOLVED_CONVERSATION_ACTIONS = [
|
||||||
|
{
|
||||||
|
id: 'reopen_conversation',
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.REOPEN_CONVERSATION',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
||||||
|
icon: ICON_REOPEN_CONVERSATION,
|
||||||
|
handler: () => bus.$emit(CMD_REOPEN_CONVERSATION),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const SEND_TRANSCRIPT_ACTION = {
|
||||||
|
id: 'send_transcript',
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.SEND_TRANSCRIPT',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
||||||
|
icon: ICON_SEND_TRANSCRIPT,
|
||||||
|
handler: () => bus.$emit(CMD_SEND_TRANSCRIPT),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UNMUTE_ACTION = {
|
||||||
|
id: 'unmute_conversation',
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.UNMUTE_CONVERSATION',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
||||||
|
icon: ICON_UNMUTE_CONVERSATION,
|
||||||
|
handler: () => bus.$emit(CMD_UNMUTE_CONVERSATION),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MUTE_ACTION = {
|
||||||
|
id: 'mute_conversation',
|
||||||
|
title: 'COMMAND_BAR.COMMANDS.MUTE_CONVERSATION',
|
||||||
|
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
||||||
|
icon: ICON_MUTE_CONVERSATION,
|
||||||
|
handler: () => bus.$emit(CMD_MUTE_CONVERSATION),
|
||||||
|
};
|
||||||
@@ -14,3 +14,4 @@ export const CMD_TOGGLE_CONTACT_SIDEBAR = 'CMD_TOGGLE_CONTACT_SIDEBAR';
|
|||||||
export const CMD_REOPEN_CONVERSATION = 'CMD_REOPEN_CONVERSATION';
|
export const CMD_REOPEN_CONVERSATION = 'CMD_REOPEN_CONVERSATION';
|
||||||
export const CMD_RESOLVE_CONVERSATION = 'CMD_RESOLVE_CONVERSATION';
|
export const CMD_RESOLVE_CONVERSATION = 'CMD_RESOLVE_CONVERSATION';
|
||||||
export const CMD_SNOOZE_CONVERSATION = 'CMD_SNOOZE_CONVERSATION';
|
export const CMD_SNOOZE_CONVERSATION = 'CMD_SNOOZE_CONVERSATION';
|
||||||
|
export const CMD_AI_ASSIST = 'CMD_AI_ASSIST';
|
||||||
|
|||||||
@@ -1,142 +1,34 @@
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import {
|
|
||||||
CMD_MUTE_CONVERSATION,
|
|
||||||
CMD_REOPEN_CONVERSATION,
|
|
||||||
CMD_RESOLVE_CONVERSATION,
|
|
||||||
CMD_SEND_TRANSCRIPT,
|
|
||||||
CMD_SNOOZE_CONVERSATION,
|
|
||||||
CMD_UNMUTE_CONVERSATION,
|
|
||||||
} from './commandBarBusEvents';
|
|
||||||
|
|
||||||
|
import { CMD_AI_ASSIST } from './commandBarBusEvents';
|
||||||
|
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
||||||
import {
|
import {
|
||||||
ICON_ADD_LABEL,
|
ICON_ADD_LABEL,
|
||||||
ICON_ASSIGN_AGENT,
|
ICON_ASSIGN_AGENT,
|
||||||
ICON_ASSIGN_PRIORITY,
|
ICON_ASSIGN_PRIORITY,
|
||||||
ICON_ASSIGN_TEAM,
|
ICON_ASSIGN_TEAM,
|
||||||
ICON_MUTE_CONVERSATION,
|
|
||||||
ICON_REMOVE_LABEL,
|
ICON_REMOVE_LABEL,
|
||||||
ICON_REOPEN_CONVERSATION,
|
|
||||||
ICON_RESOLVE_CONVERSATION,
|
|
||||||
ICON_SEND_TRANSCRIPT,
|
|
||||||
ICON_SNOOZE_CONVERSATION,
|
|
||||||
ICON_UNMUTE_CONVERSATION,
|
|
||||||
ICON_PRIORITY_URGENT,
|
ICON_PRIORITY_URGENT,
|
||||||
ICON_PRIORITY_HIGH,
|
ICON_PRIORITY_HIGH,
|
||||||
ICON_PRIORITY_LOW,
|
ICON_PRIORITY_LOW,
|
||||||
ICON_PRIORITY_MEDIUM,
|
ICON_PRIORITY_MEDIUM,
|
||||||
ICON_PRIORITY_NONE,
|
ICON_PRIORITY_NONE,
|
||||||
|
ICON_AI_ASSIST,
|
||||||
|
ICON_AI_SUMMARY,
|
||||||
|
ICON_AI_SHORTEN,
|
||||||
|
ICON_AI_EXPAND,
|
||||||
|
ICON_AI_GRAMMAR,
|
||||||
} from './CommandBarIcons';
|
} from './CommandBarIcons';
|
||||||
|
|
||||||
const SNOOZE_OPTIONS = wootConstants.SNOOZE_OPTIONS;
|
import {
|
||||||
|
OPEN_CONVERSATION_ACTIONS,
|
||||||
const OPEN_CONVERSATION_ACTIONS = [
|
SNOOZE_CONVERSATION_ACTIONS,
|
||||||
{
|
RESOLVED_CONVERSATION_ACTIONS,
|
||||||
id: 'resolve_conversation',
|
SEND_TRANSCRIPT_ACTION,
|
||||||
title: 'COMMAND_BAR.COMMANDS.RESOLVE_CONVERSATION',
|
UNMUTE_ACTION,
|
||||||
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
MUTE_ACTION,
|
||||||
icon: ICON_RESOLVE_CONVERSATION,
|
} from './commandBarActions';
|
||||||
handler: () => bus.$emit(CMD_RESOLVE_CONVERSATION),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const SNOOZE_CONVERSATION_ACTIONS = [
|
|
||||||
{
|
|
||||||
id: 'snooze_conversation',
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.SNOOZE_CONVERSATION',
|
|
||||||
icon: ICON_SNOOZE_CONVERSATION,
|
|
||||||
children: Object.values(SNOOZE_OPTIONS),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
id: SNOOZE_OPTIONS.UNTIL_NEXT_REPLY,
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_REPLY',
|
|
||||||
parent: 'snooze_conversation',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
|
||||||
icon: ICON_SNOOZE_CONVERSATION,
|
|
||||||
handler: () =>
|
|
||||||
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_REPLY),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: SNOOZE_OPTIONS.AN_HOUR_FROM_NOW,
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.AN_HOUR_FROM_NOW',
|
|
||||||
parent: 'snooze_conversation',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
|
||||||
icon: ICON_SNOOZE_CONVERSATION,
|
|
||||||
handler: () =>
|
|
||||||
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.AN_HOUR_FROM_NOW),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: SNOOZE_OPTIONS.UNTIL_TOMORROW,
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.UNTIL_TOMORROW',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
|
||||||
parent: 'snooze_conversation',
|
|
||||||
icon: ICON_SNOOZE_CONVERSATION,
|
|
||||||
handler: () =>
|
|
||||||
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_TOMORROW),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: SNOOZE_OPTIONS.UNTIL_NEXT_WEEK,
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_WEEK',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
|
||||||
parent: 'snooze_conversation',
|
|
||||||
icon: ICON_SNOOZE_CONVERSATION,
|
|
||||||
handler: () =>
|
|
||||||
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_WEEK),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: SNOOZE_OPTIONS.UNTIL_NEXT_MONTH,
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.UNTIL_NEXT_MONTH',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
|
||||||
parent: 'snooze_conversation',
|
|
||||||
icon: ICON_SNOOZE_CONVERSATION,
|
|
||||||
handler: () =>
|
|
||||||
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_NEXT_MONTH),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME,
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.CUSTOM',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.SNOOZE_CONVERSATION',
|
|
||||||
parent: 'snooze_conversation',
|
|
||||||
icon: ICON_SNOOZE_CONVERSATION,
|
|
||||||
handler: () =>
|
|
||||||
bus.$emit(CMD_SNOOZE_CONVERSATION, SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const RESOLVED_CONVERSATION_ACTIONS = [
|
|
||||||
{
|
|
||||||
id: 'reopen_conversation',
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.REOPEN_CONVERSATION',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
|
||||||
icon: ICON_REOPEN_CONVERSATION,
|
|
||||||
handler: () => bus.$emit(CMD_REOPEN_CONVERSATION),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const SEND_TRANSCRIPT_ACTION = {
|
|
||||||
id: 'send_transcript',
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.SEND_TRANSCRIPT',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
|
||||||
icon: ICON_SEND_TRANSCRIPT,
|
|
||||||
handler: () => bus.$emit(CMD_SEND_TRANSCRIPT),
|
|
||||||
};
|
|
||||||
|
|
||||||
const UNMUTE_ACTION = {
|
|
||||||
id: 'unmute_conversation',
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.UNMUTE_CONVERSATION',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
|
||||||
icon: ICON_UNMUTE_CONVERSATION,
|
|
||||||
handler: () => bus.$emit(CMD_UNMUTE_CONVERSATION),
|
|
||||||
};
|
|
||||||
|
|
||||||
const MUTE_ACTION = {
|
|
||||||
id: 'mute_conversation',
|
|
||||||
title: 'COMMAND_BAR.COMMANDS.MUTE_CONVERSATION',
|
|
||||||
section: 'COMMAND_BAR.SECTIONS.CONVERSATION',
|
|
||||||
icon: ICON_MUTE_CONVERSATION,
|
|
||||||
handler: () => bus.$emit(CMD_MUTE_CONVERSATION),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isAConversationRoute = routeName =>
|
export const isAConversationRoute = routeName =>
|
||||||
[
|
[
|
||||||
@@ -162,9 +54,24 @@ export default {
|
|||||||
activeLabels() {
|
activeLabels() {
|
||||||
this.setCommandbarData();
|
this.setCommandbarData();
|
||||||
},
|
},
|
||||||
|
draftMessage() {
|
||||||
|
this.setCommandbarData();
|
||||||
|
},
|
||||||
|
replyMode() {
|
||||||
|
this.setCommandbarData();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({ currentChat: 'getSelectedChat' }),
|
...mapGetters({
|
||||||
|
currentChat: 'getSelectedChat',
|
||||||
|
replyMode: 'draftMessages/getReplyEditorMode',
|
||||||
|
}),
|
||||||
|
draftMessage() {
|
||||||
|
return this.$store.getters['draftMessages/get'](this.draftKey);
|
||||||
|
},
|
||||||
|
draftKey() {
|
||||||
|
return `draft-${this.conversationId}-${this.replyMode}`;
|
||||||
|
},
|
||||||
inboxId() {
|
inboxId() {
|
||||||
return this.currentChat?.inbox_id;
|
return this.currentChat?.inbox_id;
|
||||||
},
|
},
|
||||||
@@ -337,6 +244,94 @@ export default {
|
|||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
nonDraftMessageAIAssistActions() {
|
||||||
|
if (this.replyMode === REPLY_EDITOR_MODES.REPLY) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$t(
|
||||||
|
'INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.REPLY_SUGGESTION'
|
||||||
|
),
|
||||||
|
key: 'reply_suggestion',
|
||||||
|
icon: ICON_AI_ASSIST,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.SUMMARIZE'),
|
||||||
|
key: 'summarize',
|
||||||
|
icon: ICON_AI_SUMMARY,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
draftMessageAIAssistActions() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.REPHRASE'),
|
||||||
|
key: 'rephrase',
|
||||||
|
icon: ICON_AI_ASSIST,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t(
|
||||||
|
'INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.FIX_SPELLING_GRAMMAR'
|
||||||
|
),
|
||||||
|
key: 'fix_spelling_grammar',
|
||||||
|
icon: ICON_AI_GRAMMAR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.EXPAND'),
|
||||||
|
key: 'expand',
|
||||||
|
icon: ICON_AI_EXPAND,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.SHORTEN'),
|
||||||
|
key: 'shorten',
|
||||||
|
icon: ICON_AI_SHORTEN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.MAKE_FRIENDLY'),
|
||||||
|
key: 'make_friendly',
|
||||||
|
icon: ICON_AI_ASSIST,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.MAKE_FORMAL'),
|
||||||
|
key: 'make_formal',
|
||||||
|
icon: ICON_AI_ASSIST,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.SIMPLIFY'),
|
||||||
|
key: 'simplify',
|
||||||
|
icon: ICON_AI_ASSIST,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
AIAssistActions() {
|
||||||
|
const aiOptions = this.draftMessage
|
||||||
|
? this.draftMessageAIAssistActions
|
||||||
|
: this.nonDraftMessageAIAssistActions;
|
||||||
|
const options = aiOptions.map(item => ({
|
||||||
|
id: `ai-assist-${item.key}`,
|
||||||
|
title: item.label,
|
||||||
|
parent: 'ai_assist',
|
||||||
|
section: this.$t('COMMAND_BAR.SECTIONS.AI_ASSIST'),
|
||||||
|
priority: item,
|
||||||
|
icon: item.icon,
|
||||||
|
handler: () => bus.$emit(CMD_AI_ASSIST, item.key),
|
||||||
|
}));
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'ai_assist',
|
||||||
|
title: this.$t('COMMAND_BAR.COMMANDS.AI_ASSIST'),
|
||||||
|
section: this.$t('COMMAND_BAR.SECTIONS.AI_ASSIST'),
|
||||||
|
icon: ICON_AI_ASSIST,
|
||||||
|
children: options.map(option => option.id),
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
conversationHotKeys() {
|
conversationHotKeys() {
|
||||||
if (isAConversationRoute(this.$route.name)) {
|
if (isAConversationRoute(this.$route.name)) {
|
||||||
return [
|
return [
|
||||||
@@ -346,6 +341,7 @@ export default {
|
|||||||
...this.assignTeamActions,
|
...this.assignTeamActions,
|
||||||
...this.labelActions,
|
...this.labelActions,
|
||||||
...this.assignPriorityActions,
|
...this.assignPriorityActions,
|
||||||
|
...this.AIAssistActions,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
app/javascript/shared/constants/openai.js
Normal file
11
app/javascript/shared/constants/openai.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export const OPEN_AI_OPTIONS = {
|
||||||
|
IMPROVE_WRITING: 'improve_writing',
|
||||||
|
FIX_SPELLING_GRAMMAR: 'fix_spelling_grammar',
|
||||||
|
SHORTEN: 'shorten',
|
||||||
|
EXPAND: 'expand',
|
||||||
|
MAKE_FRIENDLY: 'make_friendly',
|
||||||
|
MAKE_FORMAL: 'make_formal',
|
||||||
|
SIMPLIFY: 'simplify',
|
||||||
|
REPLY_SUGGESTION: 'reply_suggestion',
|
||||||
|
SUMMARIZE: 'summarize',
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
class Integrations::Openai::ProcessorService < Integrations::OpenaiBaseService
|
class Integrations::Openai::ProcessorService < Integrations::OpenaiBaseService
|
||||||
AGENT_INSTRUCTION = 'You are a helpful support agent.'.freeze
|
AGENT_INSTRUCTION = 'You are a helpful support agent.'.freeze
|
||||||
LANGUAGE_INSTRUCTION = 'Reply in the user\'s language.'.freeze
|
LANGUAGE_INSTRUCTION = 'Ensure that the reply should be in user language.'.freeze
|
||||||
def reply_suggestion_message
|
def reply_suggestion_message
|
||||||
make_api_call(reply_suggestion_body)
|
make_api_call(reply_suggestion_body)
|
||||||
end
|
end
|
||||||
@@ -40,7 +40,7 @@ class Integrations::Openai::ProcessorService < Integrations::OpenaiBaseService
|
|||||||
private
|
private
|
||||||
|
|
||||||
def rephrase_body
|
def rephrase_body
|
||||||
build_api_call_body("#{AGENT_INSTRUCTION} Please rephrase the following response to a more #{event['data']['tone']} tone. " \
|
build_api_call_body("#{AGENT_INSTRUCTION} Please rephrase the following response. " \
|
||||||
"#{LANGUAGE_INSTRUCTION}")
|
"#{LANGUAGE_INSTRUCTION}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ RSpec.describe Integrations::Openai::ProcessorService do
|
|||||||
{
|
{
|
||||||
'role' => 'system',
|
'role' => 'system',
|
||||||
'content' => 'You are a helpful support agent. ' \
|
'content' => 'You are a helpful support agent. ' \
|
||||||
'Please rephrase the following response to a more ' \
|
'Please rephrase the following response. ' \
|
||||||
"#{event['data']['tone']} tone. Reply in the user's language."
|
'Ensure that the reply should be in user language.'
|
||||||
},
|
},
|
||||||
{ 'role' => 'user', 'content' => event['data']['content'] }
|
{ 'role' => 'user', 'content' => event['data']['content'] }
|
||||||
]
|
]
|
||||||
@@ -130,7 +130,7 @@ RSpec.describe Integrations::Openai::ProcessorService do
|
|||||||
'model' => 'gpt-3.5-turbo',
|
'model' => 'gpt-3.5-turbo',
|
||||||
'messages' => [
|
'messages' => [
|
||||||
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please fix the spelling and grammar of the following response. ' \
|
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please fix the spelling and grammar of the following response. ' \
|
||||||
'Reply in the user\'s language.' },
|
'Ensure that the reply should be in user language.' },
|
||||||
{ 'role' => 'user', 'content' => event['data']['content'] }
|
{ 'role' => 'user', 'content' => event['data']['content'] }
|
||||||
]
|
]
|
||||||
}.to_json
|
}.to_json
|
||||||
@@ -152,7 +152,7 @@ RSpec.describe Integrations::Openai::ProcessorService do
|
|||||||
'model' => 'gpt-3.5-turbo',
|
'model' => 'gpt-3.5-turbo',
|
||||||
'messages' => [
|
'messages' => [
|
||||||
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please shorten the following response. ' \
|
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please shorten the following response. ' \
|
||||||
'Reply in the user\'s language.' },
|
'Ensure that the reply should be in user language.' },
|
||||||
{ 'role' => 'user', 'content' => event['data']['content'] }
|
{ 'role' => 'user', 'content' => event['data']['content'] }
|
||||||
]
|
]
|
||||||
}.to_json
|
}.to_json
|
||||||
@@ -174,7 +174,7 @@ RSpec.describe Integrations::Openai::ProcessorService do
|
|||||||
'model' => 'gpt-3.5-turbo',
|
'model' => 'gpt-3.5-turbo',
|
||||||
'messages' => [
|
'messages' => [
|
||||||
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please expand the following response. ' \
|
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please expand the following response. ' \
|
||||||
'Reply in the user\'s language.' },
|
'Ensure that the reply should be in user language.' },
|
||||||
{ 'role' => 'user', 'content' => event['data']['content'] }
|
{ 'role' => 'user', 'content' => event['data']['content'] }
|
||||||
]
|
]
|
||||||
}.to_json
|
}.to_json
|
||||||
@@ -196,7 +196,7 @@ RSpec.describe Integrations::Openai::ProcessorService do
|
|||||||
'model' => 'gpt-3.5-turbo',
|
'model' => 'gpt-3.5-turbo',
|
||||||
'messages' => [
|
'messages' => [
|
||||||
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please make the following response more friendly. ' \
|
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please make the following response more friendly. ' \
|
||||||
'Reply in the user\'s language.' },
|
'Ensure that the reply should be in user language.' },
|
||||||
{ 'role' => 'user', 'content' => event['data']['content'] }
|
{ 'role' => 'user', 'content' => event['data']['content'] }
|
||||||
]
|
]
|
||||||
}.to_json
|
}.to_json
|
||||||
@@ -218,7 +218,7 @@ RSpec.describe Integrations::Openai::ProcessorService do
|
|||||||
'model' => 'gpt-3.5-turbo',
|
'model' => 'gpt-3.5-turbo',
|
||||||
'messages' => [
|
'messages' => [
|
||||||
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please make the following response more formal. ' \
|
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please make the following response more formal. ' \
|
||||||
'Reply in the user\'s language.' },
|
'Ensure that the reply should be in user language.' },
|
||||||
{ 'role' => 'user', 'content' => event['data']['content'] }
|
{ 'role' => 'user', 'content' => event['data']['content'] }
|
||||||
]
|
]
|
||||||
}.to_json
|
}.to_json
|
||||||
@@ -240,7 +240,7 @@ RSpec.describe Integrations::Openai::ProcessorService do
|
|||||||
'model' => 'gpt-3.5-turbo',
|
'model' => 'gpt-3.5-turbo',
|
||||||
'messages' => [
|
'messages' => [
|
||||||
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please simplify the following response. ' \
|
{ 'role' => 'system', 'content' => 'You are a helpful support agent. Please simplify the following response. ' \
|
||||||
'Reply in the user\'s language.' },
|
'Ensure that the reply should be in user language.' },
|
||||||
{ 'role' => 'user', 'content' => event['data']['content'] }
|
{ 'role' => 'user', 'content' => event['data']['content'] }
|
||||||
]
|
]
|
||||||
}.to_json
|
}.to_json
|
||||||
|
|||||||
Reference in New Issue
Block a user