mirror of
https://github.com/abhinavxd/libredesk.git
synced 2026-09-22 02:23:32 +00:00
Merge pull request #373 from aditya2r/feat/unified-send-and-close-ticket-action
feat: add "Send and set status" split button to reply box.
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{{ $t('globals.messages.cancel') }}</AlertDialogCancel>
|
||||
<AlertDialogAction @click="processSend(true, true)">{{
|
||||
<AlertDialogAction @click="processSend(true, true, deferredStatus)">{{
|
||||
$t('replyBox.sendAnyway')
|
||||
}}</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
@@ -30,7 +30,7 @@
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{{ $t('globals.messages.cancel') }}</AlertDialogCancel>
|
||||
<AlertDialogAction @click="processSend(false, true)">{{
|
||||
<AlertDialogAction @click="processSend(false, true, deferredStatus)">{{
|
||||
$t('replyBox.sendAnyway')
|
||||
}}</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
:isSending="isSending"
|
||||
:enableSend="enableSend"
|
||||
:handleSend="handleSend"
|
||||
:handleSendAndSetStatus="handleSendAndSetStatus"
|
||||
@emojiSelect="handleEmojiSelect"
|
||||
/>
|
||||
</div>
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -38,9 +38,37 @@
|
||||
<Smile class="h-4 w-4" />
|
||||
</Toggle>
|
||||
</div>
|
||||
<Button class="h-8 w-6 px-8" @click="handleSend" :disabled="!enableSend" :isLoading="isSending" v-if="showSendButton">
|
||||
{{ $t('globals.messages.send') }}
|
||||
</Button>
|
||||
<div class="flex items-center">
|
||||
<Button
|
||||
class="h-8 px-4 rounded-r-none"
|
||||
@click="handleSend"
|
||||
:disabled="!enableSend"
|
||||
:isLoading="isSending"
|
||||
v-if="showSendButton"
|
||||
>
|
||||
{{ $t('globals.messages.send') }}
|
||||
</Button>
|
||||
<DropdownMenu v-if="showSendButton">
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button
|
||||
class="h-8 px-2 rounded-l-none border-l border-primary-foreground/30 [&[data-state=open]>svg]:rotate-180"
|
||||
:disabled="!enableSend"
|
||||
>
|
||||
<ChevronDownIcon class="text-primary-foreground transition-transform" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel>{{ $t('replyBox.sendAndSetAs') }}</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
v-for="status in conversationStore.statusOptionsNoSnooze"
|
||||
:key="status.value"
|
||||
@click="handleSendAndSetStatus(status.label)"
|
||||
>
|
||||
{{ status.label }}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}))
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user