diff --git a/frontend/apps/main/src/features/conversation/ReplyBox.vue b/frontend/apps/main/src/features/conversation/ReplyBox.vue index d1cabbaa..a2cf4218 100644 --- a/frontend/apps/main/src/features/conversation/ReplyBox.vue +++ b/frontend/apps/main/src/features/conversation/ReplyBox.vue @@ -13,7 +13,7 @@ {{ $t('globals.messages.cancel') }} - {{ + {{ $t('replyBox.sendAnyway') }} @@ -30,7 +30,7 @@ {{ $t('globals.messages.cancel') }} - {{ + {{ $t('replyBox.sendAnyway') }} @@ -103,6 +103,7 @@ v-model:mentions="mentions" @toggleFullscreen="isEditorFullscreen = !isEditorFullscreen" @send="processSend" + @sendAndSetStatus="processSendAndSetStatus" @fileUpload="handleFileUpload" @fileDelete="handleFileDelete" @filesDropped="uploadFiles" @@ -137,6 +138,7 @@ v-model:mentions="mentions" @toggleFullscreen="isEditorFullscreen = !isEditorFullscreen" @send="processSend" + @sendAndSetStatus="processSendAndSetStatus" @fileUpload="handleFileUpload" @fileDelete="handleFileDelete" @filesDropped="uploadFiles" @@ -247,6 +249,7 @@ const aiPrompts = computed(() => aiPromptStore.prompts) const replyBoxContentRef = ref(null) const showContactEmailWarning = ref(false) const showMissingTagsWarning = ref(false) +const deferredStatus = ref(null) const mentions = ref([]) aiPromptStore.fetchPrompts() @@ -305,10 +308,7 @@ const hasTextContent = computed(() => { return textContent.value.trim().length > 0 }) -/** - * Processes the send action. - */ -const processSend = async (skipContactEmailCheck = false, skipMissingTagsCheck = false) => { +const processSend = async (skipContactEmailCheck = false, skipMissingTagsCheck = false, statusToSet = null) => { let hasMessageSendingErrored = false isEditorFullscreen.value = false @@ -327,6 +327,7 @@ const processSend = async (skipContactEmailCheck = false, skipMissingTagsCheck = currentInbox?.prompt_tags_on_reply && !(conversationStore.current.tags?.length > 0) ) { + deferredStatus.value = statusToSet showMissingTagsWarning.value = true return } @@ -352,6 +353,7 @@ const processSend = async (skipContactEmailCheck = false, skipMissingTagsCheck = .map((e) => e.trim()) .includes(contactEmail) ) { + deferredStatus.value = statusToSet showContactEmailWarning.value = true return } @@ -462,10 +464,13 @@ const processSend = async (skipContactEmailCheck = false, skipMissingTagsCheck = clearMediaFiles() emailErrors.value = [] mentions.value = [] + if (statusToSet) conversationStore.updateStatus(statusToSet) } isSending.value = false } +const processSendAndSetStatus = (status) => processSend(false, false, status) + /** * Watches for changes in the conversation's macro id and update message content. */ diff --git a/frontend/apps/main/src/features/conversation/ReplyBoxContent.vue b/frontend/apps/main/src/features/conversation/ReplyBoxContent.vue index 24b9b6fc..be911978 100644 --- a/frontend/apps/main/src/features/conversation/ReplyBoxContent.vue +++ b/frontend/apps/main/src/features/conversation/ReplyBoxContent.vue @@ -130,6 +130,7 @@ :isSending="isSending" :enableSend="enableSend" :handleSend="handleSend" + :handleSendAndSetStatus="handleSendAndSetStatus" @emojiSelect="handleEmojiSelect" /> @@ -239,6 +240,7 @@ const props = defineProps({ const emit = defineEmits([ 'toggleFullscreen', 'send', + 'sendAndSetStatus', 'fileUpload', 'inlineImageUpload', 'fileDelete', @@ -302,21 +304,34 @@ const validateEmails = async () => { }) } -/** - * Send the reply or private note - */ -const handleSend = async () => { +const validateBeforeSend = async () => { await validateEmails() if (emailErrors.value.length > 0) { emitter.emit(EMITTER_EVENTS.SHOW_TOAST, { variant: 'destructive', description: t('globals.messages.correctEmailErrors') }) - return + return false } + return true +} + +/** + * Send the reply or private note + */ +const handleSend = async () => { + if (!(await validateBeforeSend())) return emit('send') } +/** + * Send the reply or private note and set conversation status + */ +const handleSendAndSetStatus = async (status) => { + if (!(await validateBeforeSend())) return + emit('sendAndSetStatus', status) +} + const handleFileUpload = (event) => { emit('fileUpload', event) } diff --git a/frontend/apps/main/src/features/conversation/ReplyBoxMenuBar.vue b/frontend/apps/main/src/features/conversation/ReplyBoxMenuBar.vue index 593c42d5..53272829 100644 --- a/frontend/apps/main/src/features/conversation/ReplyBoxMenuBar.vue +++ b/frontend/apps/main/src/features/conversation/ReplyBoxMenuBar.vue @@ -38,9 +38,37 @@ - +
+ + + + + + + {{ $t('replyBox.sendAndSetAs') }} + + {{ status.label }} + + + +
@@ -49,7 +77,16 @@ import { ref, defineAsyncComponent } from 'vue' import { onClickOutside } from '@vueuse/core' import { Button } from '@shared-ui/components/ui/button' import { Toggle } from '@shared-ui/components/ui/toggle' -import { Paperclip, Smile } from 'lucide-vue-next' +import { Paperclip, Smile, ChevronDownIcon } from 'lucide-vue-next' +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuItem, + DropdownMenuContent, + DropdownMenuLabel +} from '@shared-ui/components/ui/dropdown-menu' +import { useConversationStore } from '@main/stores/conversation' +const conversationStore = useConversationStore() const EmojiPicker = defineAsyncComponent(async () => { const [mod] = await Promise.all([ @@ -71,6 +108,7 @@ defineProps({ isSending: Boolean, enableSend: Boolean, handleSend: Function, + handleSendAndSetStatus: Function, showSendButton: { type: Boolean, default: true diff --git a/frontend/apps/main/src/stores/conversation.js b/frontend/apps/main/src/stores/conversation.js index 1dd3727a..ba789803 100644 --- a/frontend/apps/main/src/stores/conversation.js +++ b/frontend/apps/main/src/stores/conversation.js @@ -40,7 +40,7 @@ export const useConversationStore = defineStore('conversation', () => { return statuses.value.map(s => ({ label: s.name, value: s.id })) }) const statusOptionsNoSnooze = computed(() => - statuses.value.filter(s => s.name !== 'Snoozed').map(s => ({ + statuses.value.filter(s => s.name !== CONVERSATION_DEFAULT_STATUSES.SNOOZED).map(s => ({ label: s.name, value: s.id })) diff --git a/i18n/en-US.json b/i18n/en-US.json index 92b5a14a..9cca9a20 100644 --- a/i18n/en-US.json +++ b/i18n/en-US.json @@ -940,6 +940,7 @@ "replyBox.missingTagsTitle": "No tags on this conversation", "replyBox.removeBCC": "Remove BCC", "replyBox.sendAnyway": "Send anyway", + "replyBox.sendAndSetAs": "Send and set as", "replyBox.toRequired": "At least one recipient is required in the To field.", "report.agentStatus": "Agent Status", "report.chart.newConversations": "New conversations",