feat: Auto resolve conversations after n days of inactivity (#1308)

fixes: #418
This commit is contained in:
Akash Srivastava 2020-11-01 12:53:25 +05:30 committed by GitHub
parent 65ed4c78a4
commit 074084b258
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
79 changed files with 358 additions and 22 deletions

View file

@ -33,7 +33,7 @@ class Api::V1::AccountsController < Api::BaseController
end end
def update def update
@account.update!(account_params.slice(:name, :locale, :domain, :support_email)) @account.update!(account_params.slice(:name, :locale, :domain, :support_email, :auto_resolve_duration))
end end
def update_active_at def update_active_at
@ -54,7 +54,7 @@ class Api::V1::AccountsController < Api::BaseController
end end
def account_params def account_params
params.permit(:account_name, :email, :name, :locale, :domain, :support_email) params.permit(:account_name, :email, :name, :locale, :domain, :support_email, :auto_resolve_duration)
end end
def check_signup_enabled def check_signup_enabled

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "عنوان البريد الإلكتروني الخاص باستقبال رسائل الدعم الفني", "PLACEHOLDER": "عنوان البريد الإلكتروني الخاص باستقبال رسائل الدعم الفني",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "الاستمرار في المحادثة عبر رسائل البريد الإلكتروني مفعّل لحسابك.", "INBOUND_EMAIL_ENABLED": "الاستمرار في المحادثة عبر رسائل البريد الإلكتروني مفعّل لحسابك.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "يمكنك تلقي رسائل البريد الإلكتروني في النطاق المخصص الخاص بك الآن." "CUSTOM_EMAIL_DOMAIN_ENABLED": "يمكنك تلقي رسائل البريد الإلكتروني في النطاق المخصص الخاص بك الآن."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "To email υποστήριξης της εταιρίας σας", "PLACEHOLDER": "To email υποστήριξης της εταιρίας σας",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Η συνέχεια της συνομιλίας με emails έχει ενεργοποιηθεί για τον λογαριασμό.", "INBOUND_EMAIL_ENABLED": "Η συνέχεια της συνομιλίας με emails έχει ενεργοποιηθεί για τον λογαριασμό.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Τώρα μπορείτε να λαμβάνετε emails στον τομέα (domain) σας." "CUSTOM_EMAIL_DOMAIN_ENABLED": "Τώρα μπορείτε να λαμβάνετε emails στον τομέα (domain) σας."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Email de soporte de su empresa", "PLACEHOLDER": "Email de soporte de su empresa",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Continuidad de la conversación con emails está habilitada para su cuenta.", "INBOUND_EMAIL_ENABLED": "Continuidad de la conversación con emails está habilitada para su cuenta.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Ahora puede recibir emails en su dominio personalizado." "CUSTOM_EMAIL_DOMAIN_ENABLED": "Ahora puede recibir emails en su dominio personalizado."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "ایمیل پشتیبانی شرکت شما", "PLACEHOLDER": "ایمیل پشتیبانی شرکت شما",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "L'adresse de courriel de support de votre entreprise", "PLACEHOLDER": "L'adresse de courriel de support de votre entreprise",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "La continuité des conversations avec les courriels est activée pour votre compte.", "INBOUND_EMAIL_ENABLED": "La continuité des conversations avec les courriels est activée pour votre compte.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Vous pouvez maintenant recevoir des courriels dans votre domaine personnalisé." "CUSTOM_EMAIL_DOMAIN_ENABLED": "Vous pouvez maintenant recevoir des courriels dans votre domaine personnalisé."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "E-mail di supporto della tua azienda", "PLACEHOLDER": "E-mail di supporto della tua azienda",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "നിങ്ങളുടെ കമ്പനിയുടെ പിന്തുണാ ഇമെയിൽ", "PLACEHOLDER": "നിങ്ങളുടെ കമ്പനിയുടെ പിന്തുണാ ഇമെയിൽ",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "നിങ്ങളുടെ അക്കൗണ്ടിനായി ഇമെയിലുകളുമായുള്ള സംഭാഷണ തുടർച്ച പ്രവർത്തനക്ഷമമാക്കി.", "INBOUND_EMAIL_ENABLED": "നിങ്ങളുടെ അക്കൗണ്ടിനായി ഇമെയിലുകളുമായുള്ള സംഭാഷണ തുടർച്ച പ്രവർത്തനക്ഷമമാക്കി.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "നിങ്ങളുടെ ഇഷ്‌ടാനുസൃത ഡൊമെയ്‌നിൽ നിങ്ങൾക്ക് ഇപ്പോൾ ഇമെയിലുകൾ സ്വീകരിക്കാൻ കഴിയും." "CUSTOM_EMAIL_DOMAIN_ENABLED": "നിങ്ങളുടെ ഇഷ്‌ടാനുസൃത ഡൊമെയ്‌നിൽ നിങ്ങൾക്ക് ഇപ്പോൾ ഇമെയിലുകൾ സ്വീകരിക്കാൻ കഴിയും."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "E-mailadres support van uw bedrijf", "PLACEHOLDER": "E-mailadres support van uw bedrijf",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "E-mail de suporte da sua empresa", "PLACEHOLDER": "E-mail de suporte da sua empresa",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "E-mail de suporte da sua empresa", "PLACEHOLDER": "E-mail de suporte da sua empresa",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "A continuidade das conversas com e-mails está ativada para sua conta.", "INBOUND_EMAIL_ENABLED": "A continuidade das conversas com e-mails está ativada para sua conta.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Você pode receber e-mails em seu domínio personalizado agora." "CUSTOM_EMAIL_DOMAIN_ENABLED": "Você pode receber e-mails em seu domínio personalizado agora."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "E-mailul de asistență al companiei dumneavoastră", "PLACEHOLDER": "E-mailul de asistență al companiei dumneavoastră",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Continuitatea conversației cu e-mailurile este activată pentru contul dvs.", "INBOUND_EMAIL_ENABLED": "Continuitatea conversației cu e-mailurile este activată pentru contul dvs.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Puteți primi e-mailuri în domeniul dvs. personalizat acum." "CUSTOM_EMAIL_DOMAIN_ENABLED": "Puteți primi e-mailuri în domeniul dvs. personalizat acum."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Email службы поддержки вашей компании", "PLACEHOLDER": "Email службы поддержки вашей компании",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Для вашего аккаунта включено продолжение диалогов по электронной почте.", "INBOUND_EMAIL_ENABLED": "Для вашего аккаунта включено продолжение диалогов по электронной почте.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Теперь вы можете получать письма на свой домен." "CUSTOM_EMAIL_DOMAIN_ENABLED": "Теперь вы можете получать письма на свой домен."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "உங்கள் நிறுவனத்தின் சேவைக்கான ஈ-மெயில் முகவரி", "PLACEHOLDER": "உங்கள் நிறுவனத்தின் சேவைக்கான ஈ-மெயில் முகவரி",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "อีเมล์ช่วยเหลือในบริษัทของคุณ", "PLACEHOLDER": "อีเมล์ช่วยเหลือในบริษัทของคุณ",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "การสนทนาด้วยอีเมล์ถูกเปิดสำหรับบัญชีของคุณ", "INBOUND_EMAIL_ENABLED": "การสนทนาด้วยอีเมล์ถูกเปิดสำหรับบัญชีของคุณ",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "คุณสามารถรับอีเมล์ในโดเมนเเบบกำหนดเองได้เเล้ว" "CUSTOM_EMAIL_DOMAIN_ENABLED": "คุณสามารถรับอีเมล์ในโดเมนเเบบกำหนดเองได้เเล้ว"

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Your company's support email", "PLACEHOLDER": "Your company's support email",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.", "INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now." "CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "Email hỗ trợ của công ty bạn", "PLACEHOLDER": "Email hỗ trợ của công ty bạn",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "Tính liên tục của cuộc trò chuyện với email được kích hoạt cho tài khoản của bạn.", "INBOUND_EMAIL_ENABLED": "Tính liên tục của cuộc trò chuyện với email được kích hoạt cho tài khoản của bạn.",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Bạn có thể nhận email trong miền tùy chỉnh của mình ngay bây giờ." "CUSTOM_EMAIL_DOMAIN_ENABLED": "Bạn có thể nhận email trong miền tùy chỉnh của mình ngay bây giờ."

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "您公司的支持邮件", "PLACEHOLDER": "您公司的支持邮件",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "您的帐户启用了与电子邮件的对话连续性。", "INBOUND_EMAIL_ENABLED": "您的帐户启用了与电子邮件的对话连续性。",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "您现在可以在您的自定义域名的电子邮件中接收消息。" "CUSTOM_EMAIL_DOMAIN_ENABLED": "您现在可以在您的自定义域名的电子邮件中接收消息。"

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "您公司的支持邮件", "PLACEHOLDER": "您公司的支持邮件",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "您的帐户启用了与电子邮件的对话连续性。", "INBOUND_EMAIL_ENABLED": "您的帐户启用了与电子邮件的对话连续性。",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "您现在可以在您的自定义域名的电子邮件中接收消息。" "CUSTOM_EMAIL_DOMAIN_ENABLED": "您现在可以在您的自定义域名的电子邮件中接收消息。"

View file

@ -33,6 +33,11 @@
"PLACEHOLDER": "您公司的客服信箱", "PLACEHOLDER": "您公司的客服信箱",
"ERROR": "" "ERROR": ""
}, },
"AUTO_RESOLVE_DURATION": {
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
"PLACEHOLDER": "30",
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
},
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "您的帳戶啟用了電子信箱與對話的持續性功能。", "INBOUND_EMAIL_ENABLED": "您的帳戶啟用了電子信箱與對話的持續性功能。",
"CUSTOM_EMAIL_DOMAIN_ENABLED": "您現在可以在您的自定義域名的電子信箱中接收消息。" "CUSTOM_EMAIL_DOMAIN_ENABLED": "您現在可以在您的自定義域名的電子信箱中接收消息。"

