Merge branch 'develop' into feat-macros-frontend
This commit is contained in:
commit
ae304bc88e
49 changed files with 893 additions and 357 deletions
|
@ -11,7 +11,7 @@ class DeviseOverrides::PasswordsController < Devise::PasswordsController
|
|||
@recoverable = User.find_by(reset_password_token: reset_password_token)
|
||||
if @recoverable && reset_password_and_confirmation(@recoverable)
|
||||
send_auth_headers(@recoverable)
|
||||
render partial: 'devise/auth.json', locals: { resource: @recoverable }
|
||||
render partial: 'devise/auth', formats: [:json], locals: { resource: @recoverable }
|
||||
else
|
||||
render json: { message: 'Invalid token', redirect_url: '/' }, status: :unprocessable_entity
|
||||
end
|
||||
|
|
9
app/javascript/dashboard/api/agentBots.js
Normal file
9
app/javascript/dashboard/api/agentBots.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import ApiClient from './ApiClient';
|
||||
|
||||
class AgentBotsAPI extends ApiClient {
|
||||
constructor() {
|
||||
super('agent_bots', { accountScoped: true });
|
||||
}
|
||||
}
|
||||
|
||||
export default new AgentBotsAPI();
|
|
@ -6,10 +6,6 @@ class MacrosAPI extends ApiClient {
|
|||
super('macros', { accountScoped: true });
|
||||
}
|
||||
|
||||
getSingleMacro(macroId) {
|
||||
return axios.get(`${this.url}/${macroId}`);
|
||||
}
|
||||
|
||||
executeMacro({ macroId, conversationIds }) {
|
||||
return axios.post(`${this.url}/${macroId}/execute`, {
|
||||
conversation_ids: conversationIds,
|
||||
|
|
13
app/javascript/dashboard/api/specs/agentBots.spec.js
Normal file
13
app/javascript/dashboard/api/specs/agentBots.spec.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import AgentBotsAPI from '../agentBots';
|
||||
import ApiClient from '../ApiClient';
|
||||
|
||||
describe('#AgentBotsAPI', () => {
|
||||
it('creates correct instance', () => {
|
||||
expect(AgentBotsAPI).toBeInstanceOf(ApiClient);
|
||||
expect(AgentBotsAPI).toHaveProperty('get');
|
||||
expect(AgentBotsAPI).toHaveProperty('show');
|
||||
expect(AgentBotsAPI).toHaveProperty('create');
|
||||
expect(AgentBotsAPI).toHaveProperty('update');
|
||||
expect(AgentBotsAPI).toHaveProperty('delete');
|
||||
});
|
||||
});
|
|
@ -8,6 +8,7 @@ describe('#macrosAPI', () => {
|
|||
expect(macros).toHaveProperty('create');
|
||||
expect(macros).toHaveProperty('update');
|
||||
expect(macros).toHaveProperty('delete');
|
||||
expect(macros).toHaveProperty('show');
|
||||
expect(macros.url).toBe('/api/v1/macros');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import { frontendURL } from '../../../../helper/URLHelper';
|
|||
const settings = accountId => ({
|
||||
parentNav: 'settings',
|
||||
routes: [
|
||||
'agent_bots',
|
||||
'agent_list',
|
||||
'canned_list',
|
||||
'labels_list',
|
||||
|
@ -77,6 +78,7 @@ const settings = accountId => ({
|
|||
{
|
||||
icon: 'automation',
|
||||
label: 'AUTOMATION',
|
||||
beta: true,
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL(`accounts/${accountId}/settings/automation/list`),
|
||||
toStateName: 'automation_list',
|
||||
|
@ -88,6 +90,15 @@ const settings = accountId => ({
|
|||
toState: frontendURL(`accounts/${accountId}/settings/macros`),
|
||||
toStateName: 'macros_wrapper',
|
||||
},
|
||||
{
|
||||
icon: 'bot',
|
||||
label: 'AGENT_BOTS',
|
||||
beta: true,
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL(`accounts/${accountId}/settings/agent-bots`),
|
||||
toStateName: 'agent_bots',
|
||||
featureFlagKey: 'agent_bots',
|
||||
},
|
||||
{
|
||||
icon: 'chat-multiple',
|
||||
label: 'CANNED_RESPONSES',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<li class="sidebar-item">
|
||||
<li v-show="isMenuItemVisible" class="sidebar-item">
|
||||
<div v-if="hasSubMenu" class="secondary-menu--wrap">
|
||||
<span class="secondary-menu--header fs-small">
|
||||
{{ $t(`SIDEBAR.${menuItem.label}`) }}
|
||||
|
@ -36,7 +36,7 @@
|
|||
{{ `${menuItem.count}` }}
|
||||
</span>
|
||||
<span
|
||||
v-if="menuItem.label === 'AUTOMATION'"
|
||||
v-if="menuItem.beta"
|
||||
data-view-component="true"
|
||||
label="Beta"
|
||||
class="beta"
|
||||
|
@ -114,10 +114,23 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({ activeInbox: 'getSelectedInbox' }),
|
||||
...mapGetters({
|
||||
activeInbox: 'getSelectedInbox',
|
||||
accountId: 'getCurrentAccountId',
|
||||
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
|
||||
}),
|
||||
hasSubMenu() {
|
||||
return !!this.menuItem.children;
|
||||
},
|
||||
isMenuItemVisible() {
|
||||
if (!this.menuItem.featureFlagKey) {
|
||||
return true;
|
||||
}
|
||||
return this.isFeatureEnabledonAccount(
|
||||
this.accountId,
|
||||
this.menuItem.featureFlagKey
|
||||
);
|
||||
},
|
||||
isInboxConversation() {
|
||||
return (
|
||||
this.$store.state.route.name === 'inbox_conversation' &&
|
||||
|
|
|
@ -141,9 +141,19 @@ export default {
|
|||
assignableAgentsUiFlags: 'inboxAssignableAgents/getUIFlags',
|
||||
}),
|
||||
assignableAgents() {
|
||||
return this.$store.getters['inboxAssignableAgents/getAssignableAgents'](
|
||||
this.inboxId
|
||||
);
|
||||
return [
|
||||
{
|
||||
confirmed: true,
|
||||
name: 'None',
|
||||
id: null,
|
||||
role: 'agent',
|
||||
account_id: 0,
|
||||
email: 'None',
|
||||
},
|
||||
...this.$store.getters['inboxAssignableAgents/getAssignableAgents'](
|
||||
this.inboxId
|
||||
),
|
||||
];
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
<div v-else class="agent-confirmation-container">
|
||||
<p>
|
||||
<p v-if="selectedAgent.id">
|
||||
{{
|
||||
$t('BULK_ACTION.ASSIGN_CONFIRMATION_LABEL', {
|
||||
conversationCount,
|
||||
|
@ -67,6 +67,15 @@
|
|||
<strong>
|
||||
{{ selectedAgent.name }}
|
||||
</strong>
|
||||
<span>?</span>
|
||||
</p>
|
||||
<p v-else>
|
||||
{{
|
||||
$t('BULK_ACTION.UNASSIGN_CONFIRMATION_LABEL', {
|
||||
conversationCount,
|
||||
conversationLabel,
|
||||
})
|
||||
}}
|
||||
</p>
|
||||
<div class="agent-confirmation-actions">
|
||||
<woot-button
|
||||
|
@ -82,7 +91,7 @@
|
|||
:is-loading="uiFlags.isUpdating"
|
||||
@click="submit"
|
||||
>
|
||||
{{ $t('BULK_ACTION.ASSIGN_LABEL') }}
|
||||
{{ $t('BULK_ACTION.YES') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -131,7 +140,17 @@ export default {
|
|||
agent.name.toLowerCase().includes(this.query.toLowerCase())
|
||||
);
|
||||
}
|
||||
return this.assignableAgents;
|
||||
return [
|
||||
{
|
||||
confirmed: true,
|
||||
name: 'None',
|
||||
id: null,
|
||||
role: 'agent',
|
||||
account_id: 0,
|
||||
email: 'None',
|
||||
},
|
||||
...this.assignableAgents,
|
||||
];
|
||||
},
|
||||
assignableAgents() {
|
||||
return this.$store.getters['inboxAssignableAgents/getAssignableAgents'](
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
"CONVERSATION": {
|
||||
"SELECT_A_CONVERSATION": "الرجاء اختيار محادثة من قائمة المحادثات",
|
||||
"CSAT_REPLY_MESSAGE": "الرجاء تقييم المحادثة",
|
||||
"404": "Sorry, we cannot find the conversation. Please try again",
|
||||
"SWITCH_VIEW_LAYOUT": "Switch the layout",
|
||||
"404": "عذراً، لا يمكننا العثور على المحادثة. الرجاء المحاولة مرة أخرى",
|
||||
"SWITCH_VIEW_LAYOUT": "تبديل التصميم",
|
||||
"DASHBOARD_APP_TAB_MESSAGES": "الرسائل",
|
||||
"UNVERIFIED_SESSION": "لم يتم التحقق من هوية هذا المستخدم",
|
||||
"NO_MESSAGE_1": "لا توجد رسائل بعد من العملاء في صندوق الوارد الخاص بك.",
|
||||
|
@ -63,30 +63,30 @@
|
|||
},
|
||||
"CARD_CONTEXT_MENU": {
|
||||
"PENDING": "تحديد كمعلق",
|
||||
"RESOLVED": "Mark as resolved",
|
||||
"RESOLVED": "تحديد كمحلولة",
|
||||
"REOPEN": "إعادة فتح المحادثة",
|
||||
"SNOOZE": {
|
||||
"TITLE": "Snooze",
|
||||
"TITLE": "غفوة",
|
||||
"NEXT_REPLY": "حتى الرد القادم",
|
||||
"TOMORROW": "حتى الغد",
|
||||
"NEXT_WEEK": "حتى الأسبوع القادم"
|
||||
},
|
||||
"ASSIGN_AGENT": "Assign agent",
|
||||
"ASSIGN_LABEL": "Assign label",
|
||||
"AGENTS_LOADING": "Loading agents...",
|
||||
"ASSIGN_TEAM": "Assign team",
|
||||
"ASSIGN_AGENT": "تعيين وكيل",
|
||||
"ASSIGN_LABEL": "إضافة وسم",
|
||||
"AGENTS_LOADING": "جاري تحميل الوكلاء...",
|
||||
"ASSIGN_TEAM": "تعيين فريق",
|
||||
"API": {
|
||||
"AGENT_ASSIGNMENT": {
|
||||
"SUCCESFUL": "Conversation id %{conversationId} assigned to \"%{agentName}\"",
|
||||
"FAILED": "Couldn't assign agent. Please try again."
|
||||
"SUCCESFUL": "معرف المحادثة %{conversationId} تم تعيينه ل \"%{agentName}\"",
|
||||
"FAILED": "تعذر تعيين الوكيل. الرجاء المحاولة مرة أخرى."
|
||||
},
|
||||
"LABEL_ASSIGNMENT": {
|
||||
"SUCCESFUL": "Assigned label #%{labelName} to conversation id %{conversationId}",
|
||||
"FAILED": "Couldn't assign label. Please try again."
|
||||
"SUCCESFUL": "تعيين تسمية #%{labelName} لمعرف المحادثة %{conversationId}",
|
||||
"FAILED": "تعذر تعيين التسمية. الرجاء المحاولة مرة أخرى."
|
||||
},
|
||||
"TEAM_ASSIGNMENT": {
|
||||
"SUCCESFUL": "Assigned team \"%{team}\" to conversation id %{conversationId}",
|
||||
"FAILED": "Couldn't assign team. Please try again."
|
||||
"SUCCESFUL": "الفريق المعين \"%{team}\" لمعرف المحادثة %{conversationId}",
|
||||
"FAILED": "تعذر تعيين الفريق. الرجاء المحاولة مرة أخرى."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -131,13 +131,13 @@
|
|||
},
|
||||
"VISIBLE_TO_AGENTS": "ملاحظة خاصة: مرئية فقط لأعضاء فريق العمل والموظفين",
|
||||
"CHANGE_STATUS": "تم تغيير حالة المحادثة",
|
||||
"CHANGE_STATUS_FAILED": "Conversation status change failed",
|
||||
"CHANGE_STATUS_FAILED": "فشل تغيير حالة المحادثة",
|
||||
"CHANGE_AGENT": "تم تغيير الموظف الذي تم إحالة المحادثة إليه",
|
||||
"CHANGE_AGENT_FAILED": "Assignee change failed",
|
||||
"ASSIGN_LABEL_SUCCESFUL": "Label assigned successfully",
|
||||
"ASSIGN_LABEL_FAILED": "Label assignment failed",
|
||||
"CHANGE_AGENT_FAILED": "فشل تغيير المحال إليه",
|
||||
"ASSIGN_LABEL_SUCCESFUL": "تم تعيين الوسم بنجاح",
|
||||
"ASSIGN_LABEL_FAILED": "فشل تعيين الوسم",
|
||||
"CHANGE_TEAM": "تم تغيير فريق المحادثة",
|
||||
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE} MB attachment limit",
|
||||
"FILE_SIZE_LIMIT": "الملف يتجاوز حد المرفق {MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE} ميغابايت",
|
||||
"MESSAGE_ERROR": "غير قادر على إرسال هذه الرسالة، الرجاء المحاولة مرة أخرى لاحقاً",
|
||||
"SENT_BY": "أرسلت بواسطة:",
|
||||
"BOT": "رد آلي",
|
||||
|
@ -151,7 +151,7 @@
|
|||
"CONTEXT_MENU": {
|
||||
"COPY": "نسخ",
|
||||
"DELETE": "حذف",
|
||||
"CREATE_A_CANNED_RESPONSE": "Add to canned responses"
|
||||
"CREATE_A_CANNED_RESPONSE": "إضافة إلى الردود السريعة"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
|
|
@ -6,98 +6,98 @@
|
|||
"SETTINGS_BUTTON": "الإعدادات",
|
||||
"NEW_BUTTON": "مقالة جديدة",
|
||||
"DROPDOWN_OPTIONS": {
|
||||
"PUBLISHED": "Published",
|
||||
"DRAFT": "Draft",
|
||||
"ARCHIVED": "Archived"
|
||||
"PUBLISHED": "نُشرت",
|
||||
"DRAFT": "مسودة",
|
||||
"ARCHIVED": "أرشفة"
|
||||
},
|
||||
"TITLES": {
|
||||
"ALL_ARTICLES": "All Articles",
|
||||
"MINE": "My Articles",
|
||||
"DRAFT": "Draft Articles",
|
||||
"ARCHIVED": "Archived Articles"
|
||||
"ALL_ARTICLES": "جميع المقالات",
|
||||
"MINE": "مقالاتي",
|
||||
"DRAFT": "مقالات مسودة",
|
||||
"ARCHIVED": "المقالات المؤرشفة"
|
||||
}
|
||||
},
|
||||
"EDIT_HEADER": {
|
||||
"ALL_ARTICLES": "All Articles",
|
||||
"ALL_ARTICLES": "جميع المقالات",
|
||||
"PUBLISH_BUTTON": "نشر",
|
||||
"MOVE_TO_ARCHIVE_BUTTON": "Move to archived",
|
||||
"MOVE_TO_ARCHIVE_BUTTON": "نقل إلى الأرشيف",
|
||||
"PREVIEW": "معاينة",
|
||||
"ADD_TRANSLATION": "إضافة ترجمة",
|
||||
"OPEN_SIDEBAR": "فتح الشريط الجانبي",
|
||||
"CLOSE_SIDEBAR": "إغلاق الشريط الجانبي",
|
||||
"SAVING": "Saving...",
|
||||
"SAVED": "Saved"
|
||||
"SAVING": "جاري الحفظ...",
|
||||
"SAVED": "تم الحفظ"
|
||||
},
|
||||
"ARTICLE_SETTINGS": {
|
||||
"TITLE": "Article Settings",
|
||||
"TITLE": "إعدادات المقالة",
|
||||
"FORM": {
|
||||
"CATEGORY": {
|
||||
"LABEL": "الفئة",
|
||||
"TITLE": "Select category",
|
||||
"PLACEHOLDER": "Select category",
|
||||
"NO_RESULT": "No category found",
|
||||
"SEARCH_PLACEHOLDER": "Search category"
|
||||
"TITLE": "اختر الفئة",
|
||||
"PLACEHOLDER": "اختر الفئة",
|
||||
"NO_RESULT": "لم يتم العثور على فئة",
|
||||
"SEARCH_PLACEHOLDER": "البحث عن فئة"
|
||||
},
|
||||
"AUTHOR": {
|
||||
"LABEL": "Author",
|
||||
"TITLE": "Select author",
|
||||
"PLACEHOLDER": "Select author",
|
||||
"NO_RESULT": "No authors found",
|
||||
"SEARCH_PLACEHOLDER": "Search author"
|
||||
"LABEL": "المؤلف",
|
||||
"TITLE": "اختر المؤلف",
|
||||
"PLACEHOLDER": "اختر المؤلف",
|
||||
"NO_RESULT": "لم يتم العثور على مؤلفين",
|
||||
"SEARCH_PLACEHOLDER": "البحث عن المؤلف"
|
||||
},
|
||||
"META_TITLE": {
|
||||
"LABEL": "Meta title",
|
||||
"PLACEHOLDER": "Add a meta title"
|
||||
"LABEL": "العنوان الوصفي",
|
||||
"PLACEHOLDER": "اضافة عنوان وصفي"
|
||||
},
|
||||
"META_DESCRIPTION": {
|
||||
"LABEL": "Meta description",
|
||||
"PLACEHOLDER": "Add your meta description for better SEO results..."
|
||||
"LABEL": "وصف التعريف",
|
||||
"PLACEHOLDER": "أضف وصفك للحصول على أفضل نتائج SEO..."
|
||||
},
|
||||
"META_TAGS": {
|
||||
"LABEL": "Meta tags",
|
||||
"PLACEHOLDER": "Add meta tags separated by comma..."
|
||||
"LABEL": "علامات الوصف",
|
||||
"PLACEHOLDER": "إضافة وسوم ميتا مفصولة بفاصلة..."
|
||||
}
|
||||
},
|
||||
"BUTTONS": {
|
||||
"ARCHIVE": "Archive article",
|
||||
"DELETE": "Delete article"
|
||||
"ARCHIVE": "المقالات المؤرشفة",
|
||||
"DELETE": "حذف المقال"
|
||||
}
|
||||
},
|
||||
"PORTAL": {
|
||||
"HEADER": "Portals",
|
||||
"DEFAULT": "Default",
|
||||
"NEW_BUTTON": "New Portal",
|
||||
"HEADER": "الصفحات",
|
||||
"DEFAULT": "افتراضي",
|
||||
"NEW_BUTTON": "بوابة جديدة",
|
||||
"ACTIVE_BADGE": "مفعل",
|
||||
"CHOOSE_LOCALE_LABEL": "Choose a locale",
|
||||
"LOADING_MESSAGE": "Loading portals...",
|
||||
"ARTICLES_LABEL": "articles",
|
||||
"NO_PORTALS_MESSAGE": "There are no available portals",
|
||||
"ADD_NEW_LOCALE": "Add a new locale",
|
||||
"CHOOSE_LOCALE_LABEL": "اختر لغة محلية",
|
||||
"LOADING_MESSAGE": "جاري تحميل البوابات ...",
|
||||
"ARTICLES_LABEL": "المقالات",
|
||||
"NO_PORTALS_MESSAGE": "لا توجد بوابات متاحة",
|
||||
"ADD_NEW_LOCALE": "إضافة لغة جديدة",
|
||||
"POPOVER": {
|
||||
"TITLE": "Portals",
|
||||
"PORTAL_SETTINGS": "Portal settings",
|
||||
"SUBTITLE": "You have multiple portals and can have different locales for each portal.",
|
||||
"TITLE": "الصفحات",
|
||||
"PORTAL_SETTINGS": "إعدادات البوابة",
|
||||
"SUBTITLE": "لديك بوابات متعددة ويمكن أن تحتوي على مواقع مختلفة لكل بوابة.",
|
||||
"CANCEL_BUTTON_LABEL": "إلغاء",
|
||||
"CHOOSE_LOCALE_BUTTON": "Choose Locale"
|
||||
"CHOOSE_LOCALE_BUTTON": "اختر لغة محلية"
|
||||
},
|
||||
"PORTAL_SETTINGS": {
|
||||
"LIST_ITEM": {
|
||||
"HEADER": {
|
||||
"COUNT_LABEL": "articles",
|
||||
"ADD": "Add locale",
|
||||
"VISIT": "Visit site",
|
||||
"COUNT_LABEL": "المقالات",
|
||||
"ADD": "إضافة لغة",
|
||||
"VISIT": "زيارة الموقع",
|
||||
"SETTINGS": "الإعدادات",
|
||||
"DELETE": "حذف"
|
||||
},
|
||||
"PORTAL_CONFIG": {
|
||||
"TITLE": "Portal Configurations",
|
||||
"TITLE": "اعدادات البوابة",
|
||||
"ITEMS": {
|
||||
"NAME": "الاسم",
|
||||
"DOMAIN": "Custom domain",
|
||||
"SLUG": "Slug",
|
||||
"TITLE": "Portal title",
|
||||
"THEME": "Theme color",
|
||||
"SUB_TEXT": "Portal sub text"
|
||||
"DOMAIN": "نطاق مخصص",
|
||||
"SLUG": "وصف مختصر",
|
||||
"TITLE": "عنوان البوابة",
|
||||
"THEME": "لون القالب",
|
||||
"SUB_TEXT": "النص الفرعي للبوابة"
|
||||
}
|
||||
},
|
||||
"AVAILABLE_LOCALES": {
|
||||
|
@ -109,7 +109,7 @@
|
|||
"CATEGORIES": "No. of categories",
|
||||
"SWAP": "Swap",
|
||||
"DELETE": "حذف",
|
||||
"DEFAULT_LOCALE": "Default"
|
||||
"DEFAULT_LOCALE": "افتراضي"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -152,7 +152,7 @@
|
|||
"EDIT": "Edit category",
|
||||
"DELETE": "Delete category"
|
||||
},
|
||||
"EMPTY_TEXT": "No categories found"
|
||||
"EMPTY_TEXT": "لم يتم العثور على فئات"
|
||||
}
|
||||
},
|
||||
"EDIT_BASIC_INFO": {
|
||||
|
@ -206,113 +206,113 @@
|
|||
"NAME": {
|
||||
"LABEL": "الاسم",
|
||||
"PLACEHOLDER": "Portal name",
|
||||
"HELP_TEXT": "The name will be used in the public facing portal internally.",
|
||||
"HELP_TEXT": "الاسم سيتم مشاهدتة من جميع من جميع زوار الصفحة.",
|
||||
"ERROR": "الاسم مطلوب"
|
||||
},
|
||||
"SLUG": {
|
||||
"LABEL": "Slug",
|
||||
"PLACEHOLDER": "Portal slug for urls",
|
||||
"ERROR": "Slug is required"
|
||||
"LABEL": "وصف مختصر",
|
||||
"PLACEHOLDER": "وصف مختصر لرابط البوابة",
|
||||
"ERROR": "الوصف مطلوب"
|
||||
},
|
||||
"DOMAIN": {
|
||||
"LABEL": "Custom Domain",
|
||||
"PLACEHOLDER": "Portal custom domain",
|
||||
"HELP_TEXT": "Add only If you want to use a custom domain for your portals.",
|
||||
"ERROR": "Custom Domain is required"
|
||||
"LABEL": "نطاق مخصص",
|
||||
"PLACEHOLDER": "نطاق البوابة المخصص",
|
||||
"HELP_TEXT": "أضف فقط إذا كنت ترغب في استخدام نطاق مخصص للبوابات الخاصة بك.",
|
||||
"ERROR": "النطاق المخصص مطلوب"
|
||||
},
|
||||
"HOME_PAGE_LINK": {
|
||||
"LABEL": "Home Page Link",
|
||||
"PLACEHOLDER": "Portal home page link",
|
||||
"HELP_TEXT": "The link used to return from the portal to the home page.",
|
||||
"ERROR": "Home Page Link is required"
|
||||
"LABEL": "رابط الصفحة الرئيسية",
|
||||
"PLACEHOLDER": "رابط الصفحة الرئيسية للبوابة",
|
||||
"HELP_TEXT": "الرابط المستخدم للعودة من البوابة إلى الصفحة الرئيسية.",
|
||||
"ERROR": "رابط الصفحة الرئيسية مطلوب"
|
||||
},
|
||||
"THEME_COLOR": {
|
||||
"LABEL": "Portal theme color",
|
||||
"HELP_TEXT": "This color will show as the theme color for the portal."
|
||||
"LABEL": "لون قالب البوابة",
|
||||
"HELP_TEXT": "هذا اللون سيظهر كلون للبوابة."
|
||||
},
|
||||
"PAGE_TITLE": {
|
||||
"LABEL": "Page Title",
|
||||
"PLACEHOLDER": "Portal page title",
|
||||
"HELP_TEXT": "The page title will be used in the public facing portal.",
|
||||
"ERROR": "Page title is required"
|
||||
"LABEL": "عنوان الصفحة",
|
||||
"PLACEHOLDER": "عنوان البوابة",
|
||||
"HELP_TEXT": "سيتم استخدام عنوان الصفحة في البوابة التي تواجه الجمهور.",
|
||||
"ERROR": "العنوان مطلوب"
|
||||
},
|
||||
"HEADER_TEXT": {
|
||||
"LABEL": "Header Text",
|
||||
"PLACEHOLDER": "Portal header text",
|
||||
"HELP_TEXT": "The Portal header text will be used in the public facing portal.",
|
||||
"ERROR": "Portal header text is required"
|
||||
"LABEL": "نص رأس الصفحة",
|
||||
"PLACEHOLDER": "نص رأس البوابة",
|
||||
"HELP_TEXT": "سيتم استخدام عنوان الصفحة في البوابة التي تواجه الجمهور.",
|
||||
"ERROR": "نص رأس البوابة مطلوب"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE_FOR_BASIC": "Portal created successfully.",
|
||||
"ERROR_MESSAGE_FOR_BASIC": "Couldn't create the portal. Try again.",
|
||||
"SUCCESS_MESSAGE_FOR_UPDATE": "Portal updated successfully.",
|
||||
"ERROR_MESSAGE_FOR_UPDATE": "Couldn't update the portal. Try again."
|
||||
"SUCCESS_MESSAGE_FOR_BASIC": "تم إنشاء البوابة بنجاح.",
|
||||
"ERROR_MESSAGE_FOR_BASIC": "تعذر إنشاء البوابة. حاول مرة أخرى.",
|
||||
"SUCCESS_MESSAGE_FOR_UPDATE": "تم تحديث البوابة بنجاح.",
|
||||
"ERROR_MESSAGE_FOR_UPDATE": "تعذر تحديث البوابة. حاول مرة أخرى."
|
||||
}
|
||||
},
|
||||
"ADD_LOCALE": {
|
||||
"TITLE": "Add a new locale",
|
||||
"SUB_TITLE": "This adds a new locale to your available translation list.",
|
||||
"PORTAL": "Portal",
|
||||
"TITLE": "إضافة لغة جديدة",
|
||||
"SUB_TITLE": "هذا يضيف لغة جديدة إلى قائمة الترجمة المتاحة لديك.",
|
||||
"PORTAL": "البوابة",
|
||||
"LOCALE": {
|
||||
"LABEL": "Locale",
|
||||
"PLACEHOLDER": "Choose a locale",
|
||||
"ERROR": "Locale is required"
|
||||
"LABEL": "اللغة",
|
||||
"PLACEHOLDER": "اختر لغة محلية",
|
||||
"ERROR": "اللغة مطلوبة"
|
||||
},
|
||||
"BUTTONS": {
|
||||
"CREATE": "Create locale",
|
||||
"CREATE": "إنشاء لغة",
|
||||
"CANCEL": "إلغاء"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale added successfully",
|
||||
"ERROR_MESSAGE": "Unable to add locale. Try again."
|
||||
"SUCCESS_MESSAGE": "تمت إضافة اللغة بنجاح",
|
||||
"ERROR_MESSAGE": "غير قادر على إضافة اللغة . حاول مرة أخرى."
|
||||
}
|
||||
},
|
||||
"CHANGE_DEFAULT_LOCALE": {
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Default locale updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update default locale. Try again."
|
||||
"SUCCESS_MESSAGE": "تم تحديث اللغة الغة الإفتراضية بنجاح",
|
||||
"ERROR_MESSAGE": "غير قادر على تحديث اللغة الافتراضية. حاول مرة أخرى."
|
||||
}
|
||||
},
|
||||
"DELETE_LOCALE": {
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale removed from portal successfully",
|
||||
"ERROR_MESSAGE": "Unable to remove locale from portal. Try again."
|
||||
"SUCCESS_MESSAGE": "تم إزالة اللغة من البوابة بنجاح",
|
||||
"ERROR_MESSAGE": "غير قادر على إزالة اللغة من البوابة. حاول مرة أخرى."
|
||||
}
|
||||
}
|
||||
},
|
||||
"TABLE": {
|
||||
"LOADING_MESSAGE": "Loading articles...",
|
||||
"404": "No articles matches your search 🔍",
|
||||
"NO_ARTICLES": "There are no available articles",
|
||||
"LOADING_MESSAGE": "جاري تحميل المقالات...",
|
||||
"404": "لا توجد مقالات تطابق بحثك 🔍",
|
||||
"NO_ARTICLES": "لا توجد مقالات متوفرة",
|
||||
"HEADERS": {
|
||||
"TITLE": "العنوان",
|
||||
"CATEGORY": "الفئة",
|
||||
"READ_COUNT": "Read count",
|
||||
"READ_COUNT": "عدد القراءات",
|
||||
"STATUS": "الحالة",
|
||||
"LAST_EDITED": "Last edited"
|
||||
"LAST_EDITED": "آخر تعديل"
|
||||
},
|
||||
"COLUMNS": {
|
||||
"BY": "بواسطة"
|
||||
}
|
||||
},
|
||||
"EDIT_ARTICLE": {
|
||||
"LOADING": "Loading article...",
|
||||
"TITLE_PLACEHOLDER": "Article title goes here",
|
||||
"CONTENT_PLACEHOLDER": "Write your article here",
|
||||
"LOADING": "جاري تحميل المقالات...",
|
||||
"TITLE_PLACEHOLDER": "عنوان المقالة يذهب هنا",
|
||||
"CONTENT_PLACEHOLDER": "اكتب مقالك هنا",
|
||||
"API": {
|
||||
"ERROR": "Error while saving article"
|
||||
"ERROR": "حدث خطأ أثناء حفظ المقالة"
|
||||
}
|
||||
},
|
||||
"PUBLISH_ARTICLE": {
|
||||
"API": {
|
||||
"ERROR": "Error while publishing article",
|
||||
"SUCCESS": "Article publishied successfully"
|
||||
"ERROR": "حدث خطأ أثناء نشر المقالة",
|
||||
"SUCCESS": "تم نشر المقالة بنجاح"
|
||||
}
|
||||
},
|
||||
"ARCHIVE_ARTICLE": {
|
||||
"API": {
|
||||
"ERROR": "Error while archiving article",
|
||||
"SUCCESS": "Article archived successfully"
|
||||
"ERROR": "حدث خطأ أثناء نشر المقالة",
|
||||
"SUCCESS": "تم أرشفة المقالة بنجاح"
|
||||
}
|
||||
},
|
||||
"DELETE_ARTICLE": {
|
||||
|
|
|
@ -414,7 +414,7 @@
|
|||
"CAMPAIGN": "الحملات",
|
||||
"PRE_CHAT_FORM": "نموذج ما قبل الدردشة",
|
||||
"BUSINESS_HOURS": "ساعات العمل",
|
||||
"WIDGET_BUILDER": "Widget Builder"
|
||||
"WIDGET_BUILDER": "منشئ اللايف شات"
|
||||
},
|
||||
"SETTINGS": "الإعدادات",
|
||||
"FEATURES": {
|
||||
|
@ -579,10 +579,10 @@
|
|||
"WIDGET_BUILDER": {
|
||||
"WIDGET_OPTIONS": {
|
||||
"AVATAR": {
|
||||
"LABEL": "Website Avatar",
|
||||
"LABEL": "صورة الموقع",
|
||||
"DELETE": {
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Avatar deleted successfully",
|
||||
"SUCCESS_MESSAGE": "الصورة الرمزية حذفت بنجاح",
|
||||
"ERROR_MESSAGE": "حدث خطأ، الرجاء المحاولة مرة أخرى"
|
||||
}
|
||||
}
|
||||
|
@ -590,53 +590,53 @@
|
|||
"WEBSITE_NAME": {
|
||||
"LABEL": "اسم الموقع",
|
||||
"PLACE_HOLDER": "أدخل اسم موقع الويب الخاص بك (مثال: Acme Inc)",
|
||||
"ERROR": "Please enter a valid website name"
|
||||
"ERROR": "الرجاء إدخال اسم موقع صالح"
|
||||
},
|
||||
"WELCOME_HEADING": {
|
||||
"LABEL": "العنوان الترحيبي",
|
||||
"PLACE_HOLDER": "Hi there!"
|
||||
"PLACE_HOLDER": "مرحبا بك!"
|
||||
},
|
||||
"WELCOME_TAGLINE": {
|
||||
"LABEL": "افتتاحية الترحيب",
|
||||
"PLACE_HOLDER": "نحن نجعل من السهل عليك التواصل معنا. اسألنا أي شيء، أو قم بمشاركتنا ملاحظاتك."
|
||||
},
|
||||
"REPLY_TIME": {
|
||||
"LABEL": "Reply Time",
|
||||
"LABEL": "وقت الرد",
|
||||
"IN_A_FEW_MINUTES": "في غضون دقائق قليلة",
|
||||
"IN_A_FEW_HOURS": "في غضون ساعات قليلة",
|
||||
"IN_A_DAY": "خلال يوم"
|
||||
},
|
||||
"WIDGET_COLOR_LABEL": "لون صندوق الدردشة",
|
||||
"WIDGET_BUBBLE_POSITION_LABEL": "Widget Bubble Position",
|
||||
"WIDGET_BUBBLE_TYPE_LABEL": "Widget Bubble Type",
|
||||
"WIDGET_BUBBLE_POSITION_LABEL": "موقع شعار اللايف شات",
|
||||
"WIDGET_BUBBLE_TYPE_LABEL": "شكل عرض اللايف شات",
|
||||
"WIDGET_BUBBLE_LAUNCHER_TITLE": {
|
||||
"DEFAULT": "تحدث الينا",
|
||||
"LABEL": "Widget Bubble Launcher Title",
|
||||
"LABEL": "عنوان ايقونة اللايف شات",
|
||||
"PLACE_HOLDER": "تحدث الينا"
|
||||
},
|
||||
"UPDATE": {
|
||||
"BUTTON_TEXT": "Update Widget Settings",
|
||||
"BUTTON_TEXT": "تحديث إعدادات اللايف شات",
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Widget settings updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update widget settings"
|
||||
"SUCCESS_MESSAGE": "تم تحديث إعدادت اللايف شات بنجاح",
|
||||
"ERROR_MESSAGE": "غير قادر على تحديث إعدادات اللايف شات"
|
||||
}
|
||||
},
|
||||
"WIDGET_VIEW_OPTION": {
|
||||
"PREVIEW": "معاينة",
|
||||
"SCRIPT": "Script"
|
||||
"SCRIPT": "النص"
|
||||
},
|
||||
"WIDGET_BUBBLE_POSITION": {
|
||||
"LEFT": "Left",
|
||||
"RIGHT": "Right"
|
||||
"LEFT": "يسار",
|
||||
"RIGHT": "يمين"
|
||||
},
|
||||
"WIDGET_BUBBLE_TYPE": {
|
||||
"STANDARD": "Standard",
|
||||
"EXPANDED_BUBBLE": "Expanded Bubble"
|
||||
"STANDARD": "عادي",
|
||||
"EXPANDED_BUBBLE": "ايقونت اللايف شات العرضية"
|
||||
}
|
||||
},
|
||||
"WIDGET_SCREEN": {
|
||||
"DEFAULT": "Default",
|
||||
"CHAT": "Chat"
|
||||
"DEFAULT": "افتراضي",
|
||||
"CHAT": "محادثة"
|
||||
},
|
||||
"REPLY_TIME": {
|
||||
"IN_A_FEW_MINUTES": "عادة نقوم بالرد خلال بضع دقائق",
|
||||
|
@ -649,11 +649,11 @@
|
|||
},
|
||||
"BODY": {
|
||||
"TEAM_AVAILABILITY": {
|
||||
"ONLINE": "We are Online",
|
||||
"ONLINE": "متواجدون لخدمتك",
|
||||
"OFFLINE": "نحن بعيدون في الوقت الحالي"
|
||||
},
|
||||
"USER_MESSAGE": "Hi",
|
||||
"AGENT_MESSAGE": "Hello"
|
||||
"USER_MESSAGE": "مرحبا",
|
||||
"AGENT_MESSAGE": "مرحبا"
|
||||
},
|
||||
"BRANDING_TEXT": "مدعوم بواسطة Chatwoot",
|
||||
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
|
||||
|
|
|
@ -20,17 +20,17 @@
|
|||
"NOTE": "عنوان بريدك الإلكتروني هو المعرف الخاص بك الذي ستستخدمه لتسجيل الدخول."
|
||||
},
|
||||
"SEND_MESSAGE": {
|
||||
"TITLE": "Hotkey to send messages",
|
||||
"NOTE": "You can select a hotkey (either Enter or Cmd/Ctrl+Enter) based on your preference of writing.",
|
||||
"UPDATE_SUCCESS": "Your settings have been updated successfully",
|
||||
"TITLE": "المفتاح الرئيسي لإرسال الرسائل",
|
||||
"NOTE": "يمكنك تحديد مفتاح سريع (إما Enter أو Cmd/Ctrl+Enter) استنادًا إلى تفضيلك للكتابة.",
|
||||
"UPDATE_SUCCESS": "تم تحديث الإعدادات الخاصة بك بنجاح",
|
||||
"CARD": {
|
||||
"ENTER_KEY": {
|
||||
"HEADING": "Enter (↵)",
|
||||
"CONTENT": "Send messages by pressing Enter key instead of clicking the send button."
|
||||
"CONTENT": "إرسال الرسائل بالضغط على مفتاح الإدخال بدلاً من النقر على زر الإرسال."
|
||||
},
|
||||
"CMD_ENTER_KEY": {
|
||||
"HEADING": "Cmd/Ctrl + Enter (⌘ + ↵)",
|
||||
"CONTENT": "Send messages by pressing Cmd/Ctrl + enter key instead of clicking the send button."
|
||||
"HEADING": "Cmd/Ctrl + Enter (<unk> + <unk> )",
|
||||
"CONTENT": "إرسال الرسائل بالضغط على Cmd/Ctrl + إدخال المفتاح بدلاً من النقر على زر الإرسال."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -141,8 +141,8 @@
|
|||
"TRAIL_BUTTON": "اشترك الآن",
|
||||
"DELETED_USER": "حذف المستخدم",
|
||||
"ACCOUNT_SUSPENDED": {
|
||||
"TITLE": "Account Suspended",
|
||||
"MESSAGE": "Your account is suspended. Please reach out to the support team for more information."
|
||||
"TITLE": "تم تعليق الحساب",
|
||||
"MESSAGE": "تم تعليق حسابك. يرجى الاتصال بفريق الدعم للمزيد من المعلومات."
|
||||
}
|
||||
},
|
||||
"COMPONENTS": {
|
||||
|
@ -190,7 +190,7 @@
|
|||
"CUSTOM_ATTRIBUTES": "سمات مخصصة",
|
||||
"AUTOMATION": "الأتمتة",
|
||||
"TEAMS": "الفرق",
|
||||
"BILLING": "Billing",
|
||||
"BILLING": "الفواتير",
|
||||
"CUSTOM_VIEWS_FOLDER": "المجلدات",
|
||||
"CUSTOM_VIEWS_SEGMENTS": "الأجزاء",
|
||||
"ALL_CONTACTS": "جميع جهات الاتصال",
|
||||
|
@ -212,33 +212,33 @@
|
|||
"REPORTS_OVERVIEW": "نظرة عامة",
|
||||
"FACEBOOK_REAUTHORIZE": "انتهت صلاحية اتصال الفيسبوك الخاص بك، يرجى إعادة الاتصال بصفحة الفيسبوك الخاصة بك لمواصلة الخدمات",
|
||||
"HELP_CENTER": {
|
||||
"TITLE": "Help Center (Beta)",
|
||||
"ALL_ARTICLES": "All Articles",
|
||||
"MY_ARTICLES": "My Articles",
|
||||
"DRAFT": "Draft",
|
||||
"ARCHIVED": "Archived",
|
||||
"TITLE": "مركز المساعدة (نسخة تجريبية)",
|
||||
"ALL_ARTICLES": "جميع المقالات",
|
||||
"MY_ARTICLES": "مقالاتي",
|
||||
"DRAFT": "مسودة",
|
||||
"ARCHIVED": "مؤرشفة",
|
||||
"CATEGORY": "الفئة",
|
||||
"CATEGORY_EMPTY_MESSAGE": "No categories found"
|
||||
"CATEGORY_EMPTY_MESSAGE": "لم يتم العثور على فئات"
|
||||
},
|
||||
"DOCS": "Read docs"
|
||||
"DOCS": "قراءة المستندات"
|
||||
},
|
||||
"BILLING_SETTINGS": {
|
||||
"TITLE": "Billing",
|
||||
"TITLE": "الفواتير",
|
||||
"CURRENT_PLAN": {
|
||||
"TITLE": "Current Plan",
|
||||
"PLAN_NOTE": "You are currently subscribed to the **%{plan}** plan with **%{quantity}** licenses"
|
||||
"TITLE": "الباقة الحالية",
|
||||
"PLAN_NOTE": "أنت مشترك حاليا في باقة**%{plan}** مع تراخيص **%{quantity}**"
|
||||
},
|
||||
"MANAGE_SUBSCRIPTION": {
|
||||
"TITLE": "Manage your subscription",
|
||||
"DESCRIPTION": "View your previous invoices, edit your billing details, or cancel your subscription.",
|
||||
"BUTTON_TXT": "Go to the billing portal"
|
||||
"TITLE": "إدارة الاشتراك الخاص بك",
|
||||
"DESCRIPTION": "عرض فواتيرك السابقة، تحرير تفاصيل الفوترة الخاصة بك، أو إلغاء اشتراكك.",
|
||||
"BUTTON_TXT": "الذهاب إلى بوابة الفوترة"
|
||||
},
|
||||
"CHAT_WITH_US": {
|
||||
"TITLE": "Need help?",
|
||||
"DESCRIPTION": "Do you face any issues in billing? We are here to help.",
|
||||
"TITLE": "تحتاج مساعدة؟",
|
||||
"DESCRIPTION": "هل تواجه أي مشاكل في الفواتير؟ نحن هنا للمساعدة.",
|
||||
"BUTTON_TXT": "تحدث الينا"
|
||||
},
|
||||
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
|
||||
"NO_BILLING_USER": "حساب الفوترة الخاص بك قيد الإعداد. الرجاء تحديث الصفحة وحاول مرة أخرى."
|
||||
},
|
||||
"CREATE_ACCOUNT": {
|
||||
"NO_ACCOUNT_WARNING": "أوه! لم نتمكن من العثور على الحساب. الرجاء إنشاء حساب جديد للمتابعة.",
|
||||
|
|
5
app/javascript/dashboard/i18n/locale/en/agentBots.json
Normal file
5
app/javascript/dashboard/i18n/locale/en/agentBots.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"AGENT_BOTS": {
|
||||
"HEADER": "Bots"
|
||||
}
|
||||
}
|
|
@ -2,9 +2,11 @@
|
|||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "%{conversationCount} conversations selected",
|
||||
"AGENT_SELECT_LABEL": "Select Agent",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure you want to assign %{conversationCount} %{conversationLabel} to",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign %{conversationCount} %{conversationLabel} to",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign %{conversationCount} %{conversationLabel}?",
|
||||
"GO_BACK_LABEL": "Go back",
|
||||
"ASSIGN_LABEL": "Assign",
|
||||
"YES": "Yes",
|
||||
"ASSIGN_AGENT_TOOLTIP": "Assign Agent",
|
||||
"ASSIGN_SUCCESFUL": "Conversations assigned successfully",
|
||||
"ASSIGN_FAILED": "Failed to assign conversations, please try again",
|
||||
|
|
|
@ -239,7 +239,9 @@
|
|||
},
|
||||
"API_CALLBACK": {
|
||||
"TITLE": "Callback URL",
|
||||
"SUBTITLE": "You have to configure the webhook URL in facebook developer portal with the URL mentioned here."
|
||||
"SUBTITLE": "You have to configure the webhook URL and the verification token in the Facebook Developer portal with the values shown below.",
|
||||
"WEBHOOK_URL": "Webhook URL",
|
||||
"WEBHOOK_VERIFICATION_TOKEN": "Webhook Verification Token"
|
||||
},
|
||||
"SUBMIT_BUTTON": "Create WhatsApp Channel",
|
||||
"API": {
|
||||
|
@ -357,7 +359,7 @@
|
|||
},
|
||||
"FINISH": {
|
||||
"TITLE": "Your Inbox is ready!",
|
||||
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting ",
|
||||
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
|
||||
"BUTTON_TEXT": "Take me there",
|
||||
"MORE_SETTINGS": "More settings",
|
||||
"WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
|
||||
|
|
|
@ -1,57 +1,59 @@
|
|||
import { default as _advancedFilters } from './advancedFilters.json';
|
||||
import { default as _agentMgmt } from './agentMgmt.json';
|
||||
import { default as _attributesMgmt } from './attributesMgmt.json';
|
||||
import { default as _automation } from './automation.json';
|
||||
import { default as _macros } from './macros.json';
|
||||
import { default as _bulkActions } from './bulkActions.json';
|
||||
import { default as _campaign } from './campaign.json';
|
||||
import { default as _cannedMgmt } from './cannedMgmt.json';
|
||||
import { default as _chatlist } from './chatlist.json';
|
||||
import { default as _contact } from './contact.json';
|
||||
import { default as _contactFilters } from './contactFilters.json';
|
||||
import { default as _conversation } from './conversation.json';
|
||||
import { default as _csatMgmtMgmt } from './csatMgmt.json';
|
||||
import { default as _generalSettings } from './generalSettings.json';
|
||||
import { default as _inboxMgmt } from './inboxMgmt.json';
|
||||
import { default as _integrationApps } from './integrationApps.json';
|
||||
import { default as _integrations } from './integrations.json';
|
||||
import { default as _labelsMgmt } from './labelsMgmt.json';
|
||||
import { default as _login } from './login.json';
|
||||
import { default as _report } from './report.json';
|
||||
import { default as _resetPassword } from './resetPassword.json';
|
||||
import { default as _setNewPassword } from './setNewPassword.json';
|
||||
import { default as _settings } from './settings.json';
|
||||
import { default as _signup } from './signup.json';
|
||||
import { default as _teamsSettings } from './teamsSettings.json';
|
||||
import { default as _whatsappTemplates } from './whatsappTemplates.json';
|
||||
import { default as _helpCenter } from './helpCenter.json';
|
||||
import advancedFilters from './advancedFilters.json';
|
||||
import agentBots from './agentBots.json';
|
||||
import agentMgmt from './agentMgmt.json';
|
||||
import attributesMgmt from './attributesMgmt.json';
|
||||
import automation from './automation.json';
|
||||
import bulkActions from './bulkActions.json';
|
||||
import campaign from './campaign.json';
|
||||
import cannedMgmt from './cannedMgmt.json';
|
||||
import chatlist from './chatlist.json';
|
||||
import contact from './contact.json';
|
||||
import contactFilters from './contactFilters.json';
|
||||
import conversation from './conversation.json';
|
||||
import csatMgmtMgmt from './csatMgmt.json';
|
||||
import generalSettings from './generalSettings.json';
|
||||
import helpCenter from './helpCenter.json';
|
||||
import inboxMgmt from './inboxMgmt.json';
|
||||
import integrationApps from './integrationApps.json';
|
||||
import integrations from './integrations.json';
|
||||
import labelsMgmt from './labelsMgmt.json';
|
||||
import login from './login.json';
|
||||
import macros from './macros.json';
|
||||
import report from './report.json';
|
||||
import resetPassword from './resetPassword.json';
|
||||
import setNewPassword from './setNewPassword.json';
|
||||
import settings from './settings.json';
|
||||
import signup from './signup.json';
|
||||
import teamsSettings from './teamsSettings.json';
|
||||
import whatsappTemplates from './whatsappTemplates.json';
|
||||
|
||||
export default {
|
||||
..._advancedFilters,
|
||||
..._agentMgmt,
|
||||
..._attributesMgmt,
|
||||
..._automation,
|
||||
..._campaign,
|
||||
..._cannedMgmt,
|
||||
..._chatlist,
|
||||
..._contact,
|
||||
..._contactFilters,
|
||||
..._conversation,
|
||||
..._csatMgmtMgmt,
|
||||
..._generalSettings,
|
||||
..._inboxMgmt,
|
||||
..._integrationApps,
|
||||
..._integrations,
|
||||
..._labelsMgmt,
|
||||
..._login,
|
||||
..._report,
|
||||
..._resetPassword,
|
||||
..._setNewPassword,
|
||||
..._settings,
|
||||
..._signup,
|
||||
..._teamsSettings,
|
||||
..._whatsappTemplates,
|
||||
..._bulkActions,
|
||||
..._helpCenter,
|
||||
..._macros,
|
||||
...advancedFilters,
|
||||
...agentBots,
|
||||
...agentMgmt,
|
||||
...attributesMgmt,
|
||||
...automation,
|
||||
...bulkActions,
|
||||
...campaign,
|
||||
...cannedMgmt,
|
||||
...chatlist,
|
||||
...contact,
|
||||
...contactFilters,
|
||||
...conversation,
|
||||
...csatMgmtMgmt,
|
||||
...generalSettings,
|
||||
...helpCenter,
|
||||
...inboxMgmt,
|
||||
...integrationApps,
|
||||
...integrations,
|
||||
...labelsMgmt,
|
||||
...login,
|
||||
...macros,
|
||||
...report,
|
||||
...resetPassword,
|
||||
...setNewPassword,
|
||||
...settings,
|
||||
...signup,
|
||||
...teamsSettings,
|
||||
...whatsappTemplates,
|
||||
};
|
||||
|
|
|
@ -175,6 +175,7 @@
|
|||
"CONTACTS": "Contacts",
|
||||
"HOME": "Home",
|
||||
"AGENTS": "Agents",
|
||||
"AGENT_BOTS": "Bots",
|
||||
"INBOXES": "Inboxes",
|
||||
"NOTIFICATIONS": "Notifications",
|
||||
"CANNED_RESPONSES": "Canned Responses",
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<div>Agent Bot list</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
agentBots: 'agentBots/getBots',
|
||||
uiFlags: 'agentBots/getUIFlags',
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('agentBots/get');
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,40 @@
|
|||
import SettingsContent from '../Wrapper';
|
||||
const Bot = () => import('./Index.vue');
|
||||
const CsmlEditBot = () => import('./csml/Edit.vue');
|
||||
const CsmlNewBot = () => import('./csml/New.vue');
|
||||
import { frontendURL } from '../../../../helper/URLHelper';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/settings/agent-bots'),
|
||||
roles: ['administrator'],
|
||||
component: SettingsContent,
|
||||
props: {
|
||||
headerTitle: 'AGENT_BOTS.HEADER',
|
||||
icon: 'bot',
|
||||
showNewButton: false,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'agent_bots',
|
||||
component: Bot,
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
path: 'csml/new',
|
||||
name: 'agent_bots_csml_new',
|
||||
component: CsmlNewBot,
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
path: 'csml/:botId',
|
||||
name: 'agent_bots_csml_edit',
|
||||
component: CsmlEditBot,
|
||||
roles: ['administrator'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<div>Component to edit CSML Bots</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<div>Component to create CSML Bots</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
|
@ -20,11 +20,26 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="medium-6 small-offset-3">
|
||||
<p class="config--label">
|
||||
{{ $t('INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_URL') }}
|
||||
</p>
|
||||
<woot-code
|
||||
v-if="isAWhatsappWhatsappCloudInbox"
|
||||
v-if="isWhatsAppCloudInbox"
|
||||
lang="html"
|
||||
:script="currentInbox.callback_webhook_url"
|
||||
/>
|
||||
<p class="config--label">
|
||||
{{
|
||||
$t(
|
||||
'INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_VERIFICATION_TOKEN'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<woot-code
|
||||
v-if="isWhatsAppCloudInbox"
|
||||
lang="html"
|
||||
:script="currentInbox.provider_config.webhook_verify_token"
|
||||
/>
|
||||
</div>
|
||||
<div class="medium-6 small-offset-3">
|
||||
<woot-code
|
||||
|
@ -99,7 +114,7 @@ export default {
|
|||
isASmsInbox() {
|
||||
return this.currentInbox.channel_type === 'Channel::Sms';
|
||||
},
|
||||
isAWhatsappWhatsappCloudInbox() {
|
||||
isWhatsAppCloudInbox() {
|
||||
return (
|
||||
this.currentInbox.channel_type === 'Channel::Whatsapp' &&
|
||||
this.currentInbox.provider === 'whatsapp_cloud'
|
||||
|
@ -124,7 +139,7 @@ export default {
|
|||
)}`;
|
||||
}
|
||||
|
||||
if (this.isAWhatsappWhatsappCloudInbox) {
|
||||
if (this.isWhatsAppCloudInbox) {
|
||||
return `${this.$t('INBOX_MGMT.FINISH.MESSAGE')}. ${this.$t(
|
||||
'INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.SUBTITLE'
|
||||
)}`;
|
||||
|
@ -159,4 +174,10 @@ export default {
|
|||
.settings-button {
|
||||
margin-right: var(--space-small);
|
||||
}
|
||||
|
||||
.config--label {
|
||||
color: var(--b-600);
|
||||
font-weight: var(--font-weight-medium);
|
||||
margin-top: var(--space-large);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -85,25 +85,6 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="medium-8 columns">
|
||||
<label :class="{ error: $v.webhookVerifyToken.$error }">
|
||||
<span>
|
||||
{{ $t('INBOX_MGMT.ADD.WHATSAPP.WEBHOOK_VERIFY_TOKEN.LABEL') }}
|
||||
</span>
|
||||
<input
|
||||
v-model.trim="webhookVerifyToken"
|
||||
type="text"
|
||||
:placeholder="
|
||||
$t('INBOX_MGMT.ADD.WHATSAPP.WEBHOOK_VERIFY_TOKEN.PLACEHOLDER')
|
||||
"
|
||||
@blur="$v.webhookVerifyToken.$touch"
|
||||
/>
|
||||
<span v-if="$v.webhookVerifyToken.$error" class="message">
|
||||
{{ $t('INBOX_MGMT.ADD.WHATSAPP.WEBHOOK_VERIFY_TOKEN.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="medium-12 columns">
|
||||
<woot-submit-button
|
||||
:loading="uiFlags.isCreating"
|
||||
|
@ -130,7 +111,6 @@ export default {
|
|||
apiKey: '',
|
||||
phoneNumberId: '',
|
||||
businessAccountId: '',
|
||||
webhookVerifyToken: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -142,7 +122,6 @@ export default {
|
|||
apiKey: { required },
|
||||
phoneNumberId: { required },
|
||||
businessAccountId: { required },
|
||||
webhookVerifyToken: { required },
|
||||
},
|
||||
methods: {
|
||||
async createChannel() {
|
||||
|
@ -164,7 +143,6 @@ export default {
|
|||
api_key: this.apiKey,
|
||||
phone_number_id: this.phoneNumberId,
|
||||
business_account_id: this.businessAccountId,
|
||||
webhook_verify_token: this.webhookVerifyToken,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
import { frontendURL } from '../../../helper/URLHelper';
|
||||
import account from './account/account.routes';
|
||||
import agent from './agents/agent.routes';
|
||||
import canned from './canned/canned.routes';
|
||||
import inbox from './inbox/inbox.routes';
|
||||
import integrations from './integrations/integrations.routes';
|
||||
import integrationapps from './integrationapps/integrations.routes';
|
||||
import labels from './labels/labels.routes';
|
||||
import profile from './profile/profile.routes';
|
||||
import reports from './reports/reports.routes';
|
||||
import campaigns from './campaigns/campaigns.routes';
|
||||
import teams from './teams/teams.routes';
|
||||
import agentBot from './agentBots/agentBot.routes';
|
||||
import attributes from './attributes/attributes.routes';
|
||||
import automation from './automation/automation.routes';
|
||||
import macros from './macros/macros.routes';
|
||||
import store from '../../../store';
|
||||
import billing from './billing/billing.routes';
|
||||
import campaigns from './campaigns/campaigns.routes';
|
||||
import canned from './canned/canned.routes';
|
||||
import inbox from './inbox/inbox.routes';
|
||||
import integrationapps from './integrationapps/integrations.routes';
|
||||
import integrations from './integrations/integrations.routes';
|
||||
import labels from './labels/labels.routes';
|
||||
import macros from './macros/macros.routes';
|
||||
import profile from './profile/profile.routes';
|
||||
import reports from './reports/reports.routes';
|
||||
import teams from './teams/teams.routes';
|
||||
import store from '../../../store';
|
||||
|
||||
export default {
|
||||
routes: [
|
||||
|
@ -31,6 +32,7 @@ export default {
|
|||
},
|
||||
...account.routes,
|
||||
...agent.routes,
|
||||
...agentBot.routes,
|
||||
...attributes.routes,
|
||||
...automation.routes,
|
||||
...billing.routes,
|
||||
|
|
|
@ -3,6 +3,7 @@ import Vuex from 'vuex';
|
|||
|
||||
import accounts from './modules/accounts';
|
||||
import agents from './modules/agents';
|
||||
import agentBots from './modules/agentBots';
|
||||
import attributes from './modules/attributes';
|
||||
import auth from './modules/auth';
|
||||
import automations from './modules/automations';
|
||||
|
@ -45,6 +46,7 @@ export default new Vuex.Store({
|
|||
modules: {
|
||||
accounts,
|
||||
agents,
|
||||
agentBots,
|
||||
attributes,
|
||||
auth,
|
||||
automations,
|
||||
|
|
107
app/javascript/dashboard/store/modules/agentBots.js
Normal file
107
app/javascript/dashboard/store/modules/agentBots.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
|
||||
import types from '../mutation-types';
|
||||
import AgentBotsAPI from '../../api/agentBots';
|
||||
import { throwErrorMessage } from '../utils/api';
|
||||
|
||||
export const state = {
|
||||
records: [],
|
||||
uiFlags: {
|
||||
isFetching: false,
|
||||
isFetchingItem: false,
|
||||
isCreating: false,
|
||||
isDeleting: false,
|
||||
isUpdating: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const getters = {
|
||||
getBots($state) {
|
||||
return $state.records;
|
||||
},
|
||||
getUIFlags($state) {
|
||||
return $state.uiFlags;
|
||||
},
|
||||
getBot: $state => botId => {
|
||||
const [bot] = $state.records.filter(record => record.id === Number(botId));
|
||||
return bot || {};
|
||||
},
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
get: async ({ commit }) => {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isFetching: true });
|
||||
try {
|
||||
const response = await AgentBotsAPI.get();
|
||||
commit(types.SET_AGENT_BOTS, response.data);
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
} finally {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isFetching: false });
|
||||
}
|
||||
},
|
||||
create: async ({ commit }, agentBotObj) => {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isCreating: true });
|
||||
try {
|
||||
const response = await AgentBotsAPI.create(agentBotObj);
|
||||
commit(types.ADD_AGENT_BOT, response.data);
|
||||
} catch (error) {
|
||||
throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isCreating: false });
|
||||
}
|
||||
},
|
||||
update: async ({ commit }, { id, ...agentBotObj }) => {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isUpdating: true });
|
||||
try {
|
||||
const response = await AgentBotsAPI.update(id, agentBotObj);
|
||||
commit(types.EDIT_AGENT_BOT, response.data);
|
||||
} catch (error) {
|
||||
throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isUpdating: false });
|
||||
}
|
||||
},
|
||||
delete: async ({ commit }, id) => {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isDeleting: true });
|
||||
try {
|
||||
await AgentBotsAPI.delete(id);
|
||||
commit(types.DELETE_AGENT_BOT, id);
|
||||
} catch (error) {
|
||||
throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isDeleting: false });
|
||||
}
|
||||
},
|
||||
show: async ({ commit }, id) => {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isFetchingItem: true });
|
||||
try {
|
||||
const { data } = await AgentBotsAPI.show(id);
|
||||
commit(types.DELETE_AGENT_BOT, data);
|
||||
} catch (error) {
|
||||
throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isFetchingItem: false });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const mutations = {
|
||||
[types.SET_AGENT_BOT_UI_FLAG]($state, data) {
|
||||
$state.uiFlags = {
|
||||
...$state.uiFlags,
|
||||
...data,
|
||||
};
|
||||
},
|
||||
[types.ADD_AGENT_BOT]: MutationHelpers.create,
|
||||
[types.SET_AGENT_BOTS]: MutationHelpers.set,
|
||||
[types.EDIT_AGENT_BOT]: MutationHelpers.update,
|
||||
[types.DELETE_AGENT_BOT]: MutationHelpers.destroy,
|
||||
};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
actions,
|
||||
state,
|
||||
getters,
|
||||
mutations,
|
||||
};
|
|
@ -1,10 +1,12 @@
|
|||
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
|
||||
import types from '../mutation-types';
|
||||
import MacrosAPI from '../../api/macros';
|
||||
import { throwErrorMessage } from '../utils/api';
|
||||
|
||||
export const state = {
|
||||
records: [],
|
||||
uiFlags: {
|
||||
isFetchingItem: false,
|
||||
isFetching: false,
|
||||
isCreating: false,
|
||||
isDeleting: false,
|
||||
|
@ -14,14 +16,14 @@ export const state = {
|
|||
};
|
||||
|
||||
export const getters = {
|
||||
getMacros(_state) {
|
||||
return _state.records;
|
||||
getMacros($state) {
|
||||
return $state.records;
|
||||
},
|
||||
getMacro: $state => id => {
|
||||
return $state.records.find(record => record.id === Number(id));
|
||||
},
|
||||
getUIFlags(_state) {
|
||||
return _state.uiFlags;
|
||||
getUIFlags($state) {
|
||||
return $state.uiFlags;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -37,16 +39,15 @@ export const actions = {
|
|||
commit(types.SET_MACROS_UI_FLAG, { isFetching: false });
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line consistent-return
|
||||
getSingleMacro: async function getMacroById({ commit }, macroId) {
|
||||
commit(types.SET_MACROS_UI_FLAG, { isFetching: true });
|
||||
commit(types.SET_MACROS_UI_FLAG, { isFetchingItem: true });
|
||||
try {
|
||||
const response = await MacrosAPI.getSingleMacro(macroId);
|
||||
return response.data.payload;
|
||||
const response = await MacrosAPI.show(macroId);
|
||||
commit(types.ADD_MACRO, response.data.payload);
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
} finally {
|
||||
commit(types.SET_MACROS_UI_FLAG, { isFetching: false });
|
||||
commit(types.SET_MACROS_UI_FLAG, { isFetchingItem: false });
|
||||
}
|
||||
},
|
||||
create: async function createMacro({ commit }, macrosObj) {
|
||||
|
@ -55,7 +56,7 @@ export const actions = {
|
|||
const response = await MacrosAPI.create(macrosObj);
|
||||
commit(types.ADD_MACRO, response.data.payload);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(types.SET_MACROS_UI_FLAG, { isCreating: false });
|
||||
}
|
||||
|
@ -65,7 +66,7 @@ export const actions = {
|
|||
try {
|
||||
await MacrosAPI.executeMacro(macrosObj);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(types.SET_MACROS_UI_FLAG, { isExecuting: false });
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ export const actions = {
|
|||
const response = await MacrosAPI.update(id, updateObj);
|
||||
commit(types.EDIT_MACRO, response.data.payload);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(types.SET_MACROS_UI_FLAG, { isUpdating: false });
|
||||
}
|
||||
|
@ -87,39 +88,21 @@ export const actions = {
|
|||
await MacrosAPI.delete(id);
|
||||
commit(types.DELETE_MACRO, id);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(types.SET_MACROS_UI_FLAG, { isDeleting: false });
|
||||
}
|
||||
},
|
||||
clone: async ({ commit }, id) => {
|
||||
commit(types.SET_MACROS_UI_FLAG, { isCloning: true });
|
||||
try {
|
||||
await MacrosAPI.clone(id);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
} finally {
|
||||
commit(types.SET_MACROS_UI_FLAG, { isCloning: false });
|
||||
}
|
||||
},
|
||||
uploadAttachment: async (_, file) => {
|
||||
try {
|
||||
const { data } = await MacrosAPI.attachment(file);
|
||||
return data.blob_id;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const mutations = {
|
||||
[types.SET_MACROS_UI_FLAG](_state, data) {
|
||||
_state.uiFlags = {
|
||||
..._state.uiFlags,
|
||||
[types.SET_MACROS_UI_FLAG]($state, data) {
|
||||
$state.uiFlags = {
|
||||
...$state.uiFlags,
|
||||
...data,
|
||||
};
|
||||
},
|
||||
[types.ADD_MACRO]: MutationHelpers.create,
|
||||
[types.ADD_MACRO]: MutationHelpers.setSingleRecord,
|
||||
[types.SET_MACROS]: MutationHelpers.set,
|
||||
[types.EDIT_MACRO]: MutationHelpers.update,
|
||||
[types.DELETE_MACRO]: MutationHelpers.destroy,
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
import axios from 'axios';
|
||||
import { actions } from '../../agentBots';
|
||||
import types from '../../../mutation-types';
|
||||
import { agentBotRecords } from './fixtures';
|
||||
|
||||
const commit = jest.fn();
|
||||
global.axios = axios;
|
||||
jest.mock('axios');
|
||||
|
||||
describe('#actions', () => {
|
||||
describe('#get', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
axios.get.mockResolvedValue({ data: agentBotRecords });
|
||||
await actions.get({ commit });
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isFetching: true }],
|
||||
[types.SET_AGENT_BOTS, agentBotRecords],
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isFetching: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct actions if API is error', async () => {
|
||||
axios.get.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await actions.get({ commit });
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isFetching: true }],
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isFetching: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('#create', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
axios.post.mockResolvedValue({ data: agentBotRecords[0] });
|
||||
await actions.create({ commit }, agentBotRecords[0]);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isCreating: true }],
|
||||
[types.ADD_AGENT_BOT, agentBotRecords[0]],
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isCreating: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct actions if API is error', async () => {
|
||||
axios.post.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await expect(actions.create({ commit })).rejects.toThrow(Error);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isCreating: true }],
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isCreating: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#update', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
axios.patch.mockResolvedValue({ data: agentBotRecords[0] });
|
||||
await actions.update({ commit }, agentBotRecords[0]);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isUpdating: true }],
|
||||
[types.EDIT_AGENT_BOT, agentBotRecords[0]],
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isUpdating: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct actions if API is error', async () => {
|
||||
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await expect(
|
||||
actions.update({ commit }, agentBotRecords[0])
|
||||
).rejects.toThrow(Error);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isUpdating: true }],
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isUpdating: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#delete', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
axios.delete.mockResolvedValue({ data: agentBotRecords[0] });
|
||||
await actions.delete({ commit }, agentBotRecords[0].id);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isDeleting: true }],
|
||||
[types.DELETE_AGENT_BOT, agentBotRecords[0].id],
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isDeleting: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct actions if API is error', async () => {
|
||||
axios.delete.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await expect(
|
||||
actions.delete({ commit }, agentBotRecords[0].id)
|
||||
).rejects.toThrow(Error);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isDeleting: true }],
|
||||
[types.SET_AGENT_BOT_UI_FLAG, { isDeleting: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
export const agentBotRecords = [
|
||||
{
|
||||
id: 11,
|
||||
name: 'Agent Bot 11',
|
||||
description: 'Agent Bot Description',
|
||||
type: 'csml',
|
||||
},
|
||||
|
||||
{
|
||||
id: 12,
|
||||
name: 'Agent Bot 12',
|
||||
description: 'Agent Bot Description 12',
|
||||
type: 'csml',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,31 @@
|
|||
import { getters } from '../../agentBots';
|
||||
import { agentBotRecords } from './fixtures';
|
||||
|
||||
describe('#getters', () => {
|
||||
it('getBots', () => {
|
||||
const state = { records: agentBotRecords };
|
||||
expect(getters.getBots(state)).toEqual(agentBotRecords);
|
||||
});
|
||||
|
||||
it('getBot', () => {
|
||||
const state = { records: agentBotRecords };
|
||||
expect(getters.getBot(state)(11)).toEqual(agentBotRecords[0]);
|
||||
});
|
||||
|
||||
it('getUIFlags', () => {
|
||||
const state = {
|
||||
uiFlags: {
|
||||
isFetching: true,
|
||||
isCreating: false,
|
||||
isUpdating: false,
|
||||
isDeleting: false,
|
||||
},
|
||||
};
|
||||
expect(getters.getUIFlags(state)).toEqual({
|
||||
isFetching: true,
|
||||
isCreating: false,
|
||||
isUpdating: false,
|
||||
isDeleting: false,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
import types from '../../../mutation-types';
|
||||
import { mutations } from '../../agentBots';
|
||||
import { agentBotRecords } from './fixtures';
|
||||
|
||||
describe('#mutations', () => {
|
||||
describe('#SET_AGENT_BOT_UI_FLAG', () => {
|
||||
it('set uiFlags', () => {
|
||||
const state = { uiFlags: { isFetchingItem: false } };
|
||||
mutations[types.SET_AGENT_BOT_UI_FLAG](state, { isFetchingItem: true });
|
||||
expect(state.uiFlags.isFetchingItem).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('#SET_AGENT_BOTS', () => {
|
||||
it('set agent bot records', () => {
|
||||
const state = { records: [] };
|
||||
mutations[types.SET_AGENT_BOTS](state, agentBotRecords);
|
||||
expect(state.records).toEqual(agentBotRecords);
|
||||
});
|
||||
});
|
||||
describe('#ADD_AGENT_BOT', () => {
|
||||
it('push newly created bot to the store', () => {
|
||||
const state = { records: [agentBotRecords[0]] };
|
||||
mutations[types.ADD_AGENT_BOT](state, agentBotRecords[1]);
|
||||
expect(state.records).toEqual([agentBotRecords[0], agentBotRecords[1]]);
|
||||
});
|
||||
});
|
||||
describe('#EDIT_AGENT_BOT', () => {
|
||||
it('update agent bot record', () => {
|
||||
const state = { records: [agentBotRecords[0]] };
|
||||
mutations[types.EDIT_AGENT_BOT](state, {
|
||||
id: 11,
|
||||
name: 'agent-bot-11',
|
||||
});
|
||||
expect(state.records[0].name).toEqual('agent-bot-11');
|
||||
});
|
||||
});
|
||||
describe('#DELETE_AGENT_BOT', () => {
|
||||
it('delete agent bot record', () => {
|
||||
const state = { records: [agentBotRecords[0]] };
|
||||
mutations[types.DELETE_AGENT_BOT](state, agentBotRecords[0]);
|
||||
expect(state.records).toEqual([agentBotRecords[0]]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -31,19 +31,19 @@ describe('#actions', () => {
|
|||
describe('#getMacroById', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
axios.get.mockResolvedValue({ data: { payload: macrosList[0] } });
|
||||
const data = await actions.getSingleMacro({ commit }, 22);
|
||||
expect(data).toEqual(macrosList[0]);
|
||||
await actions.getSingleMacro({ commit }, 22);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.default.SET_MACROS_UI_FLAG, { isFetching: true }],
|
||||
[types.default.SET_MACROS_UI_FLAG, { isFetching: false }],
|
||||
[types.default.SET_MACROS_UI_FLAG, { isFetchingItem: true }],
|
||||
[types.default.ADD_MACRO, macrosList[0]],
|
||||
[types.default.SET_MACROS_UI_FLAG, { isFetchingItem: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct actions if API is error', async () => {
|
||||
axios.get.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await actions.getSingleMacro({ commit }, 22);
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.default.SET_MACROS_UI_FLAG, { isFetching: true }],
|
||||
[types.default.SET_MACROS_UI_FLAG, { isFetching: false }],
|
||||
[types.default.SET_MACROS_UI_FLAG, { isFetchingItem: true }],
|
||||
[types.default.SET_MACROS_UI_FLAG, { isFetchingItem: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -246,6 +246,13 @@ export default {
|
|||
REMOVE_CATEGORY: 'REMOVE_CATEGORY',
|
||||
REMOVE_CATEGORY_ID: 'REMOVE_CATEGORY_ID',
|
||||
|
||||
// Agent Bots
|
||||
SET_AGENT_BOT_UI_FLAG: 'SET_AGENT_BOT_UI_FLAG',
|
||||
SET_AGENT_BOTS: 'SET_AGENT_BOTS',
|
||||
ADD_AGENT_BOT: 'ADD_AGENT_BOT',
|
||||
EDIT_AGENT_BOT: 'EDIT_AGENT_BOT',
|
||||
DELETE_AGENT_BOT: 'DELETE_AGENT_BOT',
|
||||
|
||||
// MACROS
|
||||
SET_MACROS_UI_FLAG: 'SET_MACROS_UI_FLAG',
|
||||
SET_MACROS: 'SET_MACROS',
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
"M6.5 2A2.5 2.5 0 0 0 4 4.5v15A2.5 2.5 0 0 0 6.5 22h13.25a.75.75 0 0 0 0-1.5H6.5a1 1 0 0 1-1-1h14.25a.75.75 0 0 0 .75-.75V4.5A2.5 2.5 0 0 0 18 2H6.5ZM19 18H5.5V4.5a1 1 0 0 1 1-1H18a1 1 0 0 1 1 1V18Z"
|
||||
],
|
||||
"book-open-globe-outline": "M3.5 5.75a.25.25 0 0 1 .25-.25H10c.69 0 1.25.56 1.25 1.25v8.959a6.49 6.49 0 0 1 1.5-2.646V6.75c0-.69.56-1.25 1.25-1.25h6.25a.25.25 0 0 1 .25.25v5.982A6.518 6.518 0 0 1 22 12.81V5.75A1.75 1.75 0 0 0 20.25 4H14c-.788 0-1.499.331-2 .863A2.742 2.742 0 0 0 10 4H3.75A1.75 1.75 0 0 0 2 5.75v12.5c0 .966.784 1.75 1.75 1.75H10c.495 0 .96-.13 1.36-.36a6.473 6.473 0 0 1-.343-1.663A1.248 1.248 0 0 1 10 18.5H3.75a.25.25 0 0 1-.25-.25V5.75ZM16.007 17c.04-1.415.248-2.669.553-3.585.171-.513.364-.893.554-1.134.195-.247.329-.281.386-.281.057 0 .192.034.386.281.19.241.383.62.554 1.134.305.916.513 2.17.553 3.585h-2.986Zm-.396-3.9c.108-.323.23-.622.368-.887A5.504 5.504 0 0 0 12.023 17h2.984c.04-1.5.26-2.866.604-3.9Zm3.778 0a6.133 6.133 0 0 0-.368-.887A5.504 5.504 0 0 1 22.978 17h-2.985c-.04-1.5-.26-2.866-.604-3.9Zm.604 4.9h2.985a5.504 5.504 0 0 1-3.957 4.787c.138-.265.26-.564.368-.886.345-1.035.564-2.4.604-3.901Zm-2.107 4.719c-.194.247-.329.281-.386.281-.057 0-.191-.034-.386-.281-.19-.241-.383-.62-.554-1.135-.305-.915-.513-2.17-.553-3.584h2.986c-.04 1.415-.248 2.669-.553 3.584-.171.514-.364.894-.554 1.135ZM12.023 18a5.504 5.504 0 0 0 3.956 4.787 6.133 6.133 0 0 1-.367-.886c-.346-1.035-.565-2.4-.605-3.901h-2.984Z",
|
||||
"bot-outline": "M17.753 14a2.25 2.25 0 0 1 2.25 2.25v.905a3.75 3.75 0 0 1-1.307 2.846C17.13 21.345 14.89 22 12 22c-2.89 0-5.128-.656-6.691-2a3.75 3.75 0 0 1-1.306-2.843v-.908A2.25 2.25 0 0 1 6.253 14h11.5Zm0 1.5h-11.5a.75.75 0 0 0-.75.75v.908c0 .655.286 1.278.784 1.706C7.545 19.945 9.44 20.502 12 20.502c2.56 0 4.458-.557 5.719-1.64a2.25 2.25 0 0 0 .784-1.706v-.906a.75.75 0 0 0-.75-.75ZM11.898 2.008 12 2a.75.75 0 0 1 .743.648l.007.102V3.5h3.5a2.25 2.25 0 0 1 2.25 2.25v4.505a2.25 2.25 0 0 1-2.25 2.25h-8.5a2.25 2.25 0 0 1-2.25-2.25V5.75A2.25 2.25 0 0 1 7.75 3.5h3.5v-.749a.75.75 0 0 1 .648-.743L12 2l-.102.007ZM16.25 5h-8.5a.75.75 0 0 0-.75.75v4.505c0 .414.336.75.75.75h8.5a.75.75 0 0 0 .75-.75V5.75a.75.75 0 0 0-.75-.75Zm-6.5 1.5a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5Zm4.492 0a1.25 1.25 0 1 1 0 2.499 1.25 1.25 0 0 1 0-2.499Z",
|
||||
"building-bank-outline": "M13.032 2.325a1.75 1.75 0 0 0-2.064 0L3.547 7.74c-.978.713-.473 2.26.736 2.26H4.5v5.8A2.75 2.75 0 0 0 3 18.25v1.5c0 .413.336.75.75.75h16.5a.75.75 0 0 0 .75-.75v-1.5a2.75 2.75 0 0 0-1.5-2.45V10h.217c1.21 0 1.713-1.547.736-2.26l-7.421-5.416Zm-1.18 1.211a.25.25 0 0 1 .295 0L18.95 8.5H5.05l6.803-4.964ZM18 10v5.5h-2V10h2Zm-3.5 0v5.5h-1.75V10h1.75Zm-3.25 0v5.5H9.5V10h1.75Zm-5.5 7h12.5c.69 0 1.25.56 1.25 1.25V19h-15v-.75c0-.69.56-1.25 1.25-1.25ZM6 15.5V10h2v5.5H6Z",
|
||||
"calendar-clock-outline": [
|
||||
"M21 6.25A3.25 3.25 0 0 0 17.75 3H6.25A3.25 3.25 0 0 0 3 6.25v11.5A3.25 3.25 0 0 0 6.25 21h5.772a6.471 6.471 0 0 1-.709-1.5H6.25a1.75 1.75 0 0 1-1.75-1.75V8.5h15v2.813a6.471 6.471 0 0 1 1.5.709V6.25ZM6.25 4.5h11.5c.966 0 1.75.784 1.75 1.75V7h-15v-.75c0-.966.784-1.75 1.75-1.75Z",
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
<li
|
||||
v-for="category in Object.keys(emojis)"
|
||||
:key="category"
|
||||
:class="{ active: selectedKey === category }"
|
||||
@click="changeCategory(category)"
|
||||
>
|
||||
<button
|
||||
v-dompurify-html="emojis[category][0]"
|
||||
class="emoji--item"
|
||||
:class="{ active: selectedKey === category }"
|
||||
@click="changeCategory(category)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
<h5 class="emoji-category--title">
|
||||
{{ selectedKey }}
|
||||
</h5>
|
||||
<div class="emoji--row">
|
||||
<h5 class="emoji-category--title">
|
||||
{{ selectedKey }}
|
||||
</h5>
|
||||
<button
|
||||
v-for="emoji in emojis[selectedKey]"
|
||||
:key="emoji"
|
||||
|
@ -64,16 +64,18 @@ export default {
|
|||
|
||||
$space-smaller: 4px;
|
||||
$space-small: 8px;
|
||||
$space-one: 10px;
|
||||
$space-slab: 12px;
|
||||
$space-normal: 16px;
|
||||
$space-two: 20px;
|
||||
$space-medium: 24px;
|
||||
|
||||
$font-size-tiny: 12px;
|
||||
$font-size-small: 14px;
|
||||
$font-size-default: 16px;
|
||||
$font-size-medium: 18px;
|
||||
|
||||
$color-bg: #ebf0f5;
|
||||
|
||||
.emoji-dialog {
|
||||
@include elegant-card;
|
||||
background: $color-white;
|
||||
|
@ -81,8 +83,8 @@ $font-size-medium: 18px;
|
|||
box-sizing: content-box;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -22 * $space-one;
|
||||
width: 32 * $space-one;
|
||||
top: -220px;
|
||||
width: 332px;
|
||||
z-index: 1;
|
||||
|
||||
&::before {
|
||||
|
@ -97,35 +99,44 @@ $font-size-medium: 18px;
|
|||
background: transparent;
|
||||
border: 0;
|
||||
font-size: $font-size-medium;
|
||||
height: $space-medium;
|
||||
border-radius: $space-smaller;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding: 0 $space-smaller;
|
||||
|
||||
&:hover {
|
||||
background: $color-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji--row {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
height: $space-one * 18;
|
||||
height: 200px;
|
||||
overflow-y: auto;
|
||||
padding: $space-smaller $space-normal;
|
||||
padding: $space-smaller;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.emoji--item {
|
||||
float: left;
|
||||
margin: $space-smaller;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-category--title {
|
||||
color: $color-heading;
|
||||
font-size: $font-size-small;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.emoji-category--title {
|
||||
color: $color-heading;
|
||||
font-size: $font-size-small;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
padding: $space-smaller $space-small;
|
||||
margin-top: $space-smaller;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.emoji-dialog--header {
|
||||
background-color: $color-body;
|
||||
background-color: $color-bg;
|
||||
border-top-left-radius: $space-small;
|
||||
border-top-right-radius: $space-small;
|
||||
padding: 0 $space-smaller;
|
||||
|
@ -135,21 +146,25 @@ $font-size-medium: 18px;
|
|||
list-style: none;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
padding: $space-smaller 0 0;
|
||||
padding: $space-smaller 0;
|
||||
|
||||
> li {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 2.4 * $space-one;
|
||||
justify-content: center;
|
||||
padding: $space-smaller $space-small;
|
||||
padding: $space-smaller;
|
||||
}
|
||||
|
||||
> .active {
|
||||
li .active {
|
||||
background: $color-white;
|
||||
border-top-left-radius: $space-smaller;
|
||||
border-top-right-radius: $space-smaller;
|
||||
}
|
||||
.emoji--item {
|
||||
font-size: $font-size-small;
|
||||
|
||||
&:hover {
|
||||
background: $color-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,7 +189,8 @@ export default {
|
|||
}
|
||||
|
||||
.emoji-dialog {
|
||||
right: space-smaller;
|
||||
right: $space-smaller;
|
||||
top: -278px;
|
||||
|
||||
&::before {
|
||||
right: $space-one;
|
||||
|
|
|
@ -36,7 +36,7 @@ class BulkActionsJob < ApplicationJob
|
|||
def available_params(params)
|
||||
return unless params[:fields]
|
||||
|
||||
params[:fields].delete_if { |_k, v| v.nil? }
|
||||
params[:fields].delete_if { |key, value| value.nil? && key == 'status' }
|
||||
end
|
||||
|
||||
def bulk_add_labels(conversation)
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
# account_id :integer not null
|
||||
# message_id :integer not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_attachments_on_account_id (account_id)
|
||||
# index_attachments_on_message_id (message_id)
|
||||
#
|
||||
|
||||
class Attachment < ApplicationRecord
|
||||
include Rails.application.routes.url_helpers
|
||||
|
|
|
@ -25,11 +25,12 @@ class Channel::Whatsapp < ApplicationRecord
|
|||
|
||||
# default at the moment is 360dialog lets change later.
|
||||
PROVIDERS = %w[default whatsapp_cloud].freeze
|
||||
before_validation :ensure_webhook_verify_token
|
||||
|
||||
validates :provider, inclusion: { in: PROVIDERS }
|
||||
|
||||
validates :phone_number, presence: true, uniqueness: true
|
||||
validate :validate_provider_config
|
||||
|
||||
after_create :sync_templates
|
||||
|
||||
def name
|
||||
|
@ -56,6 +57,10 @@ class Channel::Whatsapp < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def ensure_webhook_verify_token
|
||||
provider_config['webhook_verify_token'] ||= SecureRandom.hex(16) if provider == 'whatsapp_cloud'
|
||||
end
|
||||
|
||||
def validate_provider_config
|
||||
errors.add(:provider_config, 'Invalid Credentials') unless provider_service.validate_provider_config?
|
||||
end
|
||||
|
|
|
@ -31,8 +31,10 @@
|
|||
# index_conversations_on_account_id_and_display_id (account_id,display_id) UNIQUE
|
||||
# index_conversations_on_assignee_id_and_account_id (assignee_id,account_id)
|
||||
# index_conversations_on_campaign_id (campaign_id)
|
||||
# index_conversations_on_contact_id (contact_id)
|
||||
# index_conversations_on_contact_inbox_id (contact_inbox_id)
|
||||
# index_conversations_on_first_reply_created_at (first_reply_created_at)
|
||||
# index_conversations_on_inbox_id (inbox_id)
|
||||
# index_conversations_on_last_activity_at (last_activity_at)
|
||||
# index_conversations_on_status_and_account_id (status,account_id)
|
||||
# index_conversations_on_team_id (team_id)
|
||||
|
|
|
@ -15,3 +15,5 @@
|
|||
enabled: false
|
||||
- name: help_center
|
||||
enabled: true
|
||||
- name: agent_bots
|
||||
enabled: false
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
class AddIndexToMessageAttachments < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_index :attachments, :account_id
|
||||
add_index :attachments, :message_id
|
||||
add_index :conversations, :contact_id
|
||||
add_index :conversations, :inbox_id
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2022_09_30_025317) do
|
||||
ActiveRecord::Schema.define(version: 2022_10_10_212946) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
|
@ -148,6 +148,8 @@ ActiveRecord::Schema.define(version: 2022_09_30_025317) do
|
|||
t.datetime "updated_at", null: false
|
||||
t.string "fallback_title"
|
||||
t.string "extension"
|
||||
t.index ["account_id"], name: "index_attachments_on_account_id"
|
||||
t.index ["message_id"], name: "index_attachments_on_message_id"
|
||||
end
|
||||
|
||||
create_table "automation_rules", force: :cascade do |t|
|
||||
|
@ -410,8 +412,10 @@ ActiveRecord::Schema.define(version: 2022_09_30_025317) do
|
|||
t.index ["account_id"], name: "index_conversations_on_account_id"
|
||||
t.index ["assignee_id", "account_id"], name: "index_conversations_on_assignee_id_and_account_id"
|
||||
t.index ["campaign_id"], name: "index_conversations_on_campaign_id"
|
||||
t.index ["contact_id"], name: "index_conversations_on_contact_id"
|
||||
t.index ["contact_inbox_id"], name: "index_conversations_on_contact_inbox_id"
|
||||
t.index ["first_reply_created_at"], name: "index_conversations_on_first_reply_created_at"
|
||||
t.index ["inbox_id"], name: "index_conversations_on_inbox_id"
|
||||
t.index ["last_activity_at"], name: "index_conversations_on_last_activity_at"
|
||||
t.index ["status", "account_id"], name: "index_conversations_on_status_and_account_id"
|
||||
t.index ["team_id"], name: "index_conversations_on_team_id"
|
||||
|
|
|
@ -7,7 +7,7 @@ module RegexHelper
|
|||
UNICODE_CHARACTER_NUMBER_HYPHEN_UNDERSCORE = Regexp.new('\A[\p{L}\p{N}]+[\p{L}\p{N}_-]+\Z')
|
||||
MENTION_REGEX = Regexp.new('\[(@[\w_. ]+)\]\(mention://(?:user|team)/\d+/(.*?)+\)')
|
||||
|
||||
TWILIO_CHANNEL_SMS_REGEX = Regexp.new('^\+\d{1,14}\z')
|
||||
TWILIO_CHANNEL_WHATSAPP_REGEX = Regexp.new('^whatsapp:\+\d{1,14}\z')
|
||||
TWILIO_CHANNEL_SMS_REGEX = Regexp.new('^\+\d{1,15}\z')
|
||||
TWILIO_CHANNEL_WHATSAPP_REGEX = Regexp.new('^whatsapp:\+\d{1,15}\z')
|
||||
WHATSAPP_CHANNEL_REGEX = Regexp.new('^\d{1,14}\z')
|
||||
end
|
||||
|
|
|
@ -75,6 +75,46 @@ RSpec.describe 'Api::V1::Accounts::BulkActionsController', type: :request do
|
|||
expect(Conversation.first.status).to eq('open')
|
||||
end
|
||||
|
||||
it 'Bulk remove assignee id from conversations' do
|
||||
Conversation.first.update(assignee_id: agent_1.id)
|
||||
Conversation.second.update(assignee_id: agent_2.id)
|
||||
params = { type: 'Conversation', fields: { assignee_id: nil }, ids: Conversation.first(3).pluck(:display_id) }
|
||||
|
||||
expect(Conversation.first.status).to eq('open')
|
||||
expect(Conversation.first.assignee_id).to eq(agent_1.id)
|
||||
expect(Conversation.second.assignee_id).to eq(agent_2.id)
|
||||
|
||||
perform_enqueued_jobs do
|
||||
post "/api/v1/accounts/#{account.id}/bulk_actions",
|
||||
headers: agent.create_new_auth_token,
|
||||
params: params
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
expect(Conversation.first.assignee_id).to be_nil
|
||||
expect(Conversation.second.assignee_id).to be_nil
|
||||
expect(Conversation.first.status).to eq('open')
|
||||
end
|
||||
|
||||
it 'Do not bulk update status to nil' do
|
||||
Conversation.first.update(assignee_id: agent_1.id)
|
||||
Conversation.second.update(assignee_id: agent_2.id)
|
||||
params = { type: 'Conversation', fields: { status: nil }, ids: Conversation.first(3).pluck(:display_id) }
|
||||
|
||||
expect(Conversation.first.status).to eq('open')
|
||||
|
||||
perform_enqueued_jobs do
|
||||
post "/api/v1/accounts/#{account.id}/bulk_actions",
|
||||
headers: agent.create_new_auth_token,
|
||||
params: params
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
expect(Conversation.first.status).to eq('open')
|
||||
end
|
||||
|
||||
it 'Bulk update conversation status and assignee id' do
|
||||
params = { type: 'Conversation', fields: { assignee_id: agent_1.id, status: 'snoozed' }, ids: Conversation.first(3).pluck(:display_id) }
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ FactoryBot.define do
|
|||
channel_whatsapp.define_singleton_method(:sync_templates) { return } unless options.sync_templates
|
||||
channel_whatsapp.define_singleton_method(:validate_provider_config) { return } unless options.validate_provider_config
|
||||
if channel_whatsapp.provider == 'whatsapp_cloud'
|
||||
channel_whatsapp.provider_config = { 'api_key' => 'test_key', 'phone_number_id' => '123456789', 'business_account_id' => '123456789',
|
||||
'webhook_verify_token': 'test_token' }
|
||||
channel_whatsapp.provider_config = channel_whatsapp.provider_config.merge({ 'api_key' => 'test_key', 'phone_number_id' => '123456789',
|
||||
'business_account_id' => '123456789' })
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -20,4 +20,20 @@ RSpec.describe Channel::Whatsapp do
|
|||
expect(channel.save).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'webhook_verify_token' do
|
||||
it 'generates webhook_verify_token if not present' do
|
||||
channel = create(:channel_whatsapp, provider_config: { webhook_verify_token: nil }, provider: 'whatsapp_cloud', account: create(:account),
|
||||
validate_provider_config: false, sync_templates: false)
|
||||
|
||||
expect(channel.provider_config['webhook_verify_token']).not_to be_nil
|
||||
end
|
||||
|
||||
it 'does not generate webhook_verify_token if present' do
|
||||
channel = create(:channel_whatsapp, provider: 'whatsapp_cloud', provider_config: { webhook_verify_token: '123' }, account: create(:account),
|
||||
validate_provider_config: false, sync_templates: false)
|
||||
|
||||
expect(channel.provider_config['webhook_verify_token']).to eq '123'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -66,11 +66,11 @@ RSpec.describe ContactInbox do
|
|||
expect(valid_source_id.valid?).to be(true)
|
||||
expect(ci_character_in_source_id.valid?).to be(false)
|
||||
expect(ci_character_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for twilio sms inbox. valid Regex (?-mix:^\\+\\d{1,14}\\z)']
|
||||
['Source invalid source id for twilio sms inbox. valid Regex (?-mix:^\\+\\d{1,15}\\z)']
|
||||
)
|
||||
expect(ci_without_plus_in_source_id.valid?).to be(false)
|
||||
expect(ci_without_plus_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for twilio sms inbox. valid Regex (?-mix:^\\+\\d{1,14}\\z)']
|
||||
['Source invalid source id for twilio sms inbox. valid Regex (?-mix:^\\+\\d{1,15}\\z)']
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -83,11 +83,11 @@ RSpec.describe ContactInbox do
|
|||
expect(valid_source_id.valid?).to be(true)
|
||||
expect(ci_character_in_source_id.valid?).to be(false)
|
||||
expect(ci_character_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for twilio whatsapp inbox. valid Regex (?-mix:^whatsapp:\\+\\d{1,14}\\z)']
|
||||
['Source invalid source id for twilio whatsapp inbox. valid Regex (?-mix:^whatsapp:\\+\\d{1,15}\\z)']
|
||||
)
|
||||
expect(ci_without_plus_in_source_id.valid?).to be(false)
|
||||
expect(ci_without_plus_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for twilio whatsapp inbox. valid Regex (?-mix:^whatsapp:\\+\\d{1,14}\\z)']
|
||||
['Source invalid source id for twilio whatsapp inbox. valid Regex (?-mix:^whatsapp:\\+\\d{1,15}\\z)']
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue