mirror of
https://github.com/abhinavxd/libredesk.git
synced 2026-09-10 22:25:39 +00:00
Add prompt-to-tag-on-reply inbox setting
This commit is contained in:
+7
-6
@@ -326,12 +326,13 @@ func handleOAuthCallback(r *fastglue.Request) error {
|
||||
|
||||
// Create inbox
|
||||
newInbox := imodels.Inbox{
|
||||
Name: fmt.Sprintf("%s Inbox", userEmail),
|
||||
From: userEmail,
|
||||
Channel: inbox.ChannelEmail,
|
||||
Enabled: true,
|
||||
CSATEnabled: false,
|
||||
Config: json.RawMessage(configJSON),
|
||||
Name: fmt.Sprintf("%s Inbox", userEmail),
|
||||
From: userEmail,
|
||||
Channel: inbox.ChannelEmail,
|
||||
Enabled: true,
|
||||
CSATEnabled: false,
|
||||
PromptTagsOnReply: false,
|
||||
Config: json.RawMessage(configJSON),
|
||||
}
|
||||
|
||||
createdInbox, err := app.inbox.Create(newInbox)
|
||||
|
||||
@@ -41,6 +41,7 @@ var migList = []migFunc{
|
||||
{"v0.10.0", migrations.V0_10_0},
|
||||
{"v1.0.1", migrations.V1_0_1},
|
||||
{"v2.0.0", migrations.V2_0_0},
|
||||
{"v2.1.0", migrations.V2_1_0},
|
||||
}
|
||||
|
||||
// upgrade upgrades the database to the current version by running SQL migration files
|
||||
|
||||
@@ -70,6 +70,21 @@
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
v-if="showFormFields"
|
||||
v-slot="{ componentField, handleChange }"
|
||||
name="prompt_tags_on_reply"
|
||||
>
|
||||
<FormItem>
|
||||
<SwitchField
|
||||
:title="$t('admin.inbox.promptTagsOnReply')"
|
||||
:description="$t('admin.inbox.promptTagsOnReply.description')"
|
||||
:checked="componentField.modelValue"
|
||||
@update:checked="handleChange"
|
||||
/>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-if="setupMethod" v-slot="{ componentField }" name="auth_type">
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -750,6 +765,7 @@ const form = useForm({
|
||||
from: '',
|
||||
enabled: true,
|
||||
csat_enabled: false,
|
||||
prompt_tags_on_reply: false,
|
||||
enable_plus_addressing: true,
|
||||
auth_type: AUTH_TYPE_PASSWORD,
|
||||
imap: {
|
||||
|
||||
@@ -45,6 +45,17 @@
|
||||
</p>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField, handleChange }" name="prompt_tags_on_reply">
|
||||
<FormItem>
|
||||
<SwitchField
|
||||
:title="$t('admin.inbox.promptTagsOnReply')"
|
||||
:description="$t('admin.inbox.promptTagsOnReply.description')"
|
||||
:checked="componentField.modelValue"
|
||||
@update:checked="handleChange"
|
||||
/>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="name">
|
||||
<FormItem>
|
||||
<FormLabel>{{ $t('globals.terms.name') }}</FormLabel>
|
||||
@@ -1112,6 +1123,7 @@ const form = useForm({
|
||||
enabled: true,
|
||||
secret: '',
|
||||
csat_enabled: false,
|
||||
prompt_tags_on_reply: false,
|
||||
linked_email_inbox_id: null,
|
||||
config: {
|
||||
brand_name: '',
|
||||
|
||||
@@ -7,6 +7,7 @@ export const createFormSchema = (t) => z.object({
|
||||
from: z.string().min(1, t('globals.messages.required')),
|
||||
enabled: z.boolean().optional(),
|
||||
csat_enabled: z.boolean().optional(),
|
||||
prompt_tags_on_reply: z.boolean().optional(),
|
||||
enable_plus_addressing: z.boolean().optional(),
|
||||
auth_type: z.enum([AUTH_TYPE_PASSWORD, AUTH_TYPE_OAUTH2]),
|
||||
oauth: z.object({
|
||||
|
||||
@@ -14,6 +14,7 @@ export const createFormSchema = (t) => z.object({
|
||||
name: z.string().min(1, { message: t('globals.messages.required') }),
|
||||
enabled: z.boolean(),
|
||||
csat_enabled: z.boolean(),
|
||||
prompt_tags_on_reply: z.boolean(),
|
||||
secret: z.string().nullable().optional(),
|
||||
linked_email_inbox_id: z.number().nullable().optional(),
|
||||
config: z.object({
|
||||
|
||||
@@ -13,7 +13,24 @@
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{{ $t('globals.messages.cancel') }}</AlertDialogCancel>
|
||||
<AlertDialogAction @click="processSend(true)">{{
|
||||
<AlertDialogAction @click="processSend(true, true)">{{
|
||||
$t('replyBox.sendAnyway')
|
||||
}}</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
<AlertDialog :open="showMissingTagsWarning" @update:open="showMissingTagsWarning = $event">
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{{ $t('replyBox.missingTagsTitle') }}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{{ $t('replyBox.missingTagsDescription') }}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{{ $t('globals.messages.cancel') }}</AlertDialogCancel>
|
||||
<AlertDialogAction @click="processSend(false, true)">{{
|
||||
$t('replyBox.sendAnyway')
|
||||
}}</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
@@ -138,6 +155,7 @@ import { useDraftManager } from '@main/composables/useDraftManager'
|
||||
import api from '@main/api'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useConversationStore } from '@main/stores/conversation'
|
||||
import { useInboxStore } from '@main/stores/inbox'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -181,6 +199,7 @@ const formSchema = toTypedSchema(
|
||||
|
||||
const { t } = useI18n()
|
||||
const conversationStore = useConversationStore()
|
||||
const inboxStore = useInboxStore()
|
||||
const emitter = useEmitter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
@@ -221,6 +240,7 @@ const emailErrors = ref([])
|
||||
const aiPrompts = ref([])
|
||||
const replyBoxContentRef = ref(null)
|
||||
const showContactEmailWarning = ref(false)
|
||||
const showMissingTagsWarning = ref(false)
|
||||
const mentions = ref([])
|
||||
|
||||
/**
|
||||
@@ -297,7 +317,7 @@ const hasTextContent = computed(() => {
|
||||
/**
|
||||
* Processes the send action.
|
||||
*/
|
||||
const processSend = async (skipContactEmailCheck = false) => {
|
||||
const processSend = async (skipContactEmailCheck = false, skipMissingTagsCheck = false) => {
|
||||
let hasMessageSendingErrored = false
|
||||
isEditorFullscreen.value = false
|
||||
|
||||
@@ -305,6 +325,19 @@ const processSend = async (skipContactEmailCheck = false) => {
|
||||
const convUUID = conversationStore.current.uuid
|
||||
const isPrivate = messageType.value === 'private_note'
|
||||
|
||||
const currentInbox = inboxStore.inboxes.find(
|
||||
(i) => i.id === conversationStore.current.inbox_id
|
||||
)
|
||||
if (
|
||||
!isPrivate &&
|
||||
!skipMissingTagsCheck &&
|
||||
currentInbox?.prompt_tags_on_reply &&
|
||||
!(conversationStore.current.tags?.length > 0)
|
||||
) {
|
||||
showMissingTagsWarning.value = true
|
||||
return
|
||||
}
|
||||
|
||||
if (!isPrivate && conversationStore.current.inbox_channel === 'email') {
|
||||
// Require at least one recipient in `to`.
|
||||
if (!to.value.trim()) {
|
||||
|
||||
@@ -134,6 +134,7 @@ const submitLiveChatForm = (values) => {
|
||||
channel: 'livechat',
|
||||
enabled: values.enabled ?? true,
|
||||
csat_enabled: values.csat_enabled ?? false,
|
||||
prompt_tags_on_reply: values.prompt_tags_on_reply ?? false,
|
||||
config: values.config
|
||||
}
|
||||
createInbox(payload)
|
||||
|
||||
@@ -128,6 +128,8 @@
|
||||
"admin.inbox.csatSurveys.description_3": "CSAT surveys are only sent once per conversation.",
|
||||
"admin.inbox.enablePlusAddressing": "Enable plus addressing",
|
||||
"admin.inbox.enablePlusAddressing.description": "Improves conversation threading but requires provider support (e.g., Gmail, Microsoft 365).",
|
||||
"admin.inbox.promptTagsOnReply": "Prompt to tag before replying",
|
||||
"admin.inbox.promptTagsOnReply.description": "Warn agents before they send a reply on a conversation that has no tags. Agents can still send without tagging by confirming the prompt.",
|
||||
"admin.inbox.enabled.description": "Toggle scanning inbox and sending out messages.",
|
||||
"admin.inbox.fromEmailAddress.description": "From email address for your inbox. e.g. My inbox <support{'@'}example.com>",
|
||||
"admin.inbox.fromEmailAddress.placeholder": "My inbox <support{'@'}example.com>",
|
||||
@@ -905,6 +907,8 @@
|
||||
"replyBox.contactEmailMissingDescription": "The contact's email ({email}) is not included in to, cc, or bcc. The contact won't receive this reply.",
|
||||
"replyBox.emailAddresess": "Email addresses separated by comma",
|
||||
"replyBox.invalidEmailsIn": "Invalid email(s) in",
|
||||
"replyBox.missingTagsDescription": "This conversation has no tags. Tagging helps with reporting and triage. You can still send without tagging.",
|
||||
"replyBox.missingTagsTitle": "No tags on this conversation",
|
||||
"replyBox.removeBCC": "Remove BCC",
|
||||
"replyBox.sendAnyway": "Send anyway",
|
||||
"replyBox.toRequired": "At least one recipient is required in the To field.",
|
||||
|
||||
@@ -250,7 +250,7 @@ func (m *Manager) Create(inbox imodels.Inbox) (imodels.Inbox, error) {
|
||||
}
|
||||
|
||||
var createdInbox imodels.Inbox
|
||||
if err := m.queries.InsertInbox.Get(&createdInbox, inbox.Channel, encryptedConfig, inbox.Name, inbox.From, inbox.CSATEnabled, inbox.Secret, inbox.LinkedEmailInboxID); err != nil {
|
||||
if err := m.queries.InsertInbox.Get(&createdInbox, inbox.Channel, encryptedConfig, inbox.Name, inbox.From, inbox.CSATEnabled, inbox.PromptTagsOnReply, inbox.Secret, inbox.LinkedEmailInboxID); err != nil {
|
||||
m.lo.Error("error creating inbox", "error", err)
|
||||
return imodels.Inbox{}, envelope.NewError(envelope.GeneralError, m.i18n.T("globals.messages.somethingWentWrong"), nil)
|
||||
}
|
||||
@@ -424,7 +424,7 @@ func (m *Manager) Update(id int, inbox imodels.Inbox) (imodels.Inbox, error) {
|
||||
|
||||
// Update the inbox in the DB.
|
||||
var updatedInbox imodels.Inbox
|
||||
if err := m.queries.Update.Get(&updatedInbox, id, inbox.Channel, encryptedConfig, inbox.Name, inbox.From, inbox.CSATEnabled, inbox.Enabled, inbox.Secret, inbox.LinkedEmailInboxID); err != nil {
|
||||
if err := m.queries.Update.Get(&updatedInbox, id, inbox.Channel, encryptedConfig, inbox.Name, inbox.From, inbox.CSATEnabled, inbox.PromptTagsOnReply, inbox.Enabled, inbox.Secret, inbox.LinkedEmailInboxID); err != nil {
|
||||
m.lo.Error("error updating inbox", "error", err)
|
||||
return imodels.Inbox{}, envelope.NewError(envelope.GeneralError, m.i18n.T("globals.messages.somethingWentWrong"), nil)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ type Inbox struct {
|
||||
Channel string `db:"channel" json:"channel"`
|
||||
Enabled bool `db:"enabled" json:"enabled"`
|
||||
CSATEnabled bool `db:"csat_enabled" json:"csat_enabled"`
|
||||
PromptTagsOnReply bool `db:"prompt_tags_on_reply" json:"prompt_tags_on_reply"`
|
||||
From string `db:"from" json:"from"`
|
||||
Config json.RawMessage `db:"config" json:"config"`
|
||||
Secret null.String `db:"secret" json:"secret"`
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
-- name: get-active-inboxes
|
||||
SELECT id, uuid, created_at, updated_at, "name", deleted_at, channel, enabled, csat_enabled, config, "from", linked_email_inbox_id FROM inboxes where enabled is TRUE and deleted_at is NULL;
|
||||
SELECT id, uuid, created_at, updated_at, "name", deleted_at, channel, enabled, csat_enabled, prompt_tags_on_reply, config, "from", linked_email_inbox_id FROM inboxes where enabled is TRUE and deleted_at is NULL;
|
||||
|
||||
-- name: get-all-inboxes
|
||||
SELECT id, uuid, created_at, updated_at, "name", deleted_at, channel, enabled, csat_enabled, config, "from", linked_email_inbox_id FROM inboxes where deleted_at is NULL;
|
||||
SELECT id, uuid, created_at, updated_at, "name", deleted_at, channel, enabled, csat_enabled, prompt_tags_on_reply, config, "from", linked_email_inbox_id FROM inboxes where deleted_at is NULL;
|
||||
|
||||
-- name: insert-inbox
|
||||
INSERT INTO inboxes
|
||||
(channel, config, "name", "from", csat_enabled, secret, linked_email_inbox_id)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7)
|
||||
(channel, config, "name", "from", csat_enabled, prompt_tags_on_reply, secret, linked_email_inbox_id)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING *
|
||||
|
||||
-- name: get-inbox
|
||||
SELECT id, uuid, created_at, updated_at, "name", deleted_at, channel, enabled, csat_enabled, config, "from", secret, linked_email_inbox_id FROM inboxes where id = $1 and deleted_at is NULL;
|
||||
SELECT id, uuid, created_at, updated_at, "name", deleted_at, channel, enabled, csat_enabled, prompt_tags_on_reply, config, "from", secret, linked_email_inbox_id FROM inboxes where id = $1 and deleted_at is NULL;
|
||||
|
||||
-- name: get-inbox-by-uuid
|
||||
SELECT id, uuid, created_at, updated_at, "name", deleted_at, channel, enabled, csat_enabled, config, "from", secret, linked_email_inbox_id FROM inboxes where uuid = $1 and deleted_at is NULL;
|
||||
SELECT id, uuid, created_at, updated_at, "name", deleted_at, channel, enabled, csat_enabled, prompt_tags_on_reply, config, "from", secret, linked_email_inbox_id FROM inboxes where uuid = $1 and deleted_at is NULL;
|
||||
|
||||
-- name: update
|
||||
UPDATE inboxes
|
||||
set channel = $2, config = $3, "name" = $4, "from" = $5, csat_enabled = $6, enabled = $7, secret = $8, linked_email_inbox_id = $9, updated_at = now()
|
||||
set channel = $2, config = $3, "name" = $4, "from" = $5, csat_enabled = $6, prompt_tags_on_reply = $7, enabled = $8, secret = $9, linked_email_inbox_id = $10, updated_at = now()
|
||||
where id = $1 and deleted_at is NULL
|
||||
RETURNING *;
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/knadh/stuffbin"
|
||||
)
|
||||
|
||||
// V2_1_0 updates the database schema to v2.1.0.
|
||||
func V2_1_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
|
||||
_, err := db.Exec(`
|
||||
ALTER TABLE inboxes ADD COLUMN IF NOT EXISTS prompt_tags_on_reply bool DEFAULT false NOT NULL;
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -84,6 +84,7 @@ CREATE TABLE inboxes (
|
||||
channel channels NOT NULL,
|
||||
enabled bool DEFAULT TRUE NOT NULL,
|
||||
csat_enabled bool DEFAULT false NOT NULL,
|
||||
prompt_tags_on_reply bool DEFAULT false NOT NULL,
|
||||
config jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"from" TEXT NULL,
|
||||
secret TEXT NULL,
|
||||
|
||||
Reference in New Issue
Block a user