View file

@ -62,6 +62,20 @@
" "
/> />
</label> </label>
<label :class="{ error: $v.autoResolveDuration.$error }">
{{ $t('GENERAL_SETTINGS.FORM.AUTO_RESOLVE_DURATION.LABEL') }}
<input
v-model="autoResolveDuration"
type="number"
:placeholder="
$t('GENERAL_SETTINGS.FORM.AUTO_RESOLVE_DURATION.PLACEHOLDER')
"
@blur="$v.autoResolveDuration.$touch"
/>
<span v-if="$v.autoResolveDuration.$error" class="message">
{{ $t('GENERAL_SETTINGS.FORM.AUTO_RESOLVE_DURATION.ERROR') }}
</span>
</label>
</div> </div>
</div> </div>
<div class="current-version"> <div class="current-version">
@ -82,7 +96,7 @@
<script> <script>
import Vue from 'vue'; import Vue from 'vue';
import { required } from 'vuelidate/lib/validators'; import { required, minValue } from 'vuelidate/lib/validators';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import alertMixin from 'shared/mixins/alertMixin'; import alertMixin from 'shared/mixins/alertMixin';
import configMixin from 'shared/mixins/configMixin'; import configMixin from 'shared/mixins/configMixin';
@ -98,6 +112,7 @@ export default {
domain: '', domain: '',
supportEmail: '', supportEmail: '',
features: {}, features: {},
autoResolveDuration: null,
}; };
}, },
validations: { validations: {
@ -107,6 +122,9 @@ export default {
locale: { locale: {
required, required,
}, },
autoResolveDuration: {
minValue: minValue(1),
},
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
@ -144,6 +162,7 @@ export default {
support_email, support_email,
custom_email_domain_enabled, custom_email_domain_enabled,
features, features,
auto_resolve_duration,
} = this.getAccount(this.accountId); } = this.getAccount(this.accountId);
Vue.config.lang = locale; Vue.config.lang = locale;
@ -154,6 +173,7 @@ export default {
this.supportEmail = support_email; this.supportEmail = support_email;
this.customEmailDomainEnabled = custom_email_domain_enabled; this.customEmailDomainEnabled = custom_email_domain_enabled;
this.features = features; this.features = features;
this.autoResolveDuration = auto_resolve_duration;
} catch (error) { } catch (error) {
// Ignore error // Ignore error
} }
@ -171,6 +191,7 @@ export default {
name: this.name, name: this.name,
domain: this.domain, domain: this.domain,
support_email: this.supportEmail, support_email: this.supportEmail,
auto_resolve_duration: this.autoResolveDuration,
}); });
Vue.config.lang = this.locale; Vue.config.lang = this.locale;
this.showAlert(this.$t('GENERAL_SETTINGS.UPDATE.SUCCESS')); this.showAlert(this.$t('GENERAL_SETTINGS.UPDATE.SUCCESS'));

View file

@ -0,0 +1,16 @@
class AutoResolveConversationsJob < ApplicationJob
queue_as :medium
def perform(conversation_id)
conversation = Conversation.find_by(id: conversation_id)
return unless conversation&.auto_resolve_duration && conversation&.open?
time_since_last_activity = Time.zone.now.to_i - conversation.last_activity_at.to_i
time_left_to_auto_resolve = conversation.auto_resolve_duration.days.to_i - time_since_last_activity
if time_left_to_auto_resolve.positive?
AutoResolveConversationsJob.set(wait_until: time_left_to_auto_resolve.seconds.from_now).perform_later(conversation_id)
else
conversation.toggle_status
end
end
end

View file

@ -2,16 +2,17 @@
# #
# Table name: accounts # Table name: accounts
# #
# id :integer not null, primary key # id :integer not null, primary key
# domain :string(100) # auto_resolve_duration :integer
# feature_flags :integer default(0), not null # domain :string(100)
# locale :integer default("en") # feature_flags :integer default(0), not null
# name :string not null # locale :integer default("en")
# settings_flags :integer default(0), not null # name :string not null
# support_email :string(100) # settings_flags :integer default(0), not null
# support_email :string(100)
# timezone :string default("UTC") # timezone :string default("UTC")
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# #
class Account < ApplicationRecord class Account < ApplicationRecord
@ -29,6 +30,7 @@ class Account < ApplicationRecord
}.freeze }.freeze
validates :name, presence: true validates :name, presence: true
validates :auto_resolve_duration, numericality: { greater_than_or_equal_to: 1, allow_nil: true }
has_many :account_users, dependent: :destroy has_many :account_users, dependent: :destroy
has_many :agent_bot_inboxes, dependent: :destroy has_many :agent_bot_inboxes, dependent: :destroy

View file

