From 5bd5395d31be0c778607e1702b4774b51b68b5d5 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 10 Oct 2022 15:23:33 -0700 Subject: [PATCH 1/9] chore: Add missing indexes for attachments table (#5588) - index for attachments table - index for conversations table --- app/models/attachment.rb | 5 +++++ app/models/conversation.rb | 2 ++ .../20221010212946_add_index_to_message_attachments.rb | 8 ++++++++ db/schema.rb | 6 +++++- 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20221010212946_add_index_to_message_attachments.rb diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 4fa1b7e57..cb877ce8a 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -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 diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 3a833ef09..29eab8433 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -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) diff --git a/db/migrate/20221010212946_add_index_to_message_attachments.rb b/db/migrate/20221010212946_add_index_to_message_attachments.rb new file mode 100644 index 000000000..1ba2be299 --- /dev/null +++ b/db/migrate/20221010212946_add_index_to_message_attachments.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index 0793a6587..d719bc9f7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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" From d727093538f1aa32572ccfada3d8e9a69c7bd331 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 10 Oct 2022 15:40:12 -0700 Subject: [PATCH 2/9] chore: Update translations from Crowdin (#5578) --- .../i18n/locale/ar/conversation.json | 40 ++-- .../dashboard/i18n/locale/ar/helpCenter.json | 208 +++++++++--------- .../dashboard/i18n/locale/ar/inboxMgmt.json | 44 ++-- .../dashboard/i18n/locale/ar/settings.json | 50 ++--- 4 files changed, 171 insertions(+), 171 deletions(-) diff --git a/app/javascript/dashboard/i18n/locale/ar/conversation.json b/app/javascript/dashboard/i18n/locale/ar/conversation.json index 6f15b2be1..471e0e8a4 100644 --- a/app/javascript/dashboard/i18n/locale/ar/conversation.json +++ b/app/javascript/dashboard/i18n/locale/ar/conversation.json @@ -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": { diff --git a/app/javascript/dashboard/i18n/locale/ar/helpCenter.json b/app/javascript/dashboard/i18n/locale/ar/helpCenter.json index 9700d028c..b7ae33e35 100644 --- a/app/javascript/dashboard/i18n/locale/ar/helpCenter.json +++ b/app/javascript/dashboard/i18n/locale/ar/helpCenter.json @@ -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": { diff --git a/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json index f1e998840..939a8c32b 100644 --- a/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json @@ -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};" diff --git a/app/javascript/dashboard/i18n/locale/ar/settings.json b/app/javascript/dashboard/i18n/locale/ar/settings.json index e9cd31feb..d02371b78 100644 --- a/app/javascript/dashboard/i18n/locale/ar/settings.json +++ b/app/javascript/dashboard/i18n/locale/ar/settings.json @@ -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 ( + )", + "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": "أوه! لم نتمكن من العثور على الحساب. الرجاء إنشاء حساب جديد للمتابعة.", From 2c8ded98aad326af421907f2bfb416c2dab77bee Mon Sep 17 00:00:00 2001 From: Tejaswini Chile Date: Tue, 11 Oct 2022 23:57:22 +0530 Subject: [PATCH 3/9] fix: Update format in password reset page (#5596) --- app/controllers/devise_overrides/passwords_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/devise_overrides/passwords_controller.rb b/app/controllers/devise_overrides/passwords_controller.rb index 501b9f90c..06092c5ab 100644 --- a/app/controllers/devise_overrides/passwords_controller.rb +++ b/app/controllers/devise_overrides/passwords_controller.rb @@ -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 From 0b5a956e053ee372c2ab5391d2eb31b7b3f4ec3a Mon Sep 17 00:00:00 2001 From: Konstantin Nosov Date: Tue, 11 Oct 2022 21:59:28 +0200 Subject: [PATCH 4/9] chore: Update the design for emoji picker (#4244) Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Co-authored-by: Pranav Raj S --- .../shared/components/emoji/EmojiInput.vue | 67 ++++++++++++------- .../widget/components/ChatInputWrap.vue | 3 +- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/app/javascript/shared/components/emoji/EmojiInput.vue b/app/javascript/shared/components/emoji/EmojiInput.vue index 387605913..84891b0e2 100644 --- a/app/javascript/shared/components/emoji/EmojiInput.vue +++ b/app/javascript/shared/components/emoji/EmojiInput.vue @@ -5,21 +5,21 @@
  • +
    + {{ selectedKey }} +
    -
    - {{ selectedKey }} -
    +

    + {{ $t('INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_URL') }} +

    +

    + {{ + $t( + 'INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_VERIFICATION_TOKEN' + ) + }} +

    +
    diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/CloudWhatsapp.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/CloudWhatsapp.vue index 073dc9c9a..a182bb8b2 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/CloudWhatsapp.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/CloudWhatsapp.vue @@ -85,25 +85,6 @@
    -
    - -
    -
    '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 diff --git a/spec/models/channel/whatsapp_spec.rb b/spec/models/channel/whatsapp_spec.rb index d96ffdc85..9a6620a5e 100644 --- a/spec/models/channel/whatsapp_spec.rb +++ b/spec/models/channel/whatsapp_spec.rb @@ -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 diff --git a/spec/models/contact_inbox_spec.rb b/spec/models/contact_inbox_spec.rb index aba062980..e38ad81e8 100644 --- a/spec/models/contact_inbox_spec.rb +++ b/spec/models/contact_inbox_spec.rb @@ -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 From 9b5c0de0eaf2fd8730cdfe25e1d6647fa66d2b02 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Wed, 12 Oct 2022 11:58:52 +1100 Subject: [PATCH 8/9] chore: Add router views for agent_bots (#5600) --- .../layout/config/sidebarItems/settings.js | 11 ++ .../sidebarComponents/SecondaryNavItem.vue | 19 +++- .../dashboard/i18n/locale/en/agentBots.json | 5 + .../dashboard/i18n/locale/en/index.js | 106 +++++++++--------- .../dashboard/i18n/locale/en/settings.json | 1 + .../dashboard/settings/agentBots/Index.vue | 18 +++ .../settings/agentBots/agentBot.routes.js | 40 +++++++ .../settings/agentBots/csml/Edit.vue | 6 + .../dashboard/settings/agentBots/csml/New.vue | 6 + .../dashboard/settings/settings.routes.js | 14 ++- .../FluentIcon/dashboard-icons.json | 1 + config/features.yml | 2 + 12 files changed, 168 insertions(+), 61 deletions(-) create mode 100644 app/javascript/dashboard/i18n/locale/en/agentBots.json create mode 100644 app/javascript/dashboard/routes/dashboard/settings/agentBots/Index.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/agentBots/agentBot.routes.js create mode 100644 app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/Edit.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/New.vue diff --git a/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js b/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js index 990b35d4a..9ea287963 100644 --- a/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js +++ b/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js @@ -3,6 +3,7 @@ import { frontendURL } from '../../../../helper/URLHelper'; const settings = accountId => ({ parentNav: 'settings', routes: [ + 'agent_bots', 'agent_list', 'canned_list', 'labels_list', @@ -74,10 +75,20 @@ const settings = accountId => ({ { icon: 'automation', label: 'AUTOMATION', + beta: true, hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/automation/list`), toStateName: 'automation_list', }, + { + 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', diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue index 220ab18fa..a7a6761cf 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue @@ -1,5 +1,5 @@ + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/agentBots/agentBot.routes.js b/app/javascript/dashboard/routes/dashboard/settings/agentBots/agentBot.routes.js new file mode 100644 index 000000000..e06f18a0b --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/agentBots/agentBot.routes.js @@ -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'], + }, + ], + }, + ], +}; diff --git a/app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/Edit.vue b/app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/Edit.vue new file mode 100644 index 000000000..112961193 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/Edit.vue @@ -0,0 +1,6 @@ + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/New.vue b/app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/New.vue new file mode 100644 index 000000000..84039a483 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/agentBots/csml/New.vue @@ -0,0 +1,6 @@ + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js b/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js index 8f3de8f4e..b4ac93570 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js +++ b/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js @@ -1,19 +1,20 @@ import { frontendURL } from '../../../helper/URLHelper'; import account from './account/account.routes'; import agent from './agents/agent.routes'; +import agentBot from './agentBots/agentBot.routes'; +import attributes from './attributes/attributes.routes'; +import automation from './automation/automation.routes'; +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 integrations from './integrations/integrations.routes'; import integrationapps from './integrationapps/integrations.routes'; +import integrations from './integrations/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 attributes from './attributes/attributes.routes'; -import automation from './automation/automation.routes'; import store from '../../../store'; -import billing from './billing/billing.routes'; +import teams from './teams/teams.routes'; export default { routes: [ @@ -30,6 +31,7 @@ export default { }, ...account.routes, ...agent.routes, + ...agentBot.routes, ...attributes.routes, ...automation.routes, ...billing.routes, diff --git a/app/javascript/shared/components/FluentIcon/dashboard-icons.json b/app/javascript/shared/components/FluentIcon/dashboard-icons.json index 8b9412efc..0bce7c813 100644 --- a/app/javascript/shared/components/FluentIcon/dashboard-icons.json +++ b/app/javascript/shared/components/FluentIcon/dashboard-icons.json @@ -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", diff --git a/config/features.yml b/config/features.yml index 6fbb73a78..01bfe00e5 100644 --- a/config/features.yml +++ b/config/features.yml @@ -15,3 +15,5 @@ enabled: false - name: help_center enabled: true +- name: agent_bots + enabled: false From 6c160ccad5244f068332348febc111c162e6de10 Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Date: Wed, 12 Oct 2022 11:24:17 +0530 Subject: [PATCH 9/9] feat: Add API module and Vuex store for Macros (#5603) --- app/javascript/dashboard/api/macros.js | 16 ++ .../dashboard/api/specs/macros.spec.js | 14 ++ .../dashboard/store/modules/macros.js | 117 ++++++++++++++ .../modules/specs/macros/actions.spec.js | 151 ++++++++++++++++++ .../store/modules/specs/macros/fixtures.js | 135 ++++++++++++++++ .../modules/specs/macros/getters.spec.js | 32 ++++ .../modules/specs/macros/mutations.spec.js | 38 +++++ .../dashboard/store/mutation-types.js | 7 + 8 files changed, 510 insertions(+) create mode 100644 app/javascript/dashboard/api/macros.js create mode 100644 app/javascript/dashboard/api/specs/macros.spec.js create mode 100644 app/javascript/dashboard/store/modules/macros.js create mode 100644 app/javascript/dashboard/store/modules/specs/macros/actions.spec.js create mode 100644 app/javascript/dashboard/store/modules/specs/macros/fixtures.js create mode 100644 app/javascript/dashboard/store/modules/specs/macros/getters.spec.js create mode 100644 app/javascript/dashboard/store/modules/specs/macros/mutations.spec.js diff --git a/app/javascript/dashboard/api/macros.js b/app/javascript/dashboard/api/macros.js new file mode 100644 index 000000000..7b123c9e8 --- /dev/null +++ b/app/javascript/dashboard/api/macros.js @@ -0,0 +1,16 @@ +/* global axios */ +import ApiClient from './ApiClient'; + +class MacrosAPI extends ApiClient { + constructor() { + super('macros', { accountScoped: true }); + } + + executeMacro({ macroId, conversationIds }) { + return axios.post(`${this.url}/${macroId}/execute`, { + conversation_ids: conversationIds, + }); + } +} + +export default new MacrosAPI(); diff --git a/app/javascript/dashboard/api/specs/macros.spec.js b/app/javascript/dashboard/api/specs/macros.spec.js new file mode 100644 index 000000000..94e936521 --- /dev/null +++ b/app/javascript/dashboard/api/specs/macros.spec.js @@ -0,0 +1,14 @@ +import macros from '../macros'; +import ApiClient from '../ApiClient'; + +describe('#macrosAPI', () => { + it('creates correct instance', () => { + expect(macros).toBeInstanceOf(ApiClient); + expect(macros).toHaveProperty('get'); + expect(macros).toHaveProperty('create'); + expect(macros).toHaveProperty('update'); + expect(macros).toHaveProperty('delete'); + expect(macros).toHaveProperty('show'); + expect(macros.url).toBe('/api/v1/macros'); + }); +}); diff --git a/app/javascript/dashboard/store/modules/macros.js b/app/javascript/dashboard/store/modules/macros.js new file mode 100644 index 000000000..952f53f17 --- /dev/null +++ b/app/javascript/dashboard/store/modules/macros.js @@ -0,0 +1,117 @@ +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, + isUpdating: false, + isExecuting: false, + }, +}; + +export const getters = { + getMacros($state) { + return $state.records; + }, + getMacro: $state => id => { + return $state.records.find(record => record.id === Number(id)); + }, + getUIFlags($state) { + return $state.uiFlags; + }, +}; + +export const actions = { + get: async function getMacros({ commit }) { + commit(types.SET_MACROS_UI_FLAG, { isFetching: true }); + try { + const response = await MacrosAPI.get(); + commit(types.SET_MACROS, response.data.payload); + } catch (error) { + // Ignore error + } finally { + commit(types.SET_MACROS_UI_FLAG, { isFetching: false }); + } + }, + getSingleMacro: async function getMacroById({ commit }, macroId) { + commit(types.SET_MACROS_UI_FLAG, { isFetchingItem: true }); + try { + const response = await MacrosAPI.show(macroId); + commit(types.ADD_MACRO, response.data.payload); + } catch (error) { + // Ignore error + } finally { + commit(types.SET_MACROS_UI_FLAG, { isFetchingItem: false }); + } + }, + create: async function createMacro({ commit }, macrosObj) { + commit(types.SET_MACROS_UI_FLAG, { isCreating: true }); + try { + const response = await MacrosAPI.create(macrosObj); + commit(types.ADD_MACRO, response.data.payload); + } catch (error) { + throwErrorMessage(error); + } finally { + commit(types.SET_MACROS_UI_FLAG, { isCreating: false }); + } + }, + execute: async function executeMacro({ commit }, macrosObj) { + commit(types.SET_MACROS_UI_FLAG, { isExecuting: true }); + try { + await MacrosAPI.executeMacro(macrosObj); + } catch (error) { + throwErrorMessage(error); + } finally { + commit(types.SET_MACROS_UI_FLAG, { isExecuting: false }); + } + }, + update: async ({ commit }, { id, ...updateObj }) => { + commit(types.SET_MACROS_UI_FLAG, { isUpdating: true }); + try { + const response = await MacrosAPI.update(id, updateObj); + commit(types.EDIT_MACRO, response.data.payload); + } catch (error) { + throwErrorMessage(error); + } finally { + commit(types.SET_MACROS_UI_FLAG, { isUpdating: false }); + } + }, + delete: async ({ commit }, id) => { + commit(types.SET_MACROS_UI_FLAG, { isDeleting: true }); + try { + await MacrosAPI.delete(id); + commit(types.DELETE_MACRO, id); + } catch (error) { + throwErrorMessage(error); + } finally { + commit(types.SET_MACROS_UI_FLAG, { isDeleting: false }); + } + }, +}; + +export const mutations = { + [types.SET_MACROS_UI_FLAG]($state, data) { + $state.uiFlags = { + ...$state.uiFlags, + ...data, + }; + }, + [types.ADD_MACRO]: MutationHelpers.setSingleRecord, + [types.SET_MACROS]: MutationHelpers.set, + [types.EDIT_MACRO]: MutationHelpers.update, + [types.DELETE_MACRO]: MutationHelpers.destroy, +}; + +export default { + namespaced: true, + actions, + state, + getters, + mutations, +}; diff --git a/app/javascript/dashboard/store/modules/specs/macros/actions.spec.js b/app/javascript/dashboard/store/modules/specs/macros/actions.spec.js new file mode 100644 index 000000000..95bba8e1d --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/macros/actions.spec.js @@ -0,0 +1,151 @@ +import axios from 'axios'; +import { actions } from '../../macros'; +import * as types from '../../../mutation-types'; +import macrosList 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: { payload: macrosList } }); + await actions.get({ commit }); + expect(commit.mock.calls).toEqual([ + [types.default.SET_MACROS_UI_FLAG, { isFetching: true }], + [types.default.SET_MACROS, macrosList], + [types.default.SET_MACROS_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.default.SET_MACROS_UI_FLAG, { isFetching: true }], + [types.default.SET_MACROS_UI_FLAG, { isFetching: false }], + ]); + }); + }); + + describe('#getMacroById', () => { + it('sends correct actions if API is success', async () => { + axios.get.mockResolvedValue({ data: { payload: macrosList[0] } }); + await actions.getSingleMacro({ commit }, 22); + expect(commit.mock.calls).toEqual([ + [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, { isFetchingItem: true }], + [types.default.SET_MACROS_UI_FLAG, { isFetchingItem: false }], + ]); + }); + }); + + describe('#create', () => { + it('sends correct actions if API is success', async () => { + axios.post.mockResolvedValue({ data: { payload: macrosList[0] } }); + await actions.create({ commit }, macrosList[0]); + expect(commit.mock.calls).toEqual([ + [types.default.SET_MACROS_UI_FLAG, { isCreating: true }], + [types.default.ADD_MACRO, macrosList[0]], + [types.default.SET_MACROS_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.default.SET_MACROS_UI_FLAG, { isCreating: true }], + [types.default.SET_MACROS_UI_FLAG, { isCreating: false }], + ]); + }); + }); + + describe('#execute', () => { + const macroId = 12; + const conversationIds = [1]; + it('sends correct actions if API is success', async () => { + axios.post.mockResolvedValue({ data: null }); + await actions.execute( + { commit }, + { + macroId, + conversationIds, + } + ); + expect(commit.mock.calls).toEqual([ + [types.default.SET_MACROS_UI_FLAG, { isExecuting: true }], + [types.default.SET_MACROS_UI_FLAG, { isExecuting: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.post.mockRejectedValue({ message: 'Incorrect header' }); + await expect( + actions.execute( + { commit }, + { + macroId, + conversationIds, + } + ) + ).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([ + [types.default.SET_MACROS_UI_FLAG, { isExecuting: true }], + [types.default.SET_MACROS_UI_FLAG, { isExecuting: false }], + ]); + }); + }); + + describe('#update', () => { + it('sends correct actions if API is success', async () => { + axios.patch.mockResolvedValue({ + data: { payload: macrosList[0] }, + }); + await actions.update({ commit }, macrosList[0]); + expect(commit.mock.calls).toEqual([ + [types.default.SET_MACROS_UI_FLAG, { isUpdating: true }], + [types.default.EDIT_MACRO, macrosList[0]], + [types.default.SET_MACROS_UI_FLAG, { isUpdating: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.patch.mockRejectedValue({ message: 'Incorrect header' }); + await expect(actions.update({ commit }, macrosList[0])).rejects.toThrow( + Error + ); + expect(commit.mock.calls).toEqual([ + [types.default.SET_MACROS_UI_FLAG, { isUpdating: true }], + [types.default.SET_MACROS_UI_FLAG, { isUpdating: false }], + ]); + }); + }); + + describe('#delete', () => { + it('sends correct actions if API is success', async () => { + axios.delete.mockResolvedValue({ data: macrosList[0] }); + await actions.delete({ commit }, macrosList[0].id); + expect(commit.mock.calls).toEqual([ + [types.default.SET_MACROS_UI_FLAG, { isDeleting: true }], + [types.default.DELETE_MACRO, macrosList[0].id], + [types.default.SET_MACROS_UI_FLAG, { isDeleting: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.delete.mockRejectedValue({ message: 'Incorrect header' }); + await expect( + actions.delete({ commit }, macrosList[0].id) + ).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([ + [types.default.SET_MACROS_UI_FLAG, { isDeleting: true }], + [types.default.SET_MACROS_UI_FLAG, { isDeleting: false }], + ]); + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/macros/fixtures.js b/app/javascript/dashboard/store/modules/specs/macros/fixtures.js new file mode 100644 index 000000000..b75bd2836 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/macros/fixtures.js @@ -0,0 +1,135 @@ +export default [ + { + id: 22, + name: 'Assign billing label and sales team and message user', + visibility: 'global', + created_by: { + id: 1, + account_id: 1, + availability_status: 'online', + auto_offline: true, + confirmed: true, + email: 'john@acme.inc', + available_name: 'Fayaz Ahmed', + name: 'Fayaz Ahmed', + role: 'administrator', + thumbnail: + 'http://localhost:3000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBUUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--16c85844c93f9c139deb782137b49c87c9bc871c/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2QzNKbGMybDZaVWtpRERJMU1IZ3lOVEFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--e0e35266e8ed66e90c51be02408be8a022aca545/memoji.png', + }, + updated_by: { + id: 1, + account_id: 1, + availability_status: 'online', + auto_offline: true, + confirmed: true, + email: 'john@acme.inc', + available_name: 'Fayaz Ahmed', + name: 'Fayaz Ahmed', + role: 'administrator', + thumbnail: + 'http://localhost:3000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBUUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--16c85844c93f9c139deb782137b49c87c9bc871c/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2QzNKbGMybDZaVWtpRERJMU1IZ3lOVEFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--e0e35266e8ed66e90c51be02408be8a022aca545/memoji.png', + }, + account_id: 1, + actions: [ + { + action_name: 'add_label', + action_params: ['sales', 'billing'], + }, + { + action_name: 'assign_team', + action_params: [1], + }, + { + action_name: 'send_message', + action_params: [ + "Thank you for reaching out, we're looking into this on priority and we'll get back to you asap.", + ], + }, + ], + }, + { + id: 23, + name: 'Assign label priority and send email to team', + visibility: 'global', + created_by: { + id: 1, + account_id: 1, + availability_status: 'online', + auto_offline: true, + confirmed: true, + email: 'john@acme.inc', + available_name: 'Fayaz Ahmed', + name: 'Fayaz Ahmed', + role: 'administrator', + thumbnail: + 'http://localhost:3000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBUUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--16c85844c93f9c139deb782137b49c87c9bc871c/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2QzNKbGMybDZaVWtpRERJMU1IZ3lOVEFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--e0e35266e8ed66e90c51be02408be8a022aca545/memoji.png', + }, + updated_by: { + id: 1, + account_id: 1, + availability_status: 'online', + auto_offline: true, + confirmed: true, + email: 'john@acme.inc', + available_name: 'Fayaz Ahmed', + name: 'Fayaz Ahmed', + role: 'administrator', + thumbnail: + 'http://localhost:3000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBUUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--16c85844c93f9c139deb782137b49c87c9bc871c/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2QzNKbGMybDZaVWtpRERJMU1IZ3lOVEFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--e0e35266e8ed66e90c51be02408be8a022aca545/memoji.png', + }, + account_id: 1, + actions: [ + { + action_name: 'add_label', + action_params: ['priority'], + }, + { + action_name: 'send_email_to_team', + action_params: [ + { + message: 'Hello team,\n\nThis looks important, please take look.', + team_ids: [1], + }, + ], + }, + ], + }, + { + id: 25, + name: 'Webhook', + visibility: 'global', + created_by: { + id: 1, + account_id: 1, + availability_status: 'online', + auto_offline: true, + confirmed: true, + email: 'john@acme.inc', + available_name: 'Fayaz Ahmed', + name: 'Fayaz Ahmed', + role: 'administrator', + thumbnail: + 'http://localhost:3000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBUUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--16c85844c93f9c139deb782137b49c87c9bc871c/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2QzNKbGMybDZaVWtpRERJMU1IZ3lOVEFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--e0e35266e8ed66e90c51be02408be8a022aca545/memoji.png', + }, + updated_by: { + id: 1, + account_id: 1, + availability_status: 'online', + auto_offline: true, + confirmed: true, + email: 'john@acme.inc', + available_name: 'Fayaz Ahmed', + name: 'Fayaz Ahmed', + role: 'administrator', + thumbnail: + 'http://localhost:3000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBUUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--16c85844c93f9c139deb782137b49c87c9bc871c/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2QzNKbGMybDZaVWtpRERJMU1IZ3lOVEFHT3daVSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--e0e35266e8ed66e90c51be02408be8a022aca545/memoji.png', + }, + account_id: 1, + actions: [ + { + action_name: 'send_webhook_event', + action_params: ['https://google.com'], + }, + ], + }, +]; diff --git a/app/javascript/dashboard/store/modules/specs/macros/getters.spec.js b/app/javascript/dashboard/store/modules/specs/macros/getters.spec.js new file mode 100644 index 000000000..d855f66ff --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/macros/getters.spec.js @@ -0,0 +1,32 @@ +import { getters } from '../../macros'; +import macros from './fixtures'; +describe('#getters', () => { + it('getMacros', () => { + const state = { records: macros }; + expect(getters.getMacros(state)).toEqual(macros); + }); + + it('getMacro', () => { + const state = { records: macros }; + expect(getters.getMacro(state)(22)).toEqual(macros[0]); + }); + + it('getUIFlags', () => { + const state = { + uiFlags: { + isFetching: true, + isCreating: false, + isUpdating: false, + isDeleting: false, + isExecuting: false, + }, + }; + expect(getters.getUIFlags(state)).toEqual({ + isFetching: true, + isCreating: false, + isUpdating: false, + isDeleting: false, + isExecuting: false, + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/macros/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/macros/mutations.spec.js new file mode 100644 index 000000000..436738638 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/macros/mutations.spec.js @@ -0,0 +1,38 @@ +import types from '../../../mutation-types'; +import { mutations } from '../../macros'; +import macros from './fixtures'; +describe('#mutations', () => { + describe('#SET_MACROS', () => { + it('set macrtos records', () => { + const state = { records: [] }; + mutations[types.SET_MACROS](state, macros); + expect(state.records).toEqual(macros); + }); + }); + + describe('#ADD_MACRO', () => { + it('push newly created macro to the store', () => { + const state = { records: [macros[0]] }; + mutations[types.ADD_MACRO](state, macros[1]); + expect(state.records).toEqual([macros[0], macros[1]]); + }); + }); + + describe('#EDIT_MACRO', () => { + it('update macro record', () => { + const state = { records: [macros[0]] }; + mutations[types.EDIT_MACRO](state, macros[0]); + expect(state.records[0].name).toEqual( + 'Assign billing label and sales team and message user' + ); + }); + }); + + describe('#DELETE_MACRO', () => { + it('delete macro record', () => { + const state = { records: [macros[0]] }; + mutations[types.DELETE_MACRO](state, 22); + expect(state.records).toEqual([]); + }); + }); +}); diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js index bf971931d..bcb6ad9a5 100755 --- a/app/javascript/dashboard/store/mutation-types.js +++ b/app/javascript/dashboard/store/mutation-types.js @@ -252,4 +252,11 @@ export default { 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', + ADD_MACRO: 'ADD_MACRO', + EDIT_MACRO: 'EDIT_MACRO', + DELETE_MACRO: 'DELETE_MACRO', };