@ -54,11 +54,13 @@ class Conversation < ApplicationRecord
# wanted to change this to after_update commit. But it ended up creating a loop # wanted to change this to after_update commit. But it ended up creating a loop
# reinvestigate in future and identity the implications # reinvestigate in future and identity the implications
after_update :notify_status_change, :create_activity after_update :notify_status_change, :create_activity
after_create_commit :notify_conversation_creation after_create_commit :notify_conversation_creation, :queue_conversation_auto_resolution_job
after_save :run_round_robin after_save :run_round_robin
acts_as_taggable_on :labels acts_as_taggable_on :labels
delegate :auto_resolve_duration, to: :account
def can_reply? def can_reply?
return true unless inbox&.channel&.has_24_hour_messaging_window? return true unless inbox&.channel&.has_24_hour_messaging_window?
@ -145,6 +147,12 @@ class Conversation < ApplicationRecord
dispatcher_dispatch(CONVERSATION_CREATED) dispatcher_dispatch(CONVERSATION_CREATED)
end end
def queue_conversation_auto_resolution_job
return unless auto_resolve_duration
AutoResolveConversationsJob.set(wait_until: (last_activity_at || created_at) + auto_resolve_duration.days).perform_later(id)
end
def self_assign?(assignee_id) def self_assign?(assignee_id)
assignee_id.present? && Current.user&.id == assignee_id assignee_id.present? && Current.user&.id == assignee_id
end end
@ -157,15 +165,17 @@ class Conversation < ApplicationRecord
end end
def create_activity def create_activity
return unless Current.user user_name = Current.user.name if Current.user.present?
status_change_activity(user_name) if saved_change_to_status?
user_name = Current.user.name
create_status_change_message(user_name) if saved_change_to_status?
create_assignee_change(user_name) if saved_change_to_assignee_id? create_assignee_change(user_name) if saved_change_to_assignee_id?
create_label_change(user_name) if saved_change_to_label_list? create_label_change(user_name) if saved_change_to_label_list?
end end
def status_change_activity(user_name)
create_status_change_message(user_name)
queue_conversation_auto_resolution_job if open?
end
def activity_message_params(content) def activity_message_params(content)
{ account_id: account_id, inbox_id: inbox_id, message_type: :activity, content: content } { account_id: account_id, inbox_id: inbox_id, message_type: :activity, content: content }
end end
@ -210,12 +220,18 @@ class Conversation < ApplicationRecord
end end
def create_status_change_message(user_name) def create_status_change_message(user_name)
content = I18n.t("conversations.activity.status.#{status}", user_name: user_name) content = if user_name
I18n.t("conversations.activity.status.#{status}", user_name: user_name)
elsif resolved?
I18n.t('conversations.activity.status.auto_resolved', duration: auto_resolve_duration)
end
messages.create(activity_message_params(content)) messages.create(activity_message_params(content)) if content
end end
def create_assignee_change(user_name) def create_assignee_change(user_name)
return unless user_name
params = { assignee_name: assignee.name, user_name: user_name }.compact params = { assignee_name: assignee.name, user_name: user_name }.compact
key = assignee_id ? 'assigned' : 'removed' key = assignee_id ? 'assigned' : 'removed'
key = 'self_assigned' if self_assign? assignee_id key = 'self_assigned' if self_assign? assignee_id
@ -225,6 +241,8 @@ class Conversation < ApplicationRecord
end end
def create_label_change(user_name) def create_label_change(user_name)
return unless user_name
previous_labels, current_labels = previous_changes[:label_list] previous_labels, current_labels = previous_changes[:label_list]
return unless (previous_labels.is_a? Array) && (current_labels.is_a? Array) return unless (previous_labels.is_a? Array) && (current_labels.is_a? Array)

View file

@ -5,3 +5,4 @@ json.domain @account.domain
json.custom_email_domain_enabled @account.custom_email_domain_enabled json.custom_email_domain_enabled @account.custom_email_domain_enabled
json.support_email @account.support_email json.support_email @account.support_email
json.features @account.all_features json.features @account.all_features
json.auto_resolve_duration @account.auto_resolve_duration

View file

@ -32,6 +32,7 @@ ar:
status: status:
resolved: "تم تحديث حالة المحادثة لـ\"مغلقة\" بواسطة %{user_name}" resolved: "تم تحديث حالة المحادثة لـ\"مغلقة\" بواسطة %{user_name}"
open: "تم إعادة فتح المحادثة بواسطة %{user_name}" open: "تم إعادة فتح المحادثة بواسطة %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "تم إسنادها إلى %{assignee_name} بواسطة %{user_name}" assigned: "تم إسنادها إلى %{assignee_name} بواسطة %{user_name}"
removed: "المحادثة غير مسندة بواسطة %{user_name}" removed: "المحادثة غير مسندة بواسطة %{user_name}"

View file

@ -32,6 +32,7 @@ ca:
status: status:
resolved: "La conversa va ser marcada com resolta per %{user_name}" resolved: "La conversa va ser marcada com resolta per %{user_name}"
open: "La conversa es va reobrir per %{user_name}" open: "La conversa es va reobrir per %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assignada a %{assignee_name} per %{user_name}" assigned: "Assignada a %{assignee_name} per %{user_name}"
removed: "%{user_name} ha tret l'assignació de la conversa" removed: "%{user_name} ha tret l'assignació de la conversa"

View file

@ -32,6 +32,7 @@ cs:
status: status:
resolved: "Konverzace byla vyřešena uživatelem %{user_name}" resolved: "Konverzace byla vyřešena uživatelem %{user_name}"
open: "Konverzace byla znovu otevřena uživatelem %{user_name}" open: "Konverzace byla znovu otevřena uživatelem %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Přiřazeno k %{assignee_name} uživatelem %{user_name}" assigned: "Přiřazeno k %{assignee_name} uživatelem %{user_name}"
removed: "Konverzace zrušena uživatelem %{user_name}" removed: "Konverzace zrušena uživatelem %{user_name}"

View file

@ -32,6 +32,7 @@ da:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ de:
status: status:
resolved: "Das Gespräch wurde von gelöst gelöst %{user_name}" resolved: "Das Gespräch wurde von gelöst gelöst %{user_name}"
open: "Das Gespräch wurde von wieder eröffnet %{user_name}" open: "Das Gespräch wurde von wieder eröffnet %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "%{user_name} von %{assignee_name} zugewiesen" assigned: "%{user_name} von %{assignee_name} zugewiesen"
removed: "Gespräch nicht zugewiesen von %{user_name}" removed: "Gespräch nicht zugewiesen von %{user_name}"

View file

@ -32,6 +32,7 @@ el:
status: status:
resolved: "Η συνομιλία έχει επιλυθεί από τον %{user_name}" resolved: "Η συνομιλία έχει επιλυθεί από τον %{user_name}"
open: "Έγινε επαναφορά της συνομιλίας από τον %{user_name}" open: "Έγινε επαναφορά της συνομιλίας από τον %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Ανατέθηκε στον %{assignee_name} από τον %{user_name}" assigned: "Ανατέθηκε στον %{assignee_name} από τον %{user_name}"
removed: "Η συνομιλία σημάνθηκε ως μη ανατεθειμένη από τον %{user_name}" removed: "Η συνομιλία σημάνθηκε ως μη ανατεθειμένη από τον %{user_name}"

View file

@ -47,6 +47,7 @@ en:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
self_assigned: "%{user_name} self-assigned this conversation" self_assigned: "%{user_name} self-assigned this conversation"
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"

View file

@ -32,6 +32,7 @@ es:
status: status:
resolved: "La conversación fue marcada por %{user_name}" resolved: "La conversación fue marcada por %{user_name}"
open: "La conversación fue reabierta por %{user_name}" open: "La conversación fue reabierta por %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Asignado a %{assignee_name} por %{user_name}" assigned: "Asignado a %{assignee_name} por %{user_name}"
removed: "Conversación no asignada por %{user_name}" removed: "Conversación no asignada por %{user_name}"

View file

@ -32,6 +32,7 @@ fa:
status: status:
resolved: "مکالمه توسط اپراتور %{user_name} حل شده، اعلام شده بود" resolved: "مکالمه توسط اپراتور %{user_name} حل شده، اعلام شده بود"
open: "گفتگو توسط اپراتور %{user_name} مجددا باز شده بود" open: "گفتگو توسط اپراتور %{user_name} مجددا باز شده بود"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "%{user_name} گفتگو را به %{assignee_name} اختصاص داد" assigned: "%{user_name} گفتگو را به %{assignee_name} اختصاص داد"
removed: "گفتگو توسط اپراتور %{user_name} به وضعیت اختصاص داده نشده تغییر یافت" removed: "گفتگو توسط اپراتور %{user_name} به وضعیت اختصاص داده نشده تغییر یافت"

View file

@ -32,6 +32,7 @@ fi:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ fr:
status: status:
resolved: "La conversation a été marquée résolue par %{user_name}" resolved: "La conversation a été marquée résolue par %{user_name}"
open: "La conversation a été ré-ouverte par %{user_name}" open: "La conversation a été ré-ouverte par %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigné à %{assignee_name} par %{user_name}" assigned: "Assigné à %{assignee_name} par %{user_name}"
removed: "Responsable de la conversation supprimé par %{user_name}" removed: "Responsable de la conversation supprimé par %{user_name}"

View file

@ -32,6 +32,7 @@ hi:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ hu:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ it:
status: status:
resolved: "Conversazione segnata da %{user_name}" resolved: "Conversazione segnata da %{user_name}"
open: "La conversazione è stata riaperta da %{user_name}" open: "La conversazione è stata riaperta da %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assegnato a %{assignee_name} da %{user_name}" assigned: "Assegnato a %{assignee_name} da %{user_name}"
removed: "Conversazione non assegnata da %{user_name}" removed: "Conversazione non assegnata da %{user_name}"

View file

@ -32,6 +32,7 @@ ja:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ ko:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ ml:
status: status:
resolved: "സംഭാഷണം %{user_name} പരിഹരിച്ചതായി അടയാളപ്പെടുത്തി" resolved: "സംഭാഷണം %{user_name} പരിഹരിച്ചതായി അടയാളപ്പെടുത്തി"
open: "സംഭാഷണം %{user_name} വീണ്ടും തുറന്നു" open: "സംഭാഷണം %{user_name} വീണ്ടും തുറന്നു"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "%{assignee_name} %{user_name}-നെ നിയുക്തനാക്കി " assigned: "%{assignee_name} %{user_name}-നെ നിയുക്തനാക്കി "
removed: "%{user_name} സംഭാഷണം നിയുക്തമല്ലാതാക്കി" removed: "%{user_name} സംഭാഷണം നിയുക്തമല്ലാതാക്കി"

View file

@ -32,6 +32,7 @@ nl:
status: status:
resolved: "Gesprek werd gemarkeerd door %{user_name}" resolved: "Gesprek werd gemarkeerd door %{user_name}"
open: "Gesprek werd heropend door %{user_name}" open: "Gesprek werd heropend door %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Toegewezen aan %{assignee_name} door %{user_name}" assigned: "Toegewezen aan %{assignee_name} door %{user_name}"
removed: "Gesprek niet toegewezen door %{user_name}" removed: "Gesprek niet toegewezen door %{user_name}"

View file

@ -32,6 +32,7 @@ pl:
status: status:
resolved: "Rozmowa została oznaczona przez %{user_name}" resolved: "Rozmowa została oznaczona przez %{user_name}"
open: "Rozmowa została ponownie otwarta przez %{user_name}" open: "Rozmowa została ponownie otwarta przez %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Przypisane do %{assignee_name} przez %{user_name}" assigned: "Przypisane do %{assignee_name} przez %{user_name}"
removed: "Rozmowa nieprzypisana przez %{user_name}" removed: "Rozmowa nieprzypisana przez %{user_name}"

View file

@ -32,6 +32,7 @@ pt:
status: status:
resolved: "Conversa foi marcada como resolvida por %{user_name}" resolved: "Conversa foi marcada como resolvida por %{user_name}"
open: "Conversa foi reaberta por %{user_name}" open: "Conversa foi reaberta por %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Atribuído a %{assignee_name} por %{user_name}" assigned: "Atribuído a %{assignee_name} por %{user_name}"
removed: "Conversa não atribuída por %{user_name}" removed: "Conversa não atribuída por %{user_name}"

View file

@ -32,6 +32,7 @@ pt:
status: status:
resolved: "Conversa foi marcada como resolvida por %{user_name}" resolved: "Conversa foi marcada como resolvida por %{user_name}"
open: "Conversa foi reaberta por %{user_name}" open: "Conversa foi reaberta por %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Atribuído a %{assignee_name} por %{user_name}" assigned: "Atribuído a %{assignee_name} por %{user_name}"
removed: "Conversa não atribuída por %{user_name}" removed: "Conversa não atribuída por %{user_name}"

View file

@ -32,6 +32,7 @@ ro:
status: status:
resolved: "Conversația a fost marcată de %{user_name}" resolved: "Conversația a fost marcată de %{user_name}"
open: "Conversația a fost redeschisă de %{user_name}" open: "Conversația a fost redeschisă de %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Atribuit lui %{assignee_name} de %{user_name}" assigned: "Atribuit lui %{assignee_name} de %{user_name}"
removed: "Conversație neasociată de %{user_name}" removed: "Conversație neasociată de %{user_name}"

View file

@ -32,6 +32,7 @@ ru:
status: status:
resolved: "%{user_name} завершил диалог" resolved: "%{user_name} завершил диалог"
open: "%{user_name} открыл заново диалог" open: "%{user_name} открыл заново диалог"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "%{user_name} назначил %{assignee_name} ответственным" assigned: "%{user_name} назначил %{assignee_name} ответственным"
removed: "Ответственный снят %{user_name}" removed: "Ответственный снят %{user_name}"

View file

@ -32,6 +32,7 @@ sk:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ sv:
status: status:
resolved: "Konversationen har markerats som löst av %{user_name}" resolved: "Konversationen har markerats som löst av %{user_name}"
open: "Konversationen öppnades igen av %{user_name}" open: "Konversationen öppnades igen av %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Tilldelad till %{assignee_name} av %{user_name}" assigned: "Tilldelad till %{assignee_name} av %{user_name}"
removed: "Konversation otilldelad av %{user_name}" removed: "Konversation otilldelad av %{user_name}"

View file

@ -32,6 +32,7 @@ ta:
status: status:
resolved: "உரையாடலுக்கு %{user_name} தீர்வு வழங்கியுள்ளார்" resolved: "உரையாடலுக்கு %{user_name} தீர்வு வழங்கியுள்ளார்"
open: "உரையாடலை %{user_name} மீண்டும் திறந்துள்ளார்" open: "உரையாடலை %{user_name} மீண்டும் திறந்துள்ளார்"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "%{user_name} இதை %{assignee_name}க்கு ஒதுக்கியுள்ளார்" assigned: "%{user_name} இதை %{assignee_name}க்கு ஒதுக்கியுள்ளார்"
removed: "%{user_name} இதை ஒதுக்க படாத உரையாடளாக்கியுள்ளார்" removed: "%{user_name} இதை ஒதுக்க படாத உரையாடளாக்கியுள்ளார்"

View file

@ -32,6 +32,7 @@ th:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ tr:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ uk:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Assigned to %{assignee_name} by %{user_name}" assigned: "Assigned to %{assignee_name} by %{user_name}"
removed: "Conversation unassigned by %{user_name}" removed: "Conversation unassigned by %{user_name}"

View file

@ -32,6 +32,7 @@ vi:
status: status:
resolved: "Cuộc trò chuyện được đánh dấu là đã giải quyết bởi %{user_name}" resolved: "Cuộc trò chuyện được đánh dấu là đã giải quyết bởi %{user_name}"
open: "Cuộc trò chuyện đã được mở lại bởi %{user_name}" open: "Cuộc trò chuyện đã được mở lại bởi %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "Chỉ định %{assignee_name} bởi %{user_name}" assigned: "Chỉ định %{assignee_name} bởi %{user_name}"
removed: "Cuộc hội thoại chưa được chỉ định bởi %{user_name}" removed: "Cuộc hội thoại chưa được chỉ định bởi %{user_name}"

View file

@ -46,7 +46,8 @@ zh-TW:
activity: activity:
status: status:
resolved: "被%{user_name}標記的對話已解決。" resolved: "被%{user_name}標記的對話已解決。"
open: "被%{user_name}恢復對話。" open: "被%{user_name}恢復對話。"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "被%{user_name}分配給%{assignee_name}。" assigned: "被%{user_name}分配給%{assignee_name}。"
removed: "對話被%{user_name}設定成未分配。" removed: "對話被%{user_name}設定成未分配。"

View file

@ -32,6 +32,7 @@ zh-CN:
status: status:
resolved: "对话被标记由 %{user_name} 解决" resolved: "对话被标记由 %{user_name} 解决"
open: "对话被 %{user_name} 重新打开" open: "对话被 %{user_name} 重新打开"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "由 %{assignee_name} 分配给 %{user_name}" assigned: "由 %{assignee_name} 分配给 %{user_name}"
removed: "对话未被 %{user_name} 分配" removed: "对话未被 %{user_name} 分配"

View file

@ -32,6 +32,7 @@ zh-CN:
status: status:
resolved: "对话被标记由 %{user_name} 解决" resolved: "对话被标记由 %{user_name} 解决"
open: "对话被 %{user_name} 重新打开" open: "对话被 %{user_name} 重新打开"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "由 %{assignee_name} 分配给 %{user_name}" assigned: "由 %{assignee_name} 分配给 %{user_name}"
removed: "对话未被 %{user_name} 分配" removed: "对话未被 %{user_name} 分配"

View file

@ -47,6 +47,7 @@ zh_TW:
status: status:
resolved: "被%{user_name}標記的對話已解決。" resolved: "被%{user_name}標記的對話已解決。"
open: "被%{user_name}恢復對話。" open: "被%{user_name}恢復對話。"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
assigned: "被%{user_name}分配給%{assignee_name}。" assigned: "被%{user_name}分配給%{assignee_name}。"
removed: "對話被%{user_name}設定成未分配。" removed: "對話被%{user_name}設定成未分配。"

View file

@ -0,0 +1,5 @@
class AddAutoResolveDurationToAccount < ActiveRecord::Migration[6.0]
def change
add_column :accounts, :auto_resolve_duration, :integer
end
end

View file

@ -49,6 +49,7 @@ ActiveRecord::Schema.define(version: 2020_10_27_135006) do
t.string "support_email", limit: 100 t.string "support_email", limit: 100
t.integer "settings_flags", default: 0, null: false t.integer "settings_flags", default: 0, null: false
t.integer "feature_flags", default: 0, null: false t.integer "feature_flags", default: 0, null: false
t.integer "auto_resolve_duration"
t.string "timezone", default: "UTC" t.string "timezone", default: "UTC"
end end

View file

@ -130,6 +130,8 @@ RSpec.describe 'Accounts API', type: :request do
context 'when it is an authenticated user' do context 'when it is an authenticated user' do
it 'shows an account' do it 'shows an account' do
account.update(auto_resolve_duration: 30)
get "/api/v1/accounts/#{account.id}", get "/api/v1/accounts/#{account.id}",
headers: admin.create_new_auth_token, headers: admin.create_new_auth_token,
as: :json as: :json
@ -139,6 +141,7 @@ RSpec.describe 'Accounts API', type: :request do
expect(response.body).to include(account.locale) expect(response.body).to include(account.locale)
expect(response.body).to include(account.domain) expect(response.body).to include(account.domain)
expect(response.body).to include(account.support_email) expect(response.body).to include(account.support_email)
expect(response.body).to include(account.auto_resolve_duration.to_s)
end end
end end
end end
@ -169,7 +172,8 @@ RSpec.describe 'Accounts API', type: :request do
name: 'New Name', name: 'New Name',
locale: 'en', locale: 'en',
domain: 'example.com', domain: 'example.com',
support_email: 'care@example.com' support_email: 'care@example.com',
auto_resolve_duration: 40
} }
it 'modifies an account' do it 'modifies an account' do
@ -183,6 +187,7 @@ RSpec.describe 'Accounts API', type: :request do
expect(account.reload.locale).to eq(params[:locale]) expect(account.reload.locale).to eq(params[:locale])
expect(account.reload.domain).to eq(params[:domain]) expect(account.reload.domain).to eq(params[:domain])
expect(account.reload.support_email).to eq(params[:support_email]) expect(account.reload.support_email).to eq(params[:support_email])
expect(account.reload.auto_resolve_duration).to eq(params[:auto_resolve_duration])
end end
end end
end end

View file

@ -0,0 +1,34 @@
require 'rails_helper'
RSpec.describe AutoResolveConversationsJob, type: :job do
subject(:job) { described_class.perform_later(conversation.id) }
let!(:account) { create(:account) }
let!(:conversation) { create(:conversation, account: account) }
it 'queues the job' do
expect { job }.to have_enqueued_job(described_class)
.with(conversation.id)
.on_queue('medium')
end
it 'does nothing when there is no auto resolve duration' do
described_class.perform_now(conversation.id)
expect(conversation.reload.status).to eq('open')
end
it 're-queues the job if there is still time left to allow inactivity' do
account.update(auto_resolve_duration: 10)
conversation.update(last_activity_at: 3.days.ago)
expect { described_class.perform_now(conversation.id) }.to have_enqueued_job(described_class)
.with(conversation.id)
.on_queue('medium')
end
it 'resolves the issue if time of inactivity is more than the auto resolve duration' do
account.update(auto_resolve_duration: 10)
conversation.update(last_activity_at: 13.days.ago)
described_class.perform_now(conversation.id)
expect(conversation.reload.status).to eq('resolved')
end
end

View file

@ -4,6 +4,7 @@ require 'rails_helper'
RSpec.describe Account do RSpec.describe Account do
it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_numericality_of(:auto_resolve_duration).is_greater_than_or_equal_to(1) }
it { is_expected.to have_many(:users).through(:account_users) } it { is_expected.to have_many(:users).through(:account_users) }
it { is_expected.to have_many(:account_users) } it { is_expected.to have_many(:account_users) }

View file

@ -44,6 +44,19 @@ RSpec.describe Conversation, type: :model do
expect(Rails.configuration.dispatcher).to have_received(:dispatch) expect(Rails.configuration.dispatcher).to have_received(:dispatch)
.with(described_class::CONVERSATION_CREATED, kind_of(Time), conversation: conversation) .with(described_class::CONVERSATION_CREATED, kind_of(Time), conversation: conversation)
end end
it 'queues AutoResolveConversationsJob post creation if auto resolve duration present' do
account.update(auto_resolve_duration: 30)
expect do
create(
:conversation,
account: account,
contact: create(:contact, account: account),
inbox: inbox,
assignee: nil
)
end.to have_enqueued_job(AutoResolveConversationsJob)
end
end end
describe '.after_update' do describe '.after_update' do
@ -94,6 +107,26 @@ RSpec.describe Conversation, type: :model do
expect(conversation.messages.pluck(:content)).to include("Assigned to #{new_assignee.name} by #{old_assignee.name}") expect(conversation.messages.pluck(:content)).to include("Assigned to #{new_assignee.name} by #{old_assignee.name}")
expect(conversation.messages.pluck(:content)).to include("#{old_assignee.name} added #{label.title}") expect(conversation.messages.pluck(:content)).to include("#{old_assignee.name} added #{label.title}")
end end
it 'adds a message for system auto resolution if marked resolved by system' do
conversation2 = create(:conversation, status: 'open', account: account, assignee: old_assignee)
account.update(auto_resolve_duration: 40)
Current.user = nil
conversation2.update(status: :resolved)
system_resolved_message = "Conversation was marked resolved by system due to #{account.auto_resolve_duration} days of inactivity"
expect(conversation2.messages.pluck(:content)).to include(system_resolved_message)
end
it 'does not trigger AutoResolutionJob if conversation reopened and account does not have auto resolve duration' do
expect { conversation.update(status: :open) }
.not_to have_enqueued_job(AutoResolveConversationsJob).with(conversation.id)
end
it 'does trigger AutoResolutionJob if conversation reopened and account has auto resolve duration' do
account.update(auto_resolve_duration: 40)
expect { conversation.update(status: :open) }
.to have_enqueued_job(AutoResolveConversationsJob).with(conversation.id)
end
end end
describe '#round robin' do describe '#round robin' do