-
- {{ $t('WHATSAPP_TEMPLATES.PICKER.LABELS.TEMPLATE_BODY') }}
+
+
+
+ {{ t('WHATSAPP_TEMPLATES.PICKER.HEADER') || 'HEADER' }}
-
{{ getTemplatebody(template) }}
+
+ {{ getTemplateHeader(template).text }}
+
+
+ {{
+ t('WHATSAPP_TEMPLATES.PICKER.MEDIA_CONTENT', {
+ format: getTemplateHeader(template).format,
+ }) ||
+ `${getTemplateHeader(template).format} ${t('WHATSAPP_TEMPLATES.PICKER.MEDIA_CONTENT_FALLBACK')}`
+ }}
+
-
-
- {{ $t('WHATSAPP_TEMPLATES.PICKER.LABELS.CATEGORY') }}
+
+
+
+
+ {{ t('WHATSAPP_TEMPLATES.PICKER.BODY') || 'BODY' }}
-
{{ template.category }}
+
{{ getTemplateBody(template) }}
+
+
+
+
+
+ {{ t('WHATSAPP_TEMPLATES.PICKER.FOOTER') || 'FOOTER' }}
+
+
+ {{ getTemplateFooter(template).text }}
+
+
+
+
+
+
+ {{ t('WHATSAPP_TEMPLATES.PICKER.BUTTONS') || 'BUTTONS' }}
+
+
+
+ {{ button.text }}
+
+
+
+
+
+
+ {{ t('WHATSAPP_TEMPLATES.PICKER.CATEGORY') || 'CATEGORY' }}
+
+
{{ template.category }}
@@ -128,13 +191,13 @@ export default {
- {{ $t('WHATSAPP_TEMPLATES.PICKER.NO_TEMPLATES_FOUND') }}
+ {{ t('WHATSAPP_TEMPLATES.PICKER.NO_TEMPLATES_FOUND') }}
{{ query }}
- {{ $t('WHATSAPP_TEMPLATES.PICKER.NO_TEMPLATES_AVAILABLE') }}
+ {{ t('WHATSAPP_TEMPLATES.PICKER.NO_TEMPLATES_AVAILABLE') }}
diff --git a/app/javascript/dashboard/composables/spec/useAutomation.spec.js b/app/javascript/dashboard/composables/spec/useAutomation.spec.js
index 46c0bde50..a66f0c7b4 100644
--- a/app/javascript/dashboard/composables/spec/useAutomation.spec.js
+++ b/app/javascript/dashboard/composables/spec/useAutomation.spec.js
@@ -196,6 +196,7 @@ describe('useAutomation', () => {
automationTypes.conversation_created = { conditions: [] };
automationTypes.conversation_updated = { conditions: [] };
automationTypes.conversation_opened = { conditions: [] };
+ automationTypes.conversation_resolved = { conditions: [] };
automationHelper.generateCustomAttributeTypes.mockReturnValue([]);
automationHelper.generateCustomAttributes.mockReturnValue([]);
diff --git a/app/javascript/dashboard/constants/automation.js b/app/javascript/dashboard/constants/automation.js
index 399dd6153..a903075b8 100644
--- a/app/javascript/dashboard/constants/automation.js
+++ b/app/javascript/dashboard/constants/automation.js
@@ -8,7 +8,7 @@ export const DEFAULT_MESSAGE_CREATED_CONDITION = [
},
];
-export const DEFAULT_CONVERSATION_OPENED_CONDITION = [
+export const DEFAULT_CONVERSATION_CONDITION = [
{
attribute_key: 'browser_language',
filter_operator: 'equal_to',
diff --git a/app/javascript/dashboard/helper/automationHelper.js b/app/javascript/dashboard/helper/automationHelper.js
index c9852814a..3723fd4d5 100644
--- a/app/javascript/dashboard/helper/automationHelper.js
+++ b/app/javascript/dashboard/helper/automationHelper.js
@@ -5,7 +5,7 @@ import {
} from 'dashboard/routes/dashboard/settings/automation/operators';
import {
DEFAULT_MESSAGE_CREATED_CONDITION,
- DEFAULT_CONVERSATION_OPENED_CONDITION,
+ DEFAULT_CONVERSATION_CONDITION,
DEFAULT_OTHER_CONDITION,
DEFAULT_ACTIONS,
} from 'dashboard/constants/automation';
@@ -169,8 +169,11 @@ export const getDefaultConditions = eventName => {
if (eventName === 'message_created') {
return DEFAULT_MESSAGE_CREATED_CONDITION;
}
- if (eventName === 'conversation_opened') {
- return DEFAULT_CONVERSATION_OPENED_CONDITION;
+ if (
+ eventName === 'conversation_opened' ||
+ eventName === 'conversation_resolved'
+ ) {
+ return DEFAULT_CONVERSATION_CONDITION;
}
return DEFAULT_OTHER_CONDITION;
};
diff --git a/app/javascript/dashboard/helper/specs/templateHelper.spec.js b/app/javascript/dashboard/helper/specs/templateHelper.spec.js
new file mode 100644
index 000000000..6e0661152
--- /dev/null
+++ b/app/javascript/dashboard/helper/specs/templateHelper.spec.js
@@ -0,0 +1,368 @@
+import {
+ replaceTemplateVariables,
+ buildTemplateParameters,
+ processVariable,
+ allKeysRequired,
+} from '../templateHelper';
+import { templates } from '../../store/modules/specs/inboxes/templateFixtures';
+
+describe('templateHelper', () => {
+ const technicianTemplate = templates.find(t => t.name === 'technician_visit');
+
+ describe('processVariable', () => {
+ it('should remove curly braces from variables', () => {
+ expect(processVariable('{{name}}')).toBe('name');
+ expect(processVariable('{{1}}')).toBe('1');
+ expect(processVariable('{{customer_id}}')).toBe('customer_id');
+ });
+ });
+
+ describe('allKeysRequired', () => {
+ it('should return true when all keys have values', () => {
+ const obj = { name: 'John', age: '30' };
+ expect(allKeysRequired(obj)).toBe(true);
+ });
+
+ it('should return false when some keys are empty', () => {
+ const obj = { name: 'John', age: '' };
+ expect(allKeysRequired(obj)).toBe(false);
+ });
+
+ it('should return true for empty object', () => {
+ expect(allKeysRequired({})).toBe(true);
+ });
+ });
+
+ describe('replaceTemplateVariables', () => {
+ const templateText =
+ "Hi {{1}}, we're scheduling a technician visit to {{2}} on {{3}} between {{4}} and {{5}}. Please confirm if this time slot works for you.";
+
+ it('should replace all variables with provided values', () => {
+ const processedParams = {
+ body: {
+ 1: 'John',
+ 2: '123 Main St',
+ 3: '2025-01-15',
+ 4: '10:00 AM',
+ 5: '2:00 PM',
+ },
+ };
+
+ const result = replaceTemplateVariables(templateText, processedParams);
+ expect(result).toBe(
+ "Hi John, we're scheduling a technician visit to 123 Main St on 2025-01-15 between 10:00 AM and 2:00 PM. Please confirm if this time slot works for you."
+ );
+ });
+
+ it('should keep original variable format when no replacement value provided', () => {
+ const processedParams = {
+ body: {
+ 1: 'John',
+ 3: '2025-01-15',
+ },
+ };
+
+ const result = replaceTemplateVariables(templateText, processedParams);
+ expect(result).toContain('John');
+ expect(result).toContain('2025-01-15');
+ expect(result).toContain('{{2}}');
+ expect(result).toContain('{{4}}');
+ expect(result).toContain('{{5}}');
+ });
+
+ it('should handle empty processedParams', () => {
+ const result = replaceTemplateVariables(templateText, {});
+ expect(result).toBe(templateText);
+ });
+ });
+
+ describe('buildTemplateParameters', () => {
+ it('should build parameters for template with body variables', () => {
+ const result = buildTemplateParameters(technicianTemplate, false);
+
+ expect(result.body).toEqual({
+ 1: '',
+ 2: '',
+ 3: '',
+ 4: '',
+ 5: '',
+ });
+ });
+
+ it('should include header parameters when hasMediaHeader is true', () => {
+ const imageTemplate = templates.find(
+ t => t.name === 'order_confirmation'
+ );
+ const result = buildTemplateParameters(imageTemplate, true);
+
+ expect(result.header).toEqual({
+ media_url: '',
+ media_type: 'image',
+ });
+ });
+
+ it('should not include header parameters when hasMediaHeader is false', () => {
+ const result = buildTemplateParameters(technicianTemplate, false);
+ expect(result.header).toBeUndefined();
+ });
+
+ it('should handle template with no body component', () => {
+ const templateWithoutBody = {
+ components: [{ type: 'HEADER', format: 'TEXT' }],
+ };
+
+ const result = buildTemplateParameters(templateWithoutBody, false);
+ expect(result).toEqual({});
+ });
+
+ it('should handle template with no variables', () => {
+ const templateWithoutVars = templates.find(
+ t => t.name === 'no_variable_template'
+ );
+ const result = buildTemplateParameters(templateWithoutVars, false);
+
+ expect(result.body).toBeUndefined();
+ });
+
+ it('should handle URL buttons with variables for non-authentication templates', () => {
+ const templateWithUrlButton = {
+ category: 'MARKETING',
+ components: [
+ {
+ type: 'BODY',
+ text: 'Check out our website at {{site_url}}',
+ },
+ {
+ type: 'BUTTONS',
+ buttons: [
+ {
+ type: 'URL',
+ url: 'https://example.com/{{campaign_id}}',
+ text: 'Visit Site',
+ },
+ ],
+ },
+ ],
+ };
+
+ const result = buildTemplateParameters(templateWithUrlButton, false);
+ expect(result.buttons).toEqual([
+ {
+ type: 'url',
+ parameter: '',
+ url: 'https://example.com/{{campaign_id}}',
+ variables: ['campaign_id'],
+ },
+ ]);
+ });
+
+ it('should handle templates with no variables', () => {
+ const emptyTemplate = templates.find(
+ t => t.name === 'no_variable_template'
+ );
+ const result = buildTemplateParameters(emptyTemplate, false);
+ expect(result).toEqual({});
+ });
+
+ it('should build parameters for templates with multiple component types', () => {
+ const complexTemplate = {
+ components: [
+ { type: 'HEADER', format: 'IMAGE' },
+ { type: 'BODY', text: 'Hi {{1}}, your order {{2}} is ready!' },
+ { type: 'FOOTER', text: 'Thank you for your business' },
+ {
+ type: 'BUTTONS',
+ buttons: [{ type: 'URL', url: 'https://example.com/{{3}}' }],
+ },
+ ],
+ };
+
+ const result = buildTemplateParameters(complexTemplate, true);
+
+ expect(result.header).toEqual({
+ media_url: '',
+ media_type: 'image',
+ });
+ expect(result.body).toEqual({ 1: '', 2: '' });
+ expect(result.buttons).toEqual([
+ {
+ type: 'url',
+ parameter: '',
+ url: 'https://example.com/{{3}}',
+ variables: ['3'],
+ },
+ ]);
+ });
+
+ it('should handle copy code buttons correctly', () => {
+ const copyCodeTemplate = templates.find(
+ t => t.name === 'discount_coupon'
+ );
+ const result = buildTemplateParameters(copyCodeTemplate, false);
+
+ expect(result.body).toBeDefined();
+ expect(result.buttons).toEqual([
+ {
+ type: 'copy_code',
+ parameter: '',
+ },
+ ]);
+ });
+
+ it('should handle templates with document headers', () => {
+ const documentTemplate = templates.find(
+ t => t.name === 'purchase_receipt'
+ );
+ const result = buildTemplateParameters(documentTemplate, true);
+
+ expect(result.header).toEqual({
+ media_url: '',
+ media_type: 'document',
+ });
+ expect(result.body).toEqual({
+ 1: '',
+ 2: '',
+ 3: '',
+ });
+ });
+
+ it('should handle video header templates', () => {
+ const videoTemplate = templates.find(t => t.name === 'training_video');
+ const result = buildTemplateParameters(videoTemplate, true);
+
+ expect(result.header).toEqual({
+ media_url: '',
+ media_type: 'video',
+ });
+ expect(result.body).toEqual({
+ name: '',
+ date: '',
+ });
+ });
+ });
+
+ describe('enhanced format validation', () => {
+ it('should validate enhanced format structure', () => {
+ const processedParams = {
+ body: { 1: 'John', 2: 'Order123' },
+ header: {
+ media_url: 'https://example.com/image.jpg',
+ media_type: 'image',
+ },
+ buttons: [{ type: 'copy_code', parameter: 'SAVE20' }],
+ };
+
+ // Test that structure is properly formed
+ expect(processedParams.body).toBeDefined();
+ expect(typeof processedParams.body).toBe('object');
+ expect(processedParams.header).toBeDefined();
+ expect(Array.isArray(processedParams.buttons)).toBe(true);
+ });
+
+ it('should handle empty component sections', () => {
+ const processedParams = {
+ body: {},
+ header: {},
+ buttons: [],
+ };
+
+ expect(allKeysRequired(processedParams.body)).toBe(true);
+ expect(allKeysRequired(processedParams.header)).toBe(true);
+ expect(processedParams.buttons.length).toBe(0);
+ });
+
+ it('should validate parameter completeness', () => {
+ const incompleteParams = {
+ body: { 1: 'John', 2: '' },
+ };
+
+ expect(allKeysRequired(incompleteParams.body)).toBe(false);
+ });
+
+ it('should handle edge cases in processVariable', () => {
+ expect(processVariable('{{')).toBe('');
+ expect(processVariable('}}')).toBe('');
+ expect(processVariable('')).toBe('');
+ expect(processVariable('{{nested{{variable}}}}')).toBe('nestedvariable');
+ });
+
+ it('should handle special characters in template variables', () => {
+ /* eslint-disable no-template-curly-in-string */
+ const templateText =
+ 'Welcome {{user_name}}, your order #{{order_id}} costs ${{amount}}';
+ /* eslint-enable no-template-curly-in-string */
+ const processedParams = {
+ body: {
+ user_name: 'John & Jane',
+ order_id: '12345',
+ amount: '99.99',
+ },
+ };
+
+ const result = replaceTemplateVariables(templateText, processedParams);
+ expect(result).toBe(
+ 'Welcome John & Jane, your order #12345 costs $99.99'
+ );
+ });
+
+ it('should handle templates with mixed parameter types', () => {
+ const mixedTemplate = {
+ components: [
+ { type: 'HEADER', format: 'VIDEO' },
+ { type: 'BODY', text: 'Order {{order_id}} status: {{status}}' },
+ { type: 'FOOTER', text: 'Thank you' },
+ {
+ type: 'BUTTONS',
+ buttons: [
+ { type: 'URL', url: 'https://track.com/{{order_id}}' },
+ { type: 'COPY_CODE' },
+ { type: 'PHONE_NUMBER', phone_number: '+1234567890' },
+ ],
+ },
+ ],
+ };
+
+ const result = buildTemplateParameters(mixedTemplate, true);
+
+ expect(result.header).toEqual({
+ media_url: '',
+ media_type: 'video',
+ });
+ expect(result.body).toEqual({
+ order_id: '',
+ status: '',
+ });
+ expect(result.buttons).toHaveLength(2); // URL and COPY_CODE (PHONE_NUMBER doesn't need parameters)
+ expect(result.buttons[0].type).toBe('url');
+ expect(result.buttons[1].type).toBe('copy_code');
+ });
+
+ it('should handle templates with no processable components', () => {
+ const emptyTemplate = {
+ components: [
+ { type: 'HEADER', format: 'TEXT', text: 'Static Header' },
+ { type: 'BODY', text: 'Static body with no variables' },
+ { type: 'FOOTER', text: 'Static footer' },
+ ],
+ };
+
+ const result = buildTemplateParameters(emptyTemplate, false);
+ expect(result).toEqual({});
+ });
+
+ it('should validate that replaceTemplateVariables preserves unreplaced variables', () => {
+ const templateText = 'Hi {{name}}, order {{order_id}} is {{status}}';
+ const partialParams = {
+ body: {
+ name: 'John',
+ // order_id missing
+ status: 'ready',
+ },
+ };
+
+ const result = replaceTemplateVariables(templateText, partialParams);
+ expect(result).toBe('Hi John, order {{order_id}} is ready');
+ expect(result).toContain('{{order_id}}'); // Unreplaced variable preserved
+ });
+ });
+});
diff --git a/app/javascript/dashboard/helper/templateHelper.js b/app/javascript/dashboard/helper/templateHelper.js
new file mode 100644
index 000000000..5c9bbff05
--- /dev/null
+++ b/app/javascript/dashboard/helper/templateHelper.js
@@ -0,0 +1,91 @@
+// Constants
+export const DEFAULT_LANGUAGE = 'en';
+export const DEFAULT_CATEGORY = 'UTILITY';
+export const COMPONENT_TYPES = {
+ HEADER: 'HEADER',
+ BODY: 'BODY',
+ BUTTONS: 'BUTTONS',
+};
+export const MEDIA_FORMATS = ['IMAGE', 'VIDEO', 'DOCUMENT'];
+
+export const findComponentByType = (template, type) =>
+ template.components?.find(component => component.type === type);
+
+export const processVariable = str => {
+ return str.replace(/{{|}}/g, '');
+};
+
+export const allKeysRequired = value => {
+ const keys = Object.keys(value);
+ return keys.every(key => value[key]);
+};
+
+export const replaceTemplateVariables = (templateText, processedParams) => {
+ return templateText.replace(/{{([^}]+)}}/g, (match, variable) => {
+ const variableKey = processVariable(variable);
+ return processedParams.body?.[variableKey] || `{{${variable}}}`;
+ });
+};
+
+export const buildTemplateParameters = (template, hasMediaHeaderValue) => {
+ const allVariables = {};
+
+ const bodyComponent = findComponentByType(template, COMPONENT_TYPES.BODY);
+ const headerComponent = findComponentByType(template, COMPONENT_TYPES.HEADER);
+
+ if (!bodyComponent) return allVariables;
+
+ const templateString = bodyComponent.text;
+
+ // Process body variables
+ const matchedVariables = templateString.match(/{{([^}]+)}}/g);
+ if (matchedVariables) {
+ allVariables.body = {};
+ matchedVariables.forEach(variable => {
+ const key = processVariable(variable);
+ allVariables.body[key] = '';
+ });
+ }
+
+ if (hasMediaHeaderValue) {
+ if (!allVariables.header) allVariables.header = {};
+ allVariables.header.media_url = '';
+ allVariables.header.media_type = headerComponent.format.toLowerCase();
+ }
+
+ // Process button variables
+ const buttonComponents = template.components.filter(
+ component => component.type === COMPONENT_TYPES.BUTTONS
+ );
+
+ buttonComponents.forEach(buttonComponent => {
+ if (buttonComponent.buttons) {
+ buttonComponent.buttons.forEach((button, index) => {
+ // Handle URL buttons with variables
+ if (button.type === 'URL' && button.url && button.url.includes('{{')) {
+ const buttonVars = button.url.match(/{{([^}]+)}}/g) || [];
+ if (buttonVars.length > 0) {
+ if (!allVariables.buttons) allVariables.buttons = [];
+ allVariables.buttons[index] = {
+ type: 'url',
+ parameter: '',
+ url: button.url,
+ variables: buttonVars.map(v => processVariable(v)),
+ };
+ }
+ }
+
+ // Handle copy code buttons
+ if (button.type === 'COPY_CODE') {
+ if (!allVariables.buttons) allVariables.buttons = [];
+ allVariables.buttons[index] = {
+ type: 'copy_code',
+ parameter: '',
+ };
+ }
+ });
+ }
+ });
+
+ return allVariables;
+};
diff --git a/app/javascript/dashboard/i18n/index.js b/app/javascript/dashboard/i18n/index.js
index d0e85b43a..17bfab34d 100644
--- a/app/javascript/dashboard/i18n/index.js
+++ b/app/javascript/dashboard/i18n/index.js
@@ -1,4 +1,5 @@
import ar from './locale/ar';
+import bg from './locale/bg';
import ca from './locale/ca';
import cs from './locale/cs';
import da from './locale/da';
@@ -40,6 +41,7 @@ import lt from './locale/lt';
export default {
ar,
+ bg,
ca,
cs,
da,
diff --git a/app/javascript/dashboard/i18n/locale/am/automation.json b/app/javascript/dashboard/i18n/locale/am/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/am/automation.json
+++ b/app/javascript/dashboard/i18n/locale/am/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/am/integrations.json b/app/javascript/dashboard/i18n/locale/am/integrations.json
index 1614931a0..be9281284 100644
--- a/app/javascript/dashboard/i18n/locale/am/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/am/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/am/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/am/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/am/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/am/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ar/automation.json b/app/javascript/dashboard/i18n/locale/ar/automation.json
index be7107f4c..788652a04 100644
--- a/app/javascript/dashboard/i18n/locale/ar/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ar/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "تم إنشاء المحادثة",
"CONVERSATION_UPDATED": "تم تحديث المحادثة",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ar/integrations.json b/app/javascript/dashboard/i18n/locale/ar/integrations.json
index f00d3fe1d..bc17130c1 100644
--- a/app/javascript/dashboard/i18n/locale/ar/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ar/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "الخصائص",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ar/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ar/whatsappTemplates.json
index 110eeb8af..247af32c4 100644
--- a/app/javascript/dashboard/i18n/locale/ar/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ar/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "قوالب الواتساب",
- "SUBTITLE": "حدد القالب الذي تريد إرساله",
- "TEMPLATE_SELECTED_SUBTITLE": "معالجة {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "نماذج البحث",
- "NO_TEMPLATES_FOUND": "لم يتم العثور على قوالب",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "اللغة",
- "TEMPLATE_BODY": "نص القالب",
- "CATEGORY": "الفئة"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "المتغيرات",
- "VARIABLE_PLACEHOLDER": "أدخل قيمة {variable}",
- "GO_BACK_LABEL": "العودة للخلف",
- "SEND_MESSAGE_LABEL": "إرسال الرسالة",
- "FORM_ERROR_MESSAGE": "يرجى ملء جميع المتغيرات قبل الإرسال"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "قوالب الواتساب",
+ "SUBTITLE": "حدد القالب الذي تريد إرساله",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "نماذج البحث",
+ "NO_TEMPLATES_FOUND": "لم يتم العثور على قوالب",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "الفئة",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "اللغة",
+ "TEMPLATE_BODY": "نص القالب",
+ "CATEGORY": "الفئة"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "المتغيرات",
+ "LANGUAGE": "اللغة",
+ "CATEGORY": "الفئة",
+ "VARIABLE_PLACEHOLDER": "أدخل قيمة {variable}",
+ "GO_BACK_LABEL": "العودة للخلف",
+ "SEND_MESSAGE_LABEL": "إرسال الرسالة",
+ "FORM_ERROR_MESSAGE": "يرجى ملء جميع المتغيرات قبل الإرسال",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/az/automation.json b/app/javascript/dashboard/i18n/locale/az/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/az/automation.json
+++ b/app/javascript/dashboard/i18n/locale/az/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/az/integrations.json b/app/javascript/dashboard/i18n/locale/az/integrations.json
index 1614931a0..be9281284 100644
--- a/app/javascript/dashboard/i18n/locale/az/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/az/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/az/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/az/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/az/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/az/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/bg/automation.json b/app/javascript/dashboard/i18n/locale/bg/automation.json
index fe4d32743..006369305 100644
--- a/app/javascript/dashboard/i18n/locale/bg/automation.json
+++ b/app/javascript/dashboard/i18n/locale/bg/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/bg/integrations.json b/app/javascript/dashboard/i18n/locale/bg/integrations.json
index 6bcd3b584..1069559a6 100644
--- a/app/javascript/dashboard/i18n/locale/bg/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/bg/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/bg/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/bg/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/bg/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/bg/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ca/automation.json b/app/javascript/dashboard/i18n/locale/ca/automation.json
index e3570b417..d961c762a 100644
--- a/app/javascript/dashboard/i18n/locale/ca/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ca/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversa Creada",
"CONVERSATION_UPDATED": "Conversa Actualitzada",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ca/integrations.json b/app/javascript/dashboard/i18n/locale/ca/integrations.json
index 4607a2fe1..2668ce217 100644
--- a/app/javascript/dashboard/i18n/locale/ca/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ca/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Característiques",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ca/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ca/whatsappTemplates.json
index f078ec706..e0b132286 100644
--- a/app/javascript/dashboard/i18n/locale/ca/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ca/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Plantilles de Whatsapp",
- "SUBTITLE": "Selecciona la plantilla de whatsapp que vols enviar",
- "TEMPLATE_SELECTED_SUBTITLE": "Procés {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Cerca plantilles",
- "NO_TEMPLATES_FOUND": "No s'han trobat plantilles per a",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Idioma",
- "TEMPLATE_BODY": "Cos de la plantilla",
- "CATEGORY": "Categoria"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Introdueix el valor {variable}",
- "GO_BACK_LABEL": "Torna enrere",
- "SEND_MESSAGE_LABEL": "Envia missatge",
- "FORM_ERROR_MESSAGE": "Omple totes les variables abans d'enviar-les"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Plantilles de Whatsapp",
+ "SUBTITLE": "Selecciona la plantilla de whatsapp que vols enviar",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Cerca plantilles",
+ "NO_TEMPLATES_FOUND": "No s'han trobat plantilles per a",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Idioma",
+ "TEMPLATE_BODY": "Cos de la plantilla",
+ "CATEGORY": "Categoria"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Idioma",
+ "CATEGORY": "Categoria",
+ "VARIABLE_PLACEHOLDER": "Introdueix el valor {variable}",
+ "GO_BACK_LABEL": "Torna enrere",
+ "SEND_MESSAGE_LABEL": "Envia missatge",
+ "FORM_ERROR_MESSAGE": "Omple totes les variables abans d'enviar-les",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/cs/automation.json b/app/javascript/dashboard/i18n/locale/cs/automation.json
index d8dd12abd..6733fa929 100644
--- a/app/javascript/dashboard/i18n/locale/cs/automation.json
+++ b/app/javascript/dashboard/i18n/locale/cs/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Zpráva vytvořena",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Konverzace otevřena"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/cs/integrations.json b/app/javascript/dashboard/i18n/locale/cs/integrations.json
index 9b55bf386..26bc11c0c 100644
--- a/app/javascript/dashboard/i18n/locale/cs/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/cs/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funkce",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/cs/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/cs/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/cs/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/cs/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/da/automation.json b/app/javascript/dashboard/i18n/locale/da/automation.json
index 7910667a9..372422cfc 100644
--- a/app/javascript/dashboard/i18n/locale/da/automation.json
+++ b/app/javascript/dashboard/i18n/locale/da/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Samtale Oprettet",
"CONVERSATION_UPDATED": "Samtale Opdateret",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/da/integrations.json b/app/javascript/dashboard/i18n/locale/da/integrations.json
index 8710dc6d8..c4b498a3a 100644
--- a/app/javascript/dashboard/i18n/locale/da/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/da/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funktioner",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/da/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/da/whatsappTemplates.json
index 3acb3acb3..34699398c 100644
--- a/app/javascript/dashboard/i18n/locale/da/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/da/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Skabeloner",
- "SUBTITLE": "Vælg den whatsapp skabelon, du vil sende",
- "TEMPLATE_SELECTED_SUBTITLE": "Proces {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Søg Skabeloner",
- "NO_TEMPLATES_FOUND": "Ingen skabeloner fundet for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Sprog",
- "TEMPLATE_BODY": "Skabelon Krop",
- "CATEGORY": "Kategori"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variabler",
- "VARIABLE_PLACEHOLDER": "Indtast {variable} værdi",
- "GO_BACK_LABEL": "Gå Tilbage",
- "SEND_MESSAGE_LABEL": "Send Besked",
- "FORM_ERROR_MESSAGE": "Udfyld venligst alle variabler før afsendelse"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Skabeloner",
+ "SUBTITLE": "Vælg den whatsapp skabelon, du vil sende",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Søg Skabeloner",
+ "NO_TEMPLATES_FOUND": "Ingen skabeloner fundet for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategori",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Sprog",
+ "TEMPLATE_BODY": "Skabelon Krop",
+ "CATEGORY": "Kategori"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabler",
+ "LANGUAGE": "Sprog",
+ "CATEGORY": "Kategori",
+ "VARIABLE_PLACEHOLDER": "Indtast {variable} værdi",
+ "GO_BACK_LABEL": "Gå Tilbage",
+ "SEND_MESSAGE_LABEL": "Send Besked",
+ "FORM_ERROR_MESSAGE": "Udfyld venligst alle variabler før afsendelse",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/de/automation.json b/app/javascript/dashboard/i18n/locale/de/automation.json
index 20e343e0a..b6cef93e1 100644
--- a/app/javascript/dashboard/i18n/locale/de/automation.json
+++ b/app/javascript/dashboard/i18n/locale/de/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Konversation erstellt",
"CONVERSATION_UPDATED": "Konversation aktualisiert",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/de/integrations.json b/app/javascript/dashboard/i18n/locale/de/integrations.json
index fb78159f9..060389c52 100644
--- a/app/javascript/dashboard/i18n/locale/de/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/de/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funktionen",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/de/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/de/whatsappTemplates.json
index b5f1885ae..87f5c6a2c 100644
--- a/app/javascript/dashboard/i18n/locale/de/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/de/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "WhatsApp-Vorlagen",
- "SUBTITLE": "Wählen Sie die WhatsApp-Vorlage aus, die Sie senden möchten",
- "TEMPLATE_SELECTED_SUBTITLE": "Verarbeite {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Vorlagen suchen",
- "NO_TEMPLATES_FOUND": "Keine Vorlagen gefunden für",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Sprache",
- "TEMPLATE_BODY": "Vorlagenbody",
- "CATEGORY": "Kategorie"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variablen",
- "VARIABLE_PLACEHOLDER": "Geben Sie den Wert {variable} ein",
- "GO_BACK_LABEL": "Zurück",
- "SEND_MESSAGE_LABEL": "Nachricht senden",
- "FORM_ERROR_MESSAGE": "Bitte füllen Sie vor dem Absenden alle Variablen aus"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "WhatsApp-Vorlagen",
+ "SUBTITLE": "Wählen Sie die WhatsApp-Vorlage aus, die Sie senden möchten",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Vorlagen suchen",
+ "NO_TEMPLATES_FOUND": "Keine Vorlagen gefunden für",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorie",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Sprache",
+ "TEMPLATE_BODY": "Vorlagenbody",
+ "CATEGORY": "Kategorie"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variablen",
+ "LANGUAGE": "Sprache",
+ "CATEGORY": "Kategorie",
+ "VARIABLE_PLACEHOLDER": "Geben Sie den Wert {variable} ein",
+ "GO_BACK_LABEL": "Zurück",
+ "SEND_MESSAGE_LABEL": "Nachricht senden",
+ "FORM_ERROR_MESSAGE": "Bitte füllen Sie vor dem Absenden alle Variablen aus",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/el/automation.json b/app/javascript/dashboard/i18n/locale/el/automation.json
index d474d9914..27afa3306 100644
--- a/app/javascript/dashboard/i18n/locale/el/automation.json
+++ b/app/javascript/dashboard/i18n/locale/el/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Δημιουργήθηκε Συνομιλία",
"CONVERSATION_UPDATED": "Η Συνομιλία Ενημερώθηκε",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/el/integrations.json b/app/javascript/dashboard/i18n/locale/el/integrations.json
index 599d1c7e2..488107d41 100644
--- a/app/javascript/dashboard/i18n/locale/el/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/el/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Χαρακτηριστικά",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/el/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/el/whatsappTemplates.json
index d78b9cb12..42b710727 100644
--- a/app/javascript/dashboard/i18n/locale/el/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/el/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Πρότυπα Whatsapp",
- "SUBTITLE": "Επιλέξτε το πρότυπο Whatsapp που θέλετε να στείλετε",
- "TEMPLATE_SELECTED_SUBTITLE": "Επεξεργασία {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Αναζήτηση Προτύπων",
- "NO_TEMPLATES_FOUND": "Δεν βρέθηκαν πρότυπα για",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Γλώσσα",
- "TEMPLATE_BODY": "Σώμα Προτύπου",
- "CATEGORY": "Κατηγορία"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Μεταβλητές",
- "VARIABLE_PLACEHOLDER": "Εισάγετε τιμή για {variable}",
- "GO_BACK_LABEL": "Πίσω",
- "SEND_MESSAGE_LABEL": "Αποστολή μηνύματος",
- "FORM_ERROR_MESSAGE": "Παρακαλώ συμπληρώστε όλες τις μεταβλητές πριν την αποστολή"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Πρότυπα Whatsapp",
+ "SUBTITLE": "Επιλέξτε το πρότυπο Whatsapp που θέλετε να στείλετε",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Αναζήτηση Προτύπων",
+ "NO_TEMPLATES_FOUND": "Δεν βρέθηκαν πρότυπα για",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Κατηγορία",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Γλώσσα",
+ "TEMPLATE_BODY": "Σώμα Προτύπου",
+ "CATEGORY": "Κατηγορία"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Μεταβλητές",
+ "LANGUAGE": "Γλώσσα",
+ "CATEGORY": "Κατηγορία",
+ "VARIABLE_PLACEHOLDER": "Εισάγετε τιμή για {variable}",
+ "GO_BACK_LABEL": "Πίσω",
+ "SEND_MESSAGE_LABEL": "Αποστολή μηνύματος",
+ "FORM_ERROR_MESSAGE": "Παρακαλώ συμπληρώστε όλες τις μεταβλητές πριν την αποστολή",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/en/automation.json b/app/javascript/dashboard/i18n/locale/en/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/en/automation.json
+++ b/app/javascript/dashboard/i18n/locale/en/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json
index fde198c92..d547538db 100644
--- a/app/javascript/dashboard/i18n/locale/en/settings.json
+++ b/app/javascript/dashboard/i18n/locale/en/settings.json
@@ -226,6 +226,7 @@
"APPEARANCE": "Change appearance",
"SUPER_ADMIN_CONSOLE": "SuperAdmin console",
"DOCS": "Read documentation",
+ "CHANGELOG": "Changelog",
"LOGOUT": "Log out"
},
"APP_GLOBAL": {
diff --git a/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/es/automation.json b/app/javascript/dashboard/i18n/locale/es/automation.json
index a1f68a375..63b529aee 100644
--- a/app/javascript/dashboard/i18n/locale/es/automation.json
+++ b/app/javascript/dashboard/i18n/locale/es/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversación creada",
"CONVERSATION_UPDATED": "Conversación actualizada",
"MESSAGE_CREATED": "Mensaje creado",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversación abierta"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/es/integrations.json b/app/javascript/dashboard/i18n/locale/es/integrations.json
index fbf4754b8..ee297c2f8 100644
--- a/app/javascript/dashboard/i18n/locale/es/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/es/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Características",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/es/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/es/whatsappTemplates.json
index 49a89e381..06ceacb68 100644
--- a/app/javascript/dashboard/i18n/locale/es/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/es/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Plantillas de Whatsapp",
- "SUBTITLE": "Seleccione la plantilla de Whatsapp que desea enviar",
- "TEMPLATE_SELECTED_SUBTITLE": "Procesar {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Buscar plantillas",
- "NO_TEMPLATES_FOUND": "No se encontraron plantillas para",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Idioma",
- "TEMPLATE_BODY": "Cuerpo de plantilla",
- "CATEGORY": "Categoría"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Introduzca el valor de {variable}",
- "GO_BACK_LABEL": "Volver",
- "SEND_MESSAGE_LABEL": "Enviar mensaje",
- "FORM_ERROR_MESSAGE": "Por favor, rellene todas las variables antes de enviar"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Plantillas de Whatsapp",
+ "SUBTITLE": "Seleccione la plantilla de Whatsapp que desea enviar",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Buscar plantillas",
+ "NO_TEMPLATES_FOUND": "No se encontraron plantillas para",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoría",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Idioma",
+ "TEMPLATE_BODY": "Cuerpo de plantilla",
+ "CATEGORY": "Categoría"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Idioma",
+ "CATEGORY": "Categoría",
+ "VARIABLE_PLACEHOLDER": "Introduzca el valor de {variable}",
+ "GO_BACK_LABEL": "Volver",
+ "SEND_MESSAGE_LABEL": "Enviar mensaje",
+ "FORM_ERROR_MESSAGE": "Por favor, rellene todas las variables antes de enviar",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/fa/automation.json b/app/javascript/dashboard/i18n/locale/fa/automation.json
index 3e4ffd18e..3e02e4a69 100644
--- a/app/javascript/dashboard/i18n/locale/fa/automation.json
+++ b/app/javascript/dashboard/i18n/locale/fa/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "گفتگو ایجاد شد",
"CONVERSATION_UPDATED": "گفتگو به روز شد",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/fa/integrations.json b/app/javascript/dashboard/i18n/locale/fa/integrations.json
index 15e4417d5..18c95d245 100644
--- a/app/javascript/dashboard/i18n/locale/fa/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/fa/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "امکانات",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/fa/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/fa/whatsappTemplates.json
index 3b470e99f..ed5d76d1c 100644
--- a/app/javascript/dashboard/i18n/locale/fa/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/fa/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "قالب های واتساپ",
- "SUBTITLE": "قالب واتساپ مورد نظر برای ارسال را انتخاب کنید",
- "TEMPLATE_SELECTED_SUBTITLE": "فرآیند {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "جستجوی الگوها",
- "NO_TEMPLATES_FOUND": "هیچ قالبی برای",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "زبان",
- "TEMPLATE_BODY": "بدنه الگو",
- "CATEGORY": "دستهبندی"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "متغیرها",
- "VARIABLE_PLACEHOLDER": "مقدار {variable} را وارد کنید",
- "GO_BACK_LABEL": "بازگشت",
- "SEND_MESSAGE_LABEL": "ارسال پیام",
- "FORM_ERROR_MESSAGE": "لطفا قبل از ارسال همه متغیرها را پر کنید"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "قالب های واتساپ",
+ "SUBTITLE": "قالب واتساپ مورد نظر برای ارسال را انتخاب کنید",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "جستجوی الگوها",
+ "NO_TEMPLATES_FOUND": "هیچ قالبی برای",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "دستهبندی",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "زبان",
+ "TEMPLATE_BODY": "بدنه الگو",
+ "CATEGORY": "دستهبندی"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "متغیرها",
+ "LANGUAGE": "زبان",
+ "CATEGORY": "دستهبندی",
+ "VARIABLE_PLACEHOLDER": "مقدار {variable} را وارد کنید",
+ "GO_BACK_LABEL": "بازگشت",
+ "SEND_MESSAGE_LABEL": "ارسال پیام",
+ "FORM_ERROR_MESSAGE": "لطفا قبل از ارسال همه متغیرها را پر کنید",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/fi/automation.json b/app/javascript/dashboard/i18n/locale/fi/automation.json
index ec63918e7..740761ca5 100644
--- a/app/javascript/dashboard/i18n/locale/fi/automation.json
+++ b/app/javascript/dashboard/i18n/locale/fi/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/fi/integrations.json b/app/javascript/dashboard/i18n/locale/fi/integrations.json
index 1374fba42..378242517 100644
--- a/app/javascript/dashboard/i18n/locale/fi/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/fi/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Ominaisuudet",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/fi/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/fi/whatsappTemplates.json
index ae8bee085..337134e31 100644
--- a/app/javascript/dashboard/i18n/locale/fi/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/fi/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "WhatsApp-pohjat",
- "SUBTITLE": "Valitse WhatsApp-pohja, jonka haluat lähettää",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Etsi Pohjia",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Muuttujat",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Mene Takaisin",
- "SEND_MESSAGE_LABEL": "Lähetä Viesti",
- "FORM_ERROR_MESSAGE": "Täytä kaikki muuttujat ennen lähettämistä"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "WhatsApp-pohjat",
+ "SUBTITLE": "Valitse WhatsApp-pohja, jonka haluat lähettää",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Etsi Pohjia",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Muuttujat",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Mene Takaisin",
+ "SEND_MESSAGE_LABEL": "Lähetä Viesti",
+ "FORM_ERROR_MESSAGE": "Täytä kaikki muuttujat ennen lähettämistä",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/fr/automation.json b/app/javascript/dashboard/i18n/locale/fr/automation.json
index 500cbbdcf..11a1ddfe6 100644
--- a/app/javascript/dashboard/i18n/locale/fr/automation.json
+++ b/app/javascript/dashboard/i18n/locale/fr/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation créée",
"CONVERSATION_UPDATED": "Conversation mise à jour",
"MESSAGE_CREATED": "Message créé",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation ouverte"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/fr/integrations.json b/app/javascript/dashboard/i18n/locale/fr/integrations.json
index 0ace6565e..9ad0fb8fd 100644
--- a/app/javascript/dashboard/i18n/locale/fr/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/fr/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Fonctionnalités",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/fr/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/fr/whatsappTemplates.json
index 0d4538240..71d0685cb 100644
--- a/app/javascript/dashboard/i18n/locale/fr/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/fr/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Modèles WhatsApp",
- "SUBTITLE": "Sélectionnez le modèle whatsapp que vous souhaitez envoyer",
- "TEMPLATE_SELECTED_SUBTITLE": "Traiter {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Rechercher des modèles",
- "NO_TEMPLATES_FOUND": "Aucun modèle trouvé pour",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Langue",
- "TEMPLATE_BODY": "Corps du modèle",
- "CATEGORY": "Catégorie"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Entrez la valeur de {variable}",
- "GO_BACK_LABEL": "Retour",
- "SEND_MESSAGE_LABEL": "Envoyer un message",
- "FORM_ERROR_MESSAGE": "Veuillez remplir toutes les variables avant d'envoyer"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Modèles WhatsApp",
+ "SUBTITLE": "Sélectionnez le modèle whatsapp que vous souhaitez envoyer",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Rechercher des modèles",
+ "NO_TEMPLATES_FOUND": "Aucun modèle trouvé pour",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Catégorie",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Langue",
+ "TEMPLATE_BODY": "Corps du modèle",
+ "CATEGORY": "Catégorie"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Langue",
+ "CATEGORY": "Catégorie",
+ "VARIABLE_PLACEHOLDER": "Entrez la valeur de {variable}",
+ "GO_BACK_LABEL": "Retour",
+ "SEND_MESSAGE_LABEL": "Envoyer un message",
+ "FORM_ERROR_MESSAGE": "Veuillez remplir toutes les variables avant d'envoyer",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/he/automation.json b/app/javascript/dashboard/i18n/locale/he/automation.json
index e1be1df5c..6166c90a3 100644
--- a/app/javascript/dashboard/i18n/locale/he/automation.json
+++ b/app/javascript/dashboard/i18n/locale/he/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "השיחה נוצרה",
"CONVERSATION_UPDATED": "השיחה עודכנה",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "השיחה נפתחה"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/he/integrations.json b/app/javascript/dashboard/i18n/locale/he/integrations.json
index 16058633b..37388725a 100644
--- a/app/javascript/dashboard/i18n/locale/he/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/he/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "מאפיינים",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/he/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/he/whatsappTemplates.json
index 295b89a23..67b54f91f 100644
--- a/app/javascript/dashboard/i18n/locale/he/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/he/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "תבניות וואטסאפ",
- "SUBTITLE": "בחר את תבנית הווטסאפ שברצונך לשלוח",
- "TEMPLATE_SELECTED_SUBTITLE": "עיבוד {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "חפש תבניות",
- "NO_TEMPLATES_FOUND": "לא נמצאו תבניות עבור",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "שפה",
- "TEMPLATE_BODY": "גוף התבנית",
- "CATEGORY": "קטגוריה"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "משתנים",
- "VARIABLE_PLACEHOLDER": "הזן ערך {variable}",
- "GO_BACK_LABEL": "חזור",
- "SEND_MESSAGE_LABEL": "לשלוח הודעה",
- "FORM_ERROR_MESSAGE": "נא למלא את כל המשתנים לפני השליחה"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "תבניות וואטסאפ",
+ "SUBTITLE": "בחר את תבנית הווטסאפ שברצונך לשלוח",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "חפש תבניות",
+ "NO_TEMPLATES_FOUND": "לא נמצאו תבניות עבור",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "קטגוריה",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "שפה",
+ "TEMPLATE_BODY": "גוף התבנית",
+ "CATEGORY": "קטגוריה"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "משתנים",
+ "LANGUAGE": "שפה",
+ "CATEGORY": "קטגוריה",
+ "VARIABLE_PLACEHOLDER": "הזן ערך {variable}",
+ "GO_BACK_LABEL": "חזור",
+ "SEND_MESSAGE_LABEL": "לשלוח הודעה",
+ "FORM_ERROR_MESSAGE": "נא למלא את כל המשתנים לפני השליחה",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/hi/automation.json b/app/javascript/dashboard/i18n/locale/hi/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/hi/automation.json
+++ b/app/javascript/dashboard/i18n/locale/hi/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/hi/integrations.json b/app/javascript/dashboard/i18n/locale/hi/integrations.json
index bbb1c6662..cab231d7d 100644
--- a/app/javascript/dashboard/i18n/locale/hi/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/hi/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/hi/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/hi/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/hi/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/hi/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/hr/automation.json b/app/javascript/dashboard/i18n/locale/hr/automation.json
index 8e1bcd7c4..65722bbf2 100644
--- a/app/javascript/dashboard/i18n/locale/hr/automation.json
+++ b/app/javascript/dashboard/i18n/locale/hr/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/hr/integrations.json b/app/javascript/dashboard/i18n/locale/hr/integrations.json
index 63f5370c1..09eb8edaa 100644
--- a/app/javascript/dashboard/i18n/locale/hr/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/hr/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/hr/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/hr/whatsappTemplates.json
index d943a13c1..ce82b426f 100644
--- a/app/javascript/dashboard/i18n/locale/hr/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/hr/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Predlošci",
- "SUBTITLE": "Izaberi whatsapp predložak koji želiš poslati",
- "TEMPLATE_SELECTED_SUBTITLE": "Proces {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Pretraži Predloške",
- "NO_TEMPLATES_FOUND": "Nije pronađen predložak za",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Jezik",
- "TEMPLATE_BODY": "Tijelo predloška",
- "CATEGORY": "Kategorija"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Varijable",
- "VARIABLE_PLACEHOLDER": "Unesi {variable} vrijednost",
- "GO_BACK_LABEL": "Nazad",
- "SEND_MESSAGE_LABEL": "Šalji poruku",
- "FORM_ERROR_MESSAGE": "Popuniti sve varijable prije slanja"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Predlošci",
+ "SUBTITLE": "Izaberi whatsapp predložak koji želiš poslati",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Pretraži Predloške",
+ "NO_TEMPLATES_FOUND": "Nije pronađen predložak za",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Jezik",
+ "TEMPLATE_BODY": "Tijelo predloška",
+ "CATEGORY": "Kategorija"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Varijable",
+ "LANGUAGE": "Jezik",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Unesi {variable} vrijednost",
+ "GO_BACK_LABEL": "Nazad",
+ "SEND_MESSAGE_LABEL": "Šalji poruku",
+ "FORM_ERROR_MESSAGE": "Popuniti sve varijable prije slanja",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/hu/automation.json b/app/javascript/dashboard/i18n/locale/hu/automation.json
index fc6accbe1..82873e610 100644
--- a/app/javascript/dashboard/i18n/locale/hu/automation.json
+++ b/app/javascript/dashboard/i18n/locale/hu/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Beszélgetés létrehozva",
"CONVERSATION_UPDATED": "Beszélgetés frissítve",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/hu/integrations.json b/app/javascript/dashboard/i18n/locale/hu/integrations.json
index 00196e987..33904dde3 100644
--- a/app/javascript/dashboard/i18n/locale/hu/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/hu/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Lehetőségek",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/hu/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/hu/whatsappTemplates.json
index 860ef1875..e28c69704 100644
--- a/app/javascript/dashboard/i18n/locale/hu/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/hu/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp sablonok",
- "SUBTITLE": "Válaszd ki a Whatsapp sablont",
- "TEMPLATE_SELECTED_SUBTITLE": "Feldolgozás: {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Sablon keresése",
- "NO_TEMPLATES_FOUND": "Nem található sablon erre:",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Nyelv",
- "TEMPLATE_BODY": "Sablon törzse",
- "CATEGORY": "Kategória"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Változók",
- "VARIABLE_PLACEHOLDER": "Add meg a {variable} értékét",
- "GO_BACK_LABEL": "Vissza",
- "SEND_MESSAGE_LABEL": "Üzenet küldése",
- "FORM_ERROR_MESSAGE": "Kérlek add meg az összes változó értékét küldés előtt"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp sablonok",
+ "SUBTITLE": "Válaszd ki a Whatsapp sablont",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Sablon keresése",
+ "NO_TEMPLATES_FOUND": "Nem található sablon erre:",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategória",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Nyelv",
+ "TEMPLATE_BODY": "Sablon törzse",
+ "CATEGORY": "Kategória"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Változók",
+ "LANGUAGE": "Nyelv",
+ "CATEGORY": "Kategória",
+ "VARIABLE_PLACEHOLDER": "Add meg a {variable} értékét",
+ "GO_BACK_LABEL": "Vissza",
+ "SEND_MESSAGE_LABEL": "Üzenet küldése",
+ "FORM_ERROR_MESSAGE": "Kérlek add meg az összes változó értékét küldés előtt",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/hy/automation.json b/app/javascript/dashboard/i18n/locale/hy/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/hy/automation.json
+++ b/app/javascript/dashboard/i18n/locale/hy/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/hy/integrations.json b/app/javascript/dashboard/i18n/locale/hy/integrations.json
index 68ce25b15..f0c7abbd3 100644
--- a/app/javascript/dashboard/i18n/locale/hy/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/hy/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/hy/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/hy/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/hy/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/hy/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/id/automation.json b/app/javascript/dashboard/i18n/locale/id/automation.json
index ead34dbdd..e8a688248 100644
--- a/app/javascript/dashboard/i18n/locale/id/automation.json
+++ b/app/javascript/dashboard/i18n/locale/id/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Percakapan Dibuat",
"CONVERSATION_UPDATED": "Percakapan Diperbarui",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/id/integrations.json b/app/javascript/dashboard/i18n/locale/id/integrations.json
index 116557224..98e186693 100644
--- a/app/javascript/dashboard/i18n/locale/id/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/id/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Fitur",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/id/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/id/whatsappTemplates.json
index 88a806522..c9145225c 100644
--- a/app/javascript/dashboard/i18n/locale/id/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/id/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Templat Whatsapp",
- "SUBTITLE": "Pilih templat Whatsapp yang ingin Anda kirim",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Cari Templat",
- "NO_TEMPLATES_FOUND": "Tidak ditemukan templat untuk",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Bahasa",
- "TEMPLATE_BODY": "Isi Templat",
- "CATEGORY": "Kategori"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variabel",
- "VARIABLE_PLACEHOLDER": "Masukkan nilai {variable}",
- "GO_BACK_LABEL": "Kembali",
- "SEND_MESSAGE_LABEL": "Kirim Pesan",
- "FORM_ERROR_MESSAGE": "Harap isi semua variabel sebelum mengirim"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Templat Whatsapp",
+ "SUBTITLE": "Pilih templat Whatsapp yang ingin Anda kirim",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Cari Templat",
+ "NO_TEMPLATES_FOUND": "Tidak ditemukan templat untuk",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategori",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Bahasa",
+ "TEMPLATE_BODY": "Isi Templat",
+ "CATEGORY": "Kategori"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabel",
+ "LANGUAGE": "Bahasa",
+ "CATEGORY": "Kategori",
+ "VARIABLE_PLACEHOLDER": "Masukkan nilai {variable}",
+ "GO_BACK_LABEL": "Kembali",
+ "SEND_MESSAGE_LABEL": "Kirim Pesan",
+ "FORM_ERROR_MESSAGE": "Harap isi semua variabel sebelum mengirim",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/is/automation.json b/app/javascript/dashboard/i18n/locale/is/automation.json
index 145775f1c..8caeb2344 100644
--- a/app/javascript/dashboard/i18n/locale/is/automation.json
+++ b/app/javascript/dashboard/i18n/locale/is/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/is/integrations.json b/app/javascript/dashboard/i18n/locale/is/integrations.json
index d0df69b2d..620ce70cf 100644
--- a/app/javascript/dashboard/i18n/locale/is/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/is/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Fídusar",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/is/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/is/whatsappTemplates.json
index 0be0dfc90..55b7680af 100644
--- a/app/javascript/dashboard/i18n/locale/is/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/is/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Veldu WhatsApp sniðmátið sem þú vilt senda",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Veldu WhatsApp sniðmátið sem þú vilt senda",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/it/automation.json b/app/javascript/dashboard/i18n/locale/it/automation.json
index 2052230e2..df3a5c0a8 100644
--- a/app/javascript/dashboard/i18n/locale/it/automation.json
+++ b/app/javascript/dashboard/i18n/locale/it/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversazione creata",
"CONVERSATION_UPDATED": "Conversazione aggiornata",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/it/integrations.json b/app/javascript/dashboard/i18n/locale/it/integrations.json
index 0697ac873..102539b29 100644
--- a/app/javascript/dashboard/i18n/locale/it/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/it/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funzionalità",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/it/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/it/whatsappTemplates.json
index 2e1db45c7..b45435cca 100644
--- a/app/javascript/dashboard/i18n/locale/it/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/it/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Modelli Whatsapp",
- "SUBTITLE": "Seleziona il modello whatsapp che vuoi inviare",
- "TEMPLATE_SELECTED_SUBTITLE": "Elabora {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Cerca modelli",
- "NO_TEMPLATES_FOUND": "Nessun modello trovato per",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Lingua",
- "TEMPLATE_BODY": "Corpo modello",
- "CATEGORY": "Categoria"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variabili",
- "VARIABLE_PLACEHOLDER": "Inserisci il valore di {variable}",
- "GO_BACK_LABEL": "Torna indietro",
- "SEND_MESSAGE_LABEL": "Invia messaggio",
- "FORM_ERROR_MESSAGE": "Si prega di compilare tutte le variabili prima di inviare"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Modelli Whatsapp",
+ "SUBTITLE": "Seleziona il modello whatsapp che vuoi inviare",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Cerca modelli",
+ "NO_TEMPLATES_FOUND": "Nessun modello trovato per",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Lingua",
+ "TEMPLATE_BODY": "Corpo modello",
+ "CATEGORY": "Categoria"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabili",
+ "LANGUAGE": "Lingua",
+ "CATEGORY": "Categoria",
+ "VARIABLE_PLACEHOLDER": "Inserisci il valore di {variable}",
+ "GO_BACK_LABEL": "Torna indietro",
+ "SEND_MESSAGE_LABEL": "Invia messaggio",
+ "FORM_ERROR_MESSAGE": "Si prega di compilare tutte le variabili prima di inviare",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ja/automation.json b/app/javascript/dashboard/i18n/locale/ja/automation.json
index e78b88454..7b113c718 100644
--- a/app/javascript/dashboard/i18n/locale/ja/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ja/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "会話が作成されました",
"CONVERSATION_UPDATED": "会話が更新されました",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ja/integrations.json b/app/javascript/dashboard/i18n/locale/ja/integrations.json
index d9b7442c5..65c471f3e 100644
--- a/app/javascript/dashboard/i18n/locale/ja/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ja/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "機能",
"ALLOW_CONVERSATION_FAQS": "解決済みの会話からFAQを生成",
- "ALLOW_MEMORIES": "顧客とのやり取りから重要な詳細を記憶としてキャプチャ"
+ "ALLOW_MEMORIES": "顧客とのやり取りから重要な詳細を記憶としてキャプチャ",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ja/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ja/whatsappTemplates.json
index de389b44c..3f1a39d1b 100644
--- a/app/javascript/dashboard/i18n/locale/ja/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ja/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp テンプレート",
- "SUBTITLE": "送信したいWhatsappテンプレートを選択してください",
- "TEMPLATE_SELECTED_SUBTITLE": "{templateName} を処理中"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "テンプレートを検索",
- "NO_TEMPLATES_FOUND": "該当するテンプレートが見つかりません:",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "言語",
- "TEMPLATE_BODY": "テンプレート本文",
- "CATEGORY": "カテゴリ"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "変数",
- "VARIABLE_PLACEHOLDER": "{variable} の値を入力",
- "GO_BACK_LABEL": "戻る",
- "SEND_MESSAGE_LABEL": "メッセージを送信",
- "FORM_ERROR_MESSAGE": "送信前に全ての変数を入力してください"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp テンプレート",
+ "SUBTITLE": "送信したいWhatsappテンプレートを選択してください",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "テンプレートを検索",
+ "NO_TEMPLATES_FOUND": "該当するテンプレートが見つかりません:",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "カテゴリ",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "言語",
+ "TEMPLATE_BODY": "テンプレート本文",
+ "CATEGORY": "カテゴリ"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "変数",
+ "LANGUAGE": "言語",
+ "CATEGORY": "カテゴリ",
+ "VARIABLE_PLACEHOLDER": "{variable} の値を入力",
+ "GO_BACK_LABEL": "戻る",
+ "SEND_MESSAGE_LABEL": "メッセージを送信",
+ "FORM_ERROR_MESSAGE": "送信前に全ての変数を入力してください",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ka/automation.json b/app/javascript/dashboard/i18n/locale/ka/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/ka/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ka/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ka/integrations.json b/app/javascript/dashboard/i18n/locale/ka/integrations.json
index 68ce25b15..f0c7abbd3 100644
--- a/app/javascript/dashboard/i18n/locale/ka/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ka/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ka/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ka/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/ka/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ka/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ko/automation.json b/app/javascript/dashboard/i18n/locale/ko/automation.json
index e12df529d..a5e1b079c 100644
--- a/app/javascript/dashboard/i18n/locale/ko/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ko/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ko/integrations.json b/app/javascript/dashboard/i18n/locale/ko/integrations.json
index 37335c120..e5935505e 100644
--- a/app/javascript/dashboard/i18n/locale/ko/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ko/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "특징",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ko/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ko/whatsappTemplates.json
index 71fc338da..6f6c098f5 100644
--- a/app/javascript/dashboard/i18n/locale/ko/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ko/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "언어",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "언어",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "언어",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/lt/automation.json b/app/javascript/dashboard/i18n/locale/lt/automation.json
index 54bb9075b..3485c2355 100644
--- a/app/javascript/dashboard/i18n/locale/lt/automation.json
+++ b/app/javascript/dashboard/i18n/locale/lt/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Pokalbis sukurtas",
"CONVERSATION_UPDATED": "Pokalbis atnaujintas",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/lt/integrations.json b/app/javascript/dashboard/i18n/locale/lt/integrations.json
index 90bf8d22d..81cba5fee 100644
--- a/app/javascript/dashboard/i18n/locale/lt/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/lt/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funkcijos",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/lt/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/lt/whatsappTemplates.json
index 7c9073480..a46abc2a0 100644
--- a/app/javascript/dashboard/i18n/locale/lt/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/lt/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Šablonai",
- "SUBTITLE": "Pasirinkite WhatsApp šabloną, kurį norite siųsti",
- "TEMPLATE_SELECTED_SUBTITLE": "Apdoroti {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Ieškoti šablonų",
- "NO_TEMPLATES_FOUND": "Šablonų nerasta",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Kalba",
- "TEMPLATE_BODY": "Šablono tekstas",
- "CATEGORY": "Kategorija"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Kintamieji",
- "VARIABLE_PLACEHOLDER": "Įveskite {variable} reikšmę",
- "GO_BACK_LABEL": "Grįžti",
- "SEND_MESSAGE_LABEL": "Išsiųsti pranešimą",
- "FORM_ERROR_MESSAGE": "Prieš siųsdami užpildykite visus kintamuosius"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Šablonai",
+ "SUBTITLE": "Pasirinkite WhatsApp šabloną, kurį norite siųsti",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Ieškoti šablonų",
+ "NO_TEMPLATES_FOUND": "Šablonų nerasta",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Kalba",
+ "TEMPLATE_BODY": "Šablono tekstas",
+ "CATEGORY": "Kategorija"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Kintamieji",
+ "LANGUAGE": "Kalba",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Įveskite {variable} reikšmę",
+ "GO_BACK_LABEL": "Grįžti",
+ "SEND_MESSAGE_LABEL": "Išsiųsti pranešimą",
+ "FORM_ERROR_MESSAGE": "Prieš siųsdami užpildykite visus kintamuosius",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/lv/automation.json b/app/javascript/dashboard/i18n/locale/lv/automation.json
index 193ef9739..9877bb4e3 100644
--- a/app/javascript/dashboard/i18n/locale/lv/automation.json
+++ b/app/javascript/dashboard/i18n/locale/lv/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Saruna izveidota",
"CONVERSATION_UPDATED": "Saruna Atjaunināta",
"MESSAGE_CREATED": "Ziņojums Izveidots",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Saruna Atvērta"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/lv/integrations.json b/app/javascript/dashboard/i18n/locale/lv/integrations.json
index e5a013bd7..b8f109f61 100644
--- a/app/javascript/dashboard/i18n/locale/lv/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/lv/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Īpašības",
"ALLOW_CONVERSATION_FAQS": "Ģenerēt bieži uzdotos jautājumus no atrisinātajām sarunām",
- "ALLOW_MEMORIES": "Pārtvert galvenās nianses kā atmiņas no klientu mijiedarbībām."
+ "ALLOW_MEMORIES": "Pārtvert galvenās nianses kā atmiņas no klientu mijiedarbībām.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/lv/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/lv/whatsappTemplates.json
index 330ab173d..0b51f15f3 100644
--- a/app/javascript/dashboard/i18n/locale/lv/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/lv/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "WhatsApp Veidnes",
- "SUBTITLE": "Izvēlieties WhatsApp veidni, kuru vēlaties nosūtīt",
- "TEMPLATE_SELECTED_SUBTITLE": "Apstrādāt {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Meklēt Veidnes",
- "NO_TEMPLATES_FOUND": "Veidnes nav atrastas",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Valoda",
- "TEMPLATE_BODY": "Veidnes Pamatteksts",
- "CATEGORY": "Kategorija"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Mainīgie",
- "VARIABLE_PLACEHOLDER": "Ievadiet {variable} vērtību",
- "GO_BACK_LABEL": "Atgriezties",
- "SEND_MESSAGE_LABEL": "Sūtīt Ziņojumu",
- "FORM_ERROR_MESSAGE": "Lūdzu, pirms nosūtīšanas aizpildiet visus mainīgos"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "WhatsApp Veidnes",
+ "SUBTITLE": "Izvēlieties WhatsApp veidni, kuru vēlaties nosūtīt",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Meklēt Veidnes",
+ "NO_TEMPLATES_FOUND": "Veidnes nav atrastas",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Valoda",
+ "TEMPLATE_BODY": "Veidnes Pamatteksts",
+ "CATEGORY": "Kategorija"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Mainīgie",
+ "LANGUAGE": "Valoda",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Ievadiet {variable} vērtību",
+ "GO_BACK_LABEL": "Atgriezties",
+ "SEND_MESSAGE_LABEL": "Sūtīt Ziņojumu",
+ "FORM_ERROR_MESSAGE": "Lūdzu, pirms nosūtīšanas aizpildiet visus mainīgos",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ml/automation.json b/app/javascript/dashboard/i18n/locale/ml/automation.json
index b50618313..6c3cd4517 100644
--- a/app/javascript/dashboard/i18n/locale/ml/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ml/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ml/integrations.json b/app/javascript/dashboard/i18n/locale/ml/integrations.json
index f64124402..f375f9ff2 100644
--- a/app/javascript/dashboard/i18n/locale/ml/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ml/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ml/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ml/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/ml/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ml/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ms/automation.json b/app/javascript/dashboard/i18n/locale/ms/automation.json
index 2bdccfc0e..e3990a8ab 100644
--- a/app/javascript/dashboard/i18n/locale/ms/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ms/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ms/integrations.json b/app/javascript/dashboard/i18n/locale/ms/integrations.json
index 3f5083d30..cb2c4a677 100644
--- a/app/javascript/dashboard/i18n/locale/ms/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ms/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ms/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ms/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/ms/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ms/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ne/automation.json b/app/javascript/dashboard/i18n/locale/ne/automation.json
index a4c0ce10b..4aba66e26 100644
--- a/app/javascript/dashboard/i18n/locale/ne/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ne/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ne/integrations.json b/app/javascript/dashboard/i18n/locale/ne/integrations.json
index bc72dd2e3..56a6766fc 100644
--- a/app/javascript/dashboard/i18n/locale/ne/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ne/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ne/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ne/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/ne/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ne/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/nl/automation.json b/app/javascript/dashboard/i18n/locale/nl/automation.json
index 269bf96ab..dd45ce36d 100644
--- a/app/javascript/dashboard/i18n/locale/nl/automation.json
+++ b/app/javascript/dashboard/i18n/locale/nl/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Gesprek aangemaakt",
"CONVERSATION_UPDATED": "Gesprek bijgewerkt",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/nl/integrations.json b/app/javascript/dashboard/i18n/locale/nl/integrations.json
index 7b971629c..cecc6fac0 100644
--- a/app/javascript/dashboard/i18n/locale/nl/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/nl/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/nl/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/nl/whatsappTemplates.json
index 4061f4090..7e4317fb8 100644
--- a/app/javascript/dashboard/i18n/locale/nl/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/nl/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Selecteer de whatsapp template die u wilt verzenden",
- "TEMPLATE_SELECTED_SUBTITLE": "Verwerk {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Templates zoeken",
- "NO_TEMPLATES_FOUND": "Geen templates gevonden voor",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Taal",
- "TEMPLATE_BODY": "Template bericht",
- "CATEGORY": "Categorie"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variabelen",
- "VARIABLE_PLACEHOLDER": "Voer {variable} waarde in",
- "GO_BACK_LABEL": "Ga terug",
- "SEND_MESSAGE_LABEL": "Verstuur bericht",
- "FORM_ERROR_MESSAGE": "Vul alstublieft alle variabelen in voordat u deze verzendt"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Selecteer de whatsapp template die u wilt verzenden",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Templates zoeken",
+ "NO_TEMPLATES_FOUND": "Geen templates gevonden voor",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categorie",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Taal",
+ "TEMPLATE_BODY": "Template bericht",
+ "CATEGORY": "Categorie"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabelen",
+ "LANGUAGE": "Taal",
+ "CATEGORY": "Categorie",
+ "VARIABLE_PLACEHOLDER": "Voer {variable} waarde in",
+ "GO_BACK_LABEL": "Ga terug",
+ "SEND_MESSAGE_LABEL": "Verstuur bericht",
+ "FORM_ERROR_MESSAGE": "Vul alstublieft alle variabelen in voordat u deze verzendt",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/no/automation.json b/app/javascript/dashboard/i18n/locale/no/automation.json
index 4a5b37875..c76e4dc1a 100644
--- a/app/javascript/dashboard/i18n/locale/no/automation.json
+++ b/app/javascript/dashboard/i18n/locale/no/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/no/integrations.json b/app/javascript/dashboard/i18n/locale/no/integrations.json
index 10b668191..033245b58 100644
--- a/app/javascript/dashboard/i18n/locale/no/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/no/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funksjoner",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/no/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/no/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/no/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/no/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/pl/automation.json b/app/javascript/dashboard/i18n/locale/pl/automation.json
index 3ebfa9550..f4212f70c 100644
--- a/app/javascript/dashboard/i18n/locale/pl/automation.json
+++ b/app/javascript/dashboard/i18n/locale/pl/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Rozpoczęcie rozmowy",
"CONVERSATION_UPDATED": "Aktualizacja rozmowy",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/pl/integrations.json b/app/javascript/dashboard/i18n/locale/pl/integrations.json
index ec149a9e4..7787df954 100644
--- a/app/javascript/dashboard/i18n/locale/pl/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/pl/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funkcje",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/pl/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/pl/whatsappTemplates.json
index fb87e1425..e1946af25 100644
--- a/app/javascript/dashboard/i18n/locale/pl/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/pl/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Szablony WhatsApp",
- "SUBTITLE": "Wybierz szablon WhatsApp, który chcesz wysłać",
- "TEMPLATE_SELECTED_SUBTITLE": "Przetwarzanie {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Wyszukaj szablony",
- "NO_TEMPLATES_FOUND": "Nie znaleziono szablonów dla",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Język",
- "TEMPLATE_BODY": "Treść szablonu",
- "CATEGORY": "Kategoria"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Zmienne",
- "VARIABLE_PLACEHOLDER": "Wprowadź wartość {variable}",
- "GO_BACK_LABEL": "Powrót",
- "SEND_MESSAGE_LABEL": "Wyślij wiadomość",
- "FORM_ERROR_MESSAGE": "Proszę wypełnić wszystkie zmienne przed wysłaniem"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Szablony WhatsApp",
+ "SUBTITLE": "Wybierz szablon WhatsApp, który chcesz wysłać",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Wyszukaj szablony",
+ "NO_TEMPLATES_FOUND": "Nie znaleziono szablonów dla",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Język",
+ "TEMPLATE_BODY": "Treść szablonu",
+ "CATEGORY": "Kategoria"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Zmienne",
+ "LANGUAGE": "Język",
+ "CATEGORY": "Kategoria",
+ "VARIABLE_PLACEHOLDER": "Wprowadź wartość {variable}",
+ "GO_BACK_LABEL": "Powrót",
+ "SEND_MESSAGE_LABEL": "Wyślij wiadomość",
+ "FORM_ERROR_MESSAGE": "Proszę wypełnić wszystkie zmienne przed wysłaniem",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/pt/automation.json b/app/javascript/dashboard/i18n/locale/pt/automation.json
index a2740c577..0a720f528 100644
--- a/app/javascript/dashboard/i18n/locale/pt/automation.json
+++ b/app/javascript/dashboard/i18n/locale/pt/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversa criada",
"CONVERSATION_UPDATED": "Conversa atualizada",
"MESSAGE_CREATED": "Mensagem criada",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversa aberta"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/pt/integrations.json b/app/javascript/dashboard/i18n/locale/pt/integrations.json
index 69c3ddb4f..2a86bd97f 100644
--- a/app/javascript/dashboard/i18n/locale/pt/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/pt/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Características",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/pt/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/pt/whatsappTemplates.json
index cba42fd01..45100216d 100644
--- a/app/javascript/dashboard/i18n/locale/pt/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/pt/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Template do WhatsApp",
- "SUBTITLE": "Selecione o template do whatsapp que você deseja enviar",
- "TEMPLATE_SELECTED_SUBTITLE": "Processo {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Buscar templates",
- "NO_TEMPLATES_FOUND": "Nenhum template encontrado para",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Idioma",
- "TEMPLATE_BODY": "Corpo do Template",
- "CATEGORY": "Categoria"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variáveis",
- "VARIABLE_PLACEHOLDER": "Digite o valor {variable}",
- "GO_BACK_LABEL": "Voltar",
- "SEND_MESSAGE_LABEL": "Enviar mensagem",
- "FORM_ERROR_MESSAGE": "Preencha todas as variáveis antes de enviar"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Template do WhatsApp",
+ "SUBTITLE": "Selecione o template do whatsapp que você deseja enviar",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Buscar templates",
+ "NO_TEMPLATES_FOUND": "Nenhum template encontrado para",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Idioma",
+ "TEMPLATE_BODY": "Corpo do Template",
+ "CATEGORY": "Categoria"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variáveis",
+ "LANGUAGE": "Idioma",
+ "CATEGORY": "Categoria",
+ "VARIABLE_PLACEHOLDER": "Digite o valor {variable}",
+ "GO_BACK_LABEL": "Voltar",
+ "SEND_MESSAGE_LABEL": "Enviar mensagem",
+ "FORM_ERROR_MESSAGE": "Preencha todas as variáveis antes de enviar",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/automation.json b/app/javascript/dashboard/i18n/locale/pt_BR/automation.json
index 22a097713..a3ecb50ac 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/automation.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversa Criada",
"CONVERSATION_UPDATED": "Conversa Atualizada",
"MESSAGE_CREATED": "Mensagem Criada",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversa Aberta"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/integrations.json b/app/javascript/dashboard/i18n/locale/pt_BR/integrations.json
index 1b690cb82..67102dbb5 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funcionalidades",
"ALLOW_CONVERSATION_FAQS": "Gerar perguntas frequentes a partir de conversas resolvidas",
- "ALLOW_MEMORIES": "Capture os principais detalhes como memórias de interações do cliente."
+ "ALLOW_MEMORIES": "Capture os principais detalhes como memórias de interações do cliente.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/pt_BR/whatsappTemplates.json
index 1149bc577..27accbaa8 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Templates do Whatsapp",
- "SUBTITLE": "Selecione o template do whatsapp que você deseja enviar",
- "TEMPLATE_SELECTED_SUBTITLE": "Processar {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Pesquisar modelos",
- "NO_TEMPLATES_FOUND": "Não há templates encontrados para",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Idioma",
- "TEMPLATE_BODY": "Conteúdo do Template",
- "CATEGORY": "Categoria"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variáveis",
- "VARIABLE_PLACEHOLDER": "Insira o valor para {variable}",
- "GO_BACK_LABEL": "Voltar",
- "SEND_MESSAGE_LABEL": "Enviar Mensagem",
- "FORM_ERROR_MESSAGE": "Por favor, preencha todas as variáveis antes de enviar"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Templates do Whatsapp",
+ "SUBTITLE": "Selecione o template do whatsapp que você deseja enviar",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Pesquisar modelos",
+ "NO_TEMPLATES_FOUND": "Não há templates encontrados para",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Idioma",
+ "TEMPLATE_BODY": "Conteúdo do Template",
+ "CATEGORY": "Categoria"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variáveis",
+ "LANGUAGE": "Idioma",
+ "CATEGORY": "Categoria",
+ "VARIABLE_PLACEHOLDER": "Insira o valor para {variable}",
+ "GO_BACK_LABEL": "Voltar",
+ "SEND_MESSAGE_LABEL": "Enviar Mensagem",
+ "FORM_ERROR_MESSAGE": "Por favor, preencha todas as variáveis antes de enviar",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ro/automation.json b/app/javascript/dashboard/i18n/locale/ro/automation.json
index 38a4dd0c6..8f18d43fe 100644
--- a/app/javascript/dashboard/i18n/locale/ro/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ro/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversație creată",
"CONVERSATION_UPDATED": "Conversație actualizată",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ro/integrations.json b/app/javascript/dashboard/i18n/locale/ro/integrations.json
index 513b93b64..dae7e2088 100644
--- a/app/javascript/dashboard/i18n/locale/ro/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ro/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Caracteristici",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ro/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ro/whatsappTemplates.json
index fda8d7b10..e1697e19f 100644
--- a/app/javascript/dashboard/i18n/locale/ro/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ro/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Șabloane WhatsApp",
- "SUBTITLE": "Selectați șablonul WhatsApp pe care doriți să trimiteți",
- "TEMPLATE_SELECTED_SUBTITLE": "{templateName} de proces"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Caută Șabloane",
- "NO_TEMPLATES_FOUND": "Nu s-au găsit șabloane pentru",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Limbă",
- "TEMPLATE_BODY": "Corpul șablonului",
- "CATEGORY": "Categorie"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variabile",
- "VARIABLE_PLACEHOLDER": "Introducerea {variable} valoare",
- "GO_BACK_LABEL": "Înapoi",
- "SEND_MESSAGE_LABEL": "Trimite mesaj",
- "FORM_ERROR_MESSAGE": "Vă rugăm să completați toate variabilele înainte de a trimite"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Șabloane WhatsApp",
+ "SUBTITLE": "Selectați șablonul WhatsApp pe care doriți să trimiteți",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Caută Șabloane",
+ "NO_TEMPLATES_FOUND": "Nu s-au găsit șabloane pentru",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categorie",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Limbă",
+ "TEMPLATE_BODY": "Corpul șablonului",
+ "CATEGORY": "Categorie"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabile",
+ "LANGUAGE": "Limbă",
+ "CATEGORY": "Categorie",
+ "VARIABLE_PLACEHOLDER": "Introducerea {variable} valoare",
+ "GO_BACK_LABEL": "Înapoi",
+ "SEND_MESSAGE_LABEL": "Trimite mesaj",
+ "FORM_ERROR_MESSAGE": "Vă rugăm să completați toate variabilele înainte de a trimite",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ru/automation.json b/app/javascript/dashboard/i18n/locale/ru/automation.json
index 49e5699c9..08ead85fe 100644
--- a/app/javascript/dashboard/i18n/locale/ru/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ru/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Диалог создан",
"CONVERSATION_UPDATED": "Диалог обновлён",
"MESSAGE_CREATED": "Сообщение создано",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ru/integrations.json b/app/javascript/dashboard/i18n/locale/ru/integrations.json
index 833e9faf5..6de478fb1 100644
--- a/app/javascript/dashboard/i18n/locale/ru/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ru/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Возможности",
"ALLOW_CONVERSATION_FAQS": "Создать FAQ из решённых диалогов",
- "ALLOW_MEMORIES": "Сохраняйте ключевые детали в виде воспоминаний о взаимодействии с клиентами."
+ "ALLOW_MEMORIES": "Сохраняйте ключевые детали в виде воспоминаний о взаимодействии с клиентами.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ru/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ru/whatsappTemplates.json
index 65c55ec5a..a8effa8b8 100644
--- a/app/javascript/dashboard/i18n/locale/ru/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ru/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Шаблоны Whatsapp",
- "SUBTITLE": "Выберите шаблон whatsapp, который вы хотите отправить",
- "TEMPLATE_SELECTED_SUBTITLE": "Обработка {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Найти шаблоны",
- "NO_TEMPLATES_FOUND": "Не найдено шаблонов для",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Язык",
- "TEMPLATE_BODY": "Тело шаблона",
- "CATEGORY": "Категория"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Переменные",
- "VARIABLE_PLACEHOLDER": "Введите значение {variable}",
- "GO_BACK_LABEL": "Вернуться",
- "SEND_MESSAGE_LABEL": "Отправить сообщение",
- "FORM_ERROR_MESSAGE": "Пожалуйста, заполните все переменные перед отправкой"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Шаблоны Whatsapp",
+ "SUBTITLE": "Выберите шаблон whatsapp, который вы хотите отправить",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Найти шаблоны",
+ "NO_TEMPLATES_FOUND": "Не найдено шаблонов для",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Категория",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Язык",
+ "TEMPLATE_BODY": "Тело шаблона",
+ "CATEGORY": "Категория"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Переменные",
+ "LANGUAGE": "Язык",
+ "CATEGORY": "Категория",
+ "VARIABLE_PLACEHOLDER": "Введите значение {variable}",
+ "GO_BACK_LABEL": "Вернуться",
+ "SEND_MESSAGE_LABEL": "Отправить сообщение",
+ "FORM_ERROR_MESSAGE": "Пожалуйста, заполните все переменные перед отправкой",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/sh/automation.json b/app/javascript/dashboard/i18n/locale/sh/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/sh/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sh/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/sh/integrations.json b/app/javascript/dashboard/i18n/locale/sh/integrations.json
index 68ce25b15..f0c7abbd3 100644
--- a/app/javascript/dashboard/i18n/locale/sh/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sh/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/sh/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sh/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/sh/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sh/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/sk/automation.json b/app/javascript/dashboard/i18n/locale/sk/automation.json
index 9c2c463bc..2ea93e17a 100644
--- a/app/javascript/dashboard/i18n/locale/sk/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sk/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/sk/integrations.json b/app/javascript/dashboard/i18n/locale/sk/integrations.json
index e0d32c972..55cd79768 100644
--- a/app/javascript/dashboard/i18n/locale/sk/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sk/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/sk/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sk/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/sk/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sk/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/sl/automation.json b/app/javascript/dashboard/i18n/locale/sl/automation.json
index 694902ab5..2f97d7257 100644
--- a/app/javascript/dashboard/i18n/locale/sl/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sl/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/sl/integrations.json b/app/javascript/dashboard/i18n/locale/sl/integrations.json
index 5880a6b3c..e1f8321c5 100644
--- a/app/javascript/dashboard/i18n/locale/sl/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sl/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/sl/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sl/whatsappTemplates.json
index eb4acbea6..aa6011859 100644
--- a/app/javascript/dashboard/i18n/locale/sl/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sl/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Predloge za WhatsApp",
- "SUBTITLE": "Izberite predlogo WhatsApp, ki jo želite poslati",
- "TEMPLATE_SELECTED_SUBTITLE": "Obdelaj {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Išči predloge",
- "NO_TEMPLATES_FOUND": "Ni najdenih predlog za",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Jezik",
- "TEMPLATE_BODY": "Telo predloge",
- "CATEGORY": "Kategorija"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Spremenljivke",
- "VARIABLE_PLACEHOLDER": "Vnesi vrednost {variable}",
- "GO_BACK_LABEL": "Pojdi nazaj",
- "SEND_MESSAGE_LABEL": "Pošlji sporočilo",
- "FORM_ERROR_MESSAGE": "Prosimo, izpolnite vse spremenljivke pred pošiljanjem"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Predloge za WhatsApp",
+ "SUBTITLE": "Izberite predlogo WhatsApp, ki jo želite poslati",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Išči predloge",
+ "NO_TEMPLATES_FOUND": "Ni najdenih predlog za",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Jezik",
+ "TEMPLATE_BODY": "Telo predloge",
+ "CATEGORY": "Kategorija"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Spremenljivke",
+ "LANGUAGE": "Jezik",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Vnesi vrednost {variable}",
+ "GO_BACK_LABEL": "Pojdi nazaj",
+ "SEND_MESSAGE_LABEL": "Pošlji sporočilo",
+ "FORM_ERROR_MESSAGE": "Prosimo, izpolnite vse spremenljivke pred pošiljanjem",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/sq/automation.json b/app/javascript/dashboard/i18n/locale/sq/automation.json
index 643d3b09a..280c6c65d 100644
--- a/app/javascript/dashboard/i18n/locale/sq/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sq/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/sq/integrations.json b/app/javascript/dashboard/i18n/locale/sq/integrations.json
index 24315dec2..3fe43940a 100644
--- a/app/javascript/dashboard/i18n/locale/sq/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sq/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/sq/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sq/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/sq/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sq/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/sr/automation.json b/app/javascript/dashboard/i18n/locale/sr/automation.json
index f3bee7561..0f15f8e3d 100644
--- a/app/javascript/dashboard/i18n/locale/sr/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sr/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Razgovor je napravljen",
"CONVERSATION_UPDATED": "Razgovor je izmenjen",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/sr/integrations.json b/app/javascript/dashboard/i18n/locale/sr/integrations.json
index 8dada5dc6..77c6d0b7b 100644
--- a/app/javascript/dashboard/i18n/locale/sr/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sr/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Mogućnosti",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/sr/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sr/whatsappTemplates.json
index 27fed1503..a3cf57d23 100644
--- a/app/javascript/dashboard/i18n/locale/sr/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sr/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp šabloni",
- "SUBTITLE": "Izaberite koji whatsapp šablon želite da pošaljete",
- "TEMPLATE_SELECTED_SUBTITLE": "Obrada {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Pretraži šablone",
- "NO_TEMPLATES_FOUND": "Nijedan šablon nije pronađen",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Jezik",
- "TEMPLATE_BODY": "Telo šablona",
- "CATEGORY": "Kategorija"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Promenljive",
- "VARIABLE_PLACEHOLDER": "Unesite vrednost za {variable}",
- "GO_BACK_LABEL": "Povratak",
- "SEND_MESSAGE_LABEL": "Pošalji poruku",
- "FORM_ERROR_MESSAGE": "Molim vas popunite sve promenljive pre slanja"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp šabloni",
+ "SUBTITLE": "Izaberite koji whatsapp šablon želite da pošaljete",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Pretraži šablone",
+ "NO_TEMPLATES_FOUND": "Nijedan šablon nije pronađen",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Jezik",
+ "TEMPLATE_BODY": "Telo šablona",
+ "CATEGORY": "Kategorija"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Promenljive",
+ "LANGUAGE": "Jezik",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Unesite vrednost za {variable}",
+ "GO_BACK_LABEL": "Povratak",
+ "SEND_MESSAGE_LABEL": "Pošalji poruku",
+ "FORM_ERROR_MESSAGE": "Molim vas popunite sve promenljive pre slanja",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/sv/automation.json b/app/javascript/dashboard/i18n/locale/sv/automation.json
index d50296a86..b21bfe262 100644
--- a/app/javascript/dashboard/i18n/locale/sv/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sv/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/sv/integrations.json b/app/javascript/dashboard/i18n/locale/sv/integrations.json
index 805d3481f..ce27888e4 100644
--- a/app/javascript/dashboard/i18n/locale/sv/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sv/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Funktioner",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/sv/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sv/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/sv/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sv/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ta/automation.json b/app/javascript/dashboard/i18n/locale/ta/automation.json
index b3aa9be50..ba7a725cd 100644
--- a/app/javascript/dashboard/i18n/locale/ta/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ta/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ta/integrations.json b/app/javascript/dashboard/i18n/locale/ta/integrations.json
index 160cb189f..30244bd5a 100644
--- a/app/javascript/dashboard/i18n/locale/ta/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ta/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ta/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ta/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/ta/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ta/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/th/automation.json b/app/javascript/dashboard/i18n/locale/th/automation.json
index eac475926..8df9e274f 100644
--- a/app/javascript/dashboard/i18n/locale/th/automation.json
+++ b/app/javascript/dashboard/i18n/locale/th/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "การสนทนาที่ถูกสร้าง",
"CONVERSATION_UPDATED": "อัปเดตการสนทนาแล้ว",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/th/integrations.json b/app/javascript/dashboard/i18n/locale/th/integrations.json
index 692daf68e..c9ab15529 100644
--- a/app/javascript/dashboard/i18n/locale/th/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/th/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "ฟีเจอร์",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/th/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/th/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/th/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/th/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/tl/automation.json b/app/javascript/dashboard/i18n/locale/tl/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/tl/automation.json
+++ b/app/javascript/dashboard/i18n/locale/tl/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/tl/integrations.json b/app/javascript/dashboard/i18n/locale/tl/integrations.json
index 1614931a0..be9281284 100644
--- a/app/javascript/dashboard/i18n/locale/tl/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/tl/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/tl/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/tl/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/tl/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/tl/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/tr/automation.json b/app/javascript/dashboard/i18n/locale/tr/automation.json
index 2a1f9f55a..3bac1c990 100644
--- a/app/javascript/dashboard/i18n/locale/tr/automation.json
+++ b/app/javascript/dashboard/i18n/locale/tr/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Görüşme Oluşturuldu",
"CONVERSATION_UPDATED": "Görüşme Güncellendi",
"MESSAGE_CREATED": "Mesaj Oluşturuldu",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Sohbet Açıldı"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/tr/integrations.json b/app/javascript/dashboard/i18n/locale/tr/integrations.json
index 5e774e07a..d414ea777 100644
--- a/app/javascript/dashboard/i18n/locale/tr/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/tr/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Özellikleri",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/tr/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/tr/whatsappTemplates.json
index da51ff735..47dfd1c55 100644
--- a/app/javascript/dashboard/i18n/locale/tr/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/tr/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "WhatsApp Şablonları",
- "SUBTITLE": "Göndermek istediğiniz WhatsApp şablonunu seçin",
- "TEMPLATE_SELECTED_SUBTITLE": "{templateName} işleniyor"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Şablon Ara",
- "NO_TEMPLATES_FOUND": "İçin hiç şablon bulunamadı",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Dil",
- "TEMPLATE_BODY": "Şablon İçeriği",
- "CATEGORY": "Kategori"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Değişkenler",
- "VARIABLE_PLACEHOLDER": "{variable} değerini girin",
- "GO_BACK_LABEL": "Geri Git",
- "SEND_MESSAGE_LABEL": "Mesaj Gönder",
- "FORM_ERROR_MESSAGE": "Lütfen göndermeden önce tüm değişkenleri doldurun"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "WhatsApp Şablonları",
+ "SUBTITLE": "Göndermek istediğiniz WhatsApp şablonunu seçin",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Şablon Ara",
+ "NO_TEMPLATES_FOUND": "İçin hiç şablon bulunamadı",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategori",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Dil",
+ "TEMPLATE_BODY": "Şablon İçeriği",
+ "CATEGORY": "Kategori"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Değişkenler",
+ "LANGUAGE": "Dil",
+ "CATEGORY": "Kategori",
+ "VARIABLE_PLACEHOLDER": "{variable} değerini girin",
+ "GO_BACK_LABEL": "Geri Git",
+ "SEND_MESSAGE_LABEL": "Mesaj Gönder",
+ "FORM_ERROR_MESSAGE": "Lütfen göndermeden önce tüm değişkenleri doldurun",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/uk/automation.json b/app/javascript/dashboard/i18n/locale/uk/automation.json
index 39ad3e474..84617e9f0 100644
--- a/app/javascript/dashboard/i18n/locale/uk/automation.json
+++ b/app/javascript/dashboard/i18n/locale/uk/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Розмову створено",
"CONVERSATION_UPDATED": "Розмову оновлено",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/uk/integrations.json b/app/javascript/dashboard/i18n/locale/uk/integrations.json
index bc7afcfc6..fd1c820fc 100644
--- a/app/javascript/dashboard/i18n/locale/uk/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/uk/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Особливості",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/uk/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/uk/whatsappTemplates.json
index 1246919f5..d181cd3f1 100644
--- a/app/javascript/dashboard/i18n/locale/uk/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/uk/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Шаблони Whatsapp",
- "SUBTITLE": "Виберіть шаблон whatsApp, який Ви хочете надіслати",
- "TEMPLATE_SELECTED_SUBTITLE": "Процес {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Знайти шаблони",
- "NO_TEMPLATES_FOUND": "Шаблонів не знайдено для",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Мова",
- "TEMPLATE_BODY": "Тіло шаблона",
- "CATEGORY": "Категорія"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Змінні",
- "VARIABLE_PLACEHOLDER": "Введіть значення {variable}",
- "GO_BACK_LABEL": "Повернутися",
- "SEND_MESSAGE_LABEL": "Надіслати повідомлення",
- "FORM_ERROR_MESSAGE": "Будь ласка, заповніть всі змінні перед надсиланням"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Шаблони Whatsapp",
+ "SUBTITLE": "Виберіть шаблон whatsApp, який Ви хочете надіслати",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Знайти шаблони",
+ "NO_TEMPLATES_FOUND": "Шаблонів не знайдено для",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Категорія",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Мова",
+ "TEMPLATE_BODY": "Тіло шаблона",
+ "CATEGORY": "Категорія"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Змінні",
+ "LANGUAGE": "Мова",
+ "CATEGORY": "Категорія",
+ "VARIABLE_PLACEHOLDER": "Введіть значення {variable}",
+ "GO_BACK_LABEL": "Повернутися",
+ "SEND_MESSAGE_LABEL": "Надіслати повідомлення",
+ "FORM_ERROR_MESSAGE": "Будь ласка, заповніть всі змінні перед надсиланням",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ur/automation.json b/app/javascript/dashboard/i18n/locale/ur/automation.json
index a129539f8..3ef2ccc5e 100644
--- a/app/javascript/dashboard/i18n/locale/ur/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ur/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ur/integrations.json b/app/javascript/dashboard/i18n/locale/ur/integrations.json
index bcf0c1a33..eaab517a2 100644
--- a/app/javascript/dashboard/i18n/locale/ur/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ur/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ur/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ur/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/ur/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ur/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/automation.json b/app/javascript/dashboard/i18n/locale/ur_IN/automation.json
index cf63de81c..80274f488 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/integrations.json b/app/javascript/dashboard/i18n/locale/ur_IN/integrations.json
index 68ce25b15..f0c7abbd3 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ur_IN/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/vi/automation.json b/app/javascript/dashboard/i18n/locale/vi/automation.json
index cb483e847..1f8d5ea79 100644
--- a/app/javascript/dashboard/i18n/locale/vi/automation.json
+++ b/app/javascript/dashboard/i18n/locale/vi/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Cuộc trò chuyện đã được tạo",
"CONVERSATION_UPDATED": "Cuộc trò chuyện đã được cập nhật",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/vi/integrations.json b/app/javascript/dashboard/i18n/locale/vi/integrations.json
index fdb96d792..10a6df6b9 100644
--- a/app/javascript/dashboard/i18n/locale/vi/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/vi/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Các tính năng",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/vi/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/vi/whatsappTemplates.json
index a4abce833..fefd680ec 100644
--- a/app/javascript/dashboard/i18n/locale/vi/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/vi/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Mẫu Whatsapp",
- "SUBTITLE": "Chọn mẫu whatsapp bạn muốn gửi",
- "TEMPLATE_SELECTED_SUBTITLE": "Xử lý {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Tìm kiếm Mẫu",
- "NO_TEMPLATES_FOUND": "Không tìm thấy mẫu nào cho",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Ngôn ngữ",
- "TEMPLATE_BODY": "Nội dung của Mẫu",
- "CATEGORY": "Loại"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Biến",
- "VARIABLE_PLACEHOLDER": "Nhập giá trị {variable}",
- "GO_BACK_LABEL": "Quay lại",
- "SEND_MESSAGE_LABEL": "Gửi tin nhắn",
- "FORM_ERROR_MESSAGE": "Vui lòng điền vào tất cả các biến trước khi gửi"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Mẫu Whatsapp",
+ "SUBTITLE": "Chọn mẫu whatsapp bạn muốn gửi",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Tìm kiếm Mẫu",
+ "NO_TEMPLATES_FOUND": "Không tìm thấy mẫu nào cho",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Loại",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Ngôn ngữ",
+ "TEMPLATE_BODY": "Nội dung của Mẫu",
+ "CATEGORY": "Loại"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Biến",
+ "LANGUAGE": "Ngôn ngữ",
+ "CATEGORY": "Loại",
+ "VARIABLE_PLACEHOLDER": "Nhập giá trị {variable}",
+ "GO_BACK_LABEL": "Quay lại",
+ "SEND_MESSAGE_LABEL": "Gửi tin nhắn",
+ "FORM_ERROR_MESSAGE": "Vui lòng điền vào tất cả các biến trước khi gửi",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/automation.json b/app/javascript/dashboard/i18n/locale/zh_CN/automation.json
index 5b6872011..206b50103 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/automation.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "对话创建",
"CONVERSATION_UPDATED": "对话已更新",
"MESSAGE_CREATED": "消息已创建",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "对话已打开"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/integrations.json b/app/javascript/dashboard/i18n/locale/zh_CN/integrations.json
index 0132e71c6..24de2da6d 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "特性",
"ALLOW_CONVERSATION_FAQS": "从已解决的对话中生成常见问题",
- "ALLOW_MEMORIES": "从客户互动中捕获关键细节作为记忆"
+ "ALLOW_MEMORIES": "从客户互动中捕获关键细节作为记忆",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/zh_CN/whatsappTemplates.json
index cade063c0..9ca1ae234 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp 模板列表",
- "SUBTITLE": "请选择想要发送的 Whatsapp 消息模板",
- "TEMPLATE_SELECTED_SUBTITLE": "{templateName} 处理中"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "查找模板",
- "NO_TEMPLATES_FOUND": "没有找到对应的模版",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "语言",
- "TEMPLATE_BODY": "模板内容",
- "CATEGORY": "类别"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "参数",
- "VARIABLE_PLACEHOLDER": "请填写 {variable}",
- "GO_BACK_LABEL": "返回",
- "SEND_MESSAGE_LABEL": "发送消息",
- "FORM_ERROR_MESSAGE": "你必须填写所有参数才能发送"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp 模板列表",
+ "SUBTITLE": "请选择想要发送的 Whatsapp 消息模板",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "查找模板",
+ "NO_TEMPLATES_FOUND": "没有找到对应的模版",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "类别",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "语言",
+ "TEMPLATE_BODY": "模板内容",
+ "CATEGORY": "类别"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "参数",
+ "LANGUAGE": "语言",
+ "CATEGORY": "类别",
+ "VARIABLE_PLACEHOLDER": "请填写 {variable}",
+ "GO_BACK_LABEL": "返回",
+ "SEND_MESSAGE_LABEL": "发送消息",
+ "FORM_ERROR_MESSAGE": "你必须填写所有参数才能发送",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/automation.json b/app/javascript/dashboard/i18n/locale/zh_TW/automation.json
index d44d241ff..416683ea6 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/automation.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/automation.json
@@ -131,6 +131,7 @@
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
+ "CONVERSATION_RESOLVED": "Conversation Resolved",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/integrations.json b/app/javascript/dashboard/i18n/locale/zh_TW/integrations.json
index df361a65c..598fc185b 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/integrations.json
@@ -472,7 +472,8 @@
"FEATURES": {
"TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
- "ALLOW_MEMORIES": "Capture key details as memories from customer interactions."
+ "ALLOW_MEMORIES": "Capture key details as memories from customer interactions.",
+ "ALLOW_CITATIONS": "Include source citations in responses"
}
},
"EDIT": {
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/zh_TW/whatsappTemplates.json
index 4887d07b6..5f53faaa8 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/whatsappTemplates.json
@@ -1,29 +1,46 @@
{
- "WHATSAPP_TEMPLATES": {
- "MODAL": {
- "TITLE": "Whatsapp Templates",
- "SUBTITLE": "Select the whatsapp template you want to send",
- "TEMPLATE_SELECTED_SUBTITLE": "Process {templateName}"
- },
- "PICKER": {
- "SEARCH_PLACEHOLDER": "Search Templates",
- "NO_TEMPLATES_FOUND": "No templates found for",
- "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
- "REFRESH_BUTTON": "Refresh templates",
- "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
- "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
- "LABELS": {
- "LANGUAGE": "Language",
- "TEMPLATE_BODY": "Template Body",
- "CATEGORY": "Category"
- }
- },
- "PARSER": {
- "VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter {variable} value",
- "GO_BACK_LABEL": "Go Back",
- "SEND_MESSAGE_LABEL": "Send Message",
- "FORM_ERROR_MESSAGE": "Please fill all variables before sending"
- }
+ "WHATSAPP_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Whatsapp Templates",
+ "SUBTITLE": "Select the whatsapp template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No WhatsApp templates available. Click refresh to sync templates from WhatsApp.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "OTP_CODE": "Enter 4-8 digit OTP",
+ "EXPIRY_MINUTES": "Enter expiry minutes",
+ "BUTTON_PARAMETERS": "Button Parameters",
+ "BUTTON_LABEL": "Button {index}",
+ "COUPON_CODE": "Enter coupon code (max 15 chars)",
+ "MEDIA_URL_LABEL": "Enter {type} URL",
+ "BUTTON_PARAMETER": "Enter button parameter"
}
+ }
}
diff --git a/app/javascript/dashboard/routes/dashboard/captain/assistants/scenarios/Index.vue b/app/javascript/dashboard/routes/dashboard/captain/assistants/scenarios/Index.vue
index 2ed809367..3175500dd 100644
--- a/app/javascript/dashboard/routes/dashboard/captain/assistants/scenarios/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/captain/assistants/scenarios/Index.vue
@@ -6,6 +6,7 @@ import { picoSearch } from '@scmmishra/pico-search';
import { useStore, useMapGetter } from 'dashboard/composables/store';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
+import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import Button from 'dashboard/components-next/button/Button.vue';
import Input from 'dashboard/components-next/input/Input.vue';
@@ -20,6 +21,7 @@ const { t } = useI18n();
const route = useRoute();
const store = useStore();
const { uiSettings, updateUISettings } = useUISettings();
+const { formatMessage } = useMessageFormatter();
const assistantId = route.params.assistantId;
const uiFlags = useMapGetter('captainScenarios/getUIFlags');
@@ -42,35 +44,25 @@ const breadcrumbItems = computed(() => {
];
});
-const TOOL_LINK_REGEX = /\[([^\]]+)]\(tool:\/\/.+?\)/g;
+const LINK_INSTRUCTION_CLASS =
+ '[&_a[href^="tool://"]]:text-n-iris-11 [&_a:not([href^="tool://"])]:text-n-slate-12 [&_a]:pointer-events-none [&_a]:cursor-default';
const renderInstruction = instruction => () =>
h('span', {
- class: 'text-sm text-n-slate-12 py-4',
- innerHTML: instruction.replace(
- TOOL_LINK_REGEX,
- (_, title) =>
- `
@${title.replace(/^@/, '')}`
- ),
+ class: `text-sm text-n-slate-12 py-4 prose prose-sm min-w-0 break-words ${LINK_INSTRUCTION_CLASS}`,
+ innerHTML: instruction,
});
// Suggested example scenarios for quick add
const scenariosExample = [
{
id: 1,
- title: 'Refund Order',
- description: 'User encountered a technical issue or error message.',
+ title: 'Prospective Buyer',
+ description:
+ 'Handle customers who are showing interest in purchasing a license',
instruction:
- 'Ask for steps to reproduce + browser/app version. Use [Known Issues](tool://known_issues) to check if it’s a known bug. File with [Create Bug Report](tool://bug_report_create) if new.',
- tools: ['create_bug_report', 'known_issues'],
- },
- {
- id: 2,
- title: 'Product Recommendation',
- description: 'User is unsure which product or service to choose.',
- instruction:
- 'Ask 2–3 clarifying questions. Use [Product Match](tool://product_match[user_needs]) and suggest 2–3 options with pros/cons. Link to compare page if available.',
- tools: ['product_match[user_needs]'],
+ 'If someone is interested in purchasing a license, ask them for following:\n\n1. How many licenses are they willing to purchase?\n2. Are they migrating from another platform?\n. Once these details are collected, do the following steps\n1. add a private note to with the information you collected using [Add Private Note](tool://add_private_note)\n2. Add label "sales" to the contact using [Add Label to Conversation](tool://add_label_to_conversation)\n3. Reply saying "one of us will reach out soon" and provide an estimated timeline for the response and [Handoff to Human](tool://handoff)',
+ tools: ['add_private_note', 'add_label_to_conversation', 'handoff'],
},
];
@@ -248,7 +240,9 @@ onMounted(() => {
{{ item.description }}
-
+
{{ t('CAPTAIN.ASSISTANTS.SCENARIOS.ADD.SUGGESTED.TOOLS_USED') }}
{{ item.tools?.map(tool => `@${tool}`).join(', ') }}
diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
index dfa6163d8..0a6905039 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
+++ b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
@@ -468,6 +468,106 @@ export const AUTOMATIONS = {
},
],
},
+ conversation_resolved: {
+ conditions: [
+ {
+ key: 'browser_language',
+ name: 'BROWSER_LANGUAGE',
+ inputType: 'search_select',
+ filterOperators: OPERATOR_TYPES_1,
+ },
+ {
+ key: 'email',
+ name: 'EMAIL',
+ inputType: 'plain_text',
+ filterOperators: OPERATOR_TYPES_2,
+ },
+ {
+ key: 'mail_subject',
+ name: 'MAIL_SUBJECT',
+ inputType: 'plain_text',
+ filterOperators: OPERATOR_TYPES_2,
+ },
+ {
+ key: 'country_code',
+ name: 'COUNTRY_NAME',
+ inputType: 'search_select',
+ filterOperators: OPERATOR_TYPES_1,
+ },
+ {
+ key: 'referer',
+ name: 'REFERER_LINK',
+ inputType: 'plain_text',
+ filterOperators: OPERATOR_TYPES_2,
+ },
+ {
+ key: 'assignee_id',
+ name: 'ASSIGNEE_NAME',
+ inputType: 'search_select',
+ filterOperators: OPERATOR_TYPES_3,
+ },
+ {
+ key: 'phone_number',
+ name: 'PHONE_NUMBER',
+ inputType: 'plain_text',
+ filterOperators: OPERATOR_TYPES_6,
+ },
+ {
+ key: 'team_id',
+ name: 'TEAM_NAME',
+ inputType: 'search_select',
+ filterOperators: OPERATOR_TYPES_3,
+ },
+ {
+ key: 'inbox_id',
+ name: 'INBOX',
+ inputType: 'multi_select',
+ filterOperators: OPERATOR_TYPES_1,
+ },
+ {
+ key: 'conversation_language',
+ name: 'CONVERSATION_LANGUAGE',
+ inputType: 'multi_select',
+ filterOperators: OPERATOR_TYPES_1,
+ },
+ {
+ key: 'priority',
+ name: 'PRIORITY',
+ inputType: 'multi_select',
+ filterOperators: OPERATOR_TYPES_1,
+ },
+ ],
+ actions: [
+ {
+ key: 'assign_agent',
+ name: 'ASSIGN_AGENT',
+ },
+ {
+ key: 'assign_team',
+ name: 'ASSIGN_TEAM',
+ },
+ {
+ key: 'send_email_to_team',
+ name: 'SEND_EMAIL_TO_TEAM',
+ },
+ {
+ key: 'send_message',
+ name: 'SEND_MESSAGE',
+ },
+ {
+ key: 'send_email_transcript',
+ name: 'SEND_EMAIL_TRANSCRIPT',
+ },
+ {
+ key: 'send_webhook_event',
+ name: 'SEND_WEBHOOK_EVENT',
+ },
+ {
+ key: 'send_attachment',
+ name: 'SEND_ATTACHMENT',
+ },
+ ],
+ },
};
export const AUTOMATION_RULE_EVENTS = [
@@ -479,6 +579,10 @@ export const AUTOMATION_RULE_EVENTS = [
key: 'conversation_updated',
value: 'CONVERSATION_UPDATED',
},
+ {
+ key: 'conversation_resolved',
+ value: 'CONVERSATION_RESOLVED',
+ },
{
key: 'message_created',
value: 'MESSAGE_CREATED',
diff --git a/app/javascript/dashboard/store/modules/inboxes.js b/app/javascript/dashboard/store/modules/inboxes.js
index a6b93c923..c4789a7a9 100644
--- a/app/javascript/dashboard/store/modules/inboxes.js
+++ b/app/javascript/dashboard/store/modules/inboxes.js
@@ -44,15 +44,52 @@ export const getters = {
const messagesTemplates =
whatsAppMessageTemplates || apiInboxMessageTemplates;
- // filtering out the whatsapp templates with media
- if (messagesTemplates instanceof Array) {
- return messagesTemplates.filter(template => {
- return !template.components.some(
- i => i.format === 'IMAGE' || i.format === 'VIDEO'
- );
- });
+ return messagesTemplates;
+ },
+ getFilteredWhatsAppTemplates: $state => inboxId => {
+ const [inbox] = $state.records.filter(
+ record => record.id === Number(inboxId)
+ );
+
+ const {
+ message_templates: whatsAppMessageTemplates,
+ additional_attributes: additionalAttributes,
+ } = inbox || {};
+
+ const { message_templates: apiInboxMessageTemplates } =
+ additionalAttributes || {};
+ const templates = whatsAppMessageTemplates || apiInboxMessageTemplates;
+
+ if (!templates || !Array.isArray(templates)) {
+ return [];
}
- return [];
+
+ return templates.filter(template => {
+ // Ensure template has required properties
+ if (!template || !template.status || !template.components) {
+ return false;
+ }
+
+ // Only show approved templates
+ if (template.status.toLowerCase() !== 'approved') {
+ return false;
+ }
+
+ // Filter out interactive templates (LIST, PRODUCT, CATALOG), location templates, and call permission templates
+ const hasUnsupportedComponents = template.components.some(
+ component =>
+ ['LIST', 'PRODUCT', 'CATALOG', 'CALL_PERMISSION_REQUEST'].includes(
+ component.type
+ ) ||
+ (component.type === 'HEADER' && component.format === 'LOCATION')
+ );
+
+ if (hasUnsupportedComponents) {
+ return false;
+ }
+
+ return true;
+ });
},
getNewConversationInboxes($state) {
return $state.records.filter(inbox => {
diff --git a/app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js b/app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js
index f9ed57d63..eeb52b1dc 100644
--- a/app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js
@@ -1,5 +1,6 @@
import { getters } from '../../inboxes';
import inboxList from './fixtures';
+import { templates } from './templateFixtures';
describe('#getters', () => {
it('getInboxes', () => {
@@ -93,4 +94,269 @@ describe('#getters', () => {
provider: 'default',
});
});
+
+ describe('getFilteredWhatsAppTemplates', () => {
+ it('returns empty array when inbox not found', () => {
+ const state = { records: [] };
+ expect(getters.getFilteredWhatsAppTemplates(state)(999)).toEqual([]);
+ });
+
+ it('returns empty array when templates is null or undefined', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: null,
+ additional_attributes: { message_templates: undefined },
+ },
+ ],
+ };
+ expect(getters.getFilteredWhatsAppTemplates(state)(1)).toEqual([]);
+ });
+
+ it('returns empty array when templates is not an array', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: 'invalid',
+ additional_attributes: {},
+ },
+ ],
+ };
+ expect(getters.getFilteredWhatsAppTemplates(state)(1)).toEqual([]);
+ });
+
+ it('filters out templates without required properties', () => {
+ const invalidTemplates = [
+ { name: 'incomplete_template' }, // missing status and components
+ { status: 'approved' }, // missing name and components
+ { name: 'another_incomplete', status: 'approved' }, // missing components
+ ];
+
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: invalidTemplates,
+ },
+ ],
+ };
+ expect(getters.getFilteredWhatsAppTemplates(state)(1)).toEqual([]);
+ });
+
+ it('filters out non-approved templates', () => {
+ const mixedStatusTemplates = [
+ {
+ name: 'pending_template',
+ status: 'pending',
+ components: [{ type: 'BODY', text: 'Test' }],
+ },
+ {
+ name: 'rejected_template',
+ status: 'rejected',
+ components: [{ type: 'BODY', text: 'Test' }],
+ },
+ {
+ name: 'approved_template',
+ status: 'approved',
+ components: [{ type: 'BODY', text: 'Test' }],
+ },
+ ];
+
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: mixedStatusTemplates,
+ },
+ ],
+ };
+
+ const result = getters.getFilteredWhatsAppTemplates(state)(1);
+ expect(result).toHaveLength(1);
+ expect(result[0].name).toBe('approved_template');
+ });
+
+ it('filters out interactive templates (LIST, PRODUCT, CATALOG)', () => {
+ const interactiveTemplates = [
+ {
+ name: 'list_template',
+ status: 'approved',
+ components: [
+ { type: 'BODY', text: 'Choose an option' },
+ { type: 'LIST', sections: [] },
+ ],
+ },
+ {
+ name: 'product_template',
+ status: 'approved',
+ components: [
+ { type: 'BODY', text: 'Product info' },
+ { type: 'PRODUCT', catalog_id: '123' },
+ ],
+ },
+ {
+ name: 'catalog_template',
+ status: 'approved',
+ components: [
+ { type: 'BODY', text: 'Catalog' },
+ { type: 'CATALOG', thumbnail_product_retailer_id: '123' },
+ ],
+ },
+ {
+ name: 'regular_template',
+ status: 'approved',
+ components: [{ type: 'BODY', text: 'Regular message' }],
+ },
+ ];
+
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: interactiveTemplates,
+ },
+ ],
+ };
+
+ const result = getters.getFilteredWhatsAppTemplates(state)(1);
+ expect(result).toHaveLength(1);
+ expect(result[0].name).toBe('regular_template');
+ });
+
+ it('filters out location templates', () => {
+ const locationTemplates = [
+ {
+ name: 'location_template',
+ status: 'approved',
+ components: [
+ { type: 'HEADER', format: 'LOCATION' },
+ { type: 'BODY', text: 'Location message' },
+ ],
+ },
+ {
+ name: 'regular_template',
+ status: 'approved',
+ components: [
+ { type: 'HEADER', format: 'TEXT', text: 'Header' },
+ { type: 'BODY', text: 'Regular message' },
+ ],
+ },
+ ];
+
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: locationTemplates,
+ },
+ ],
+ };
+
+ const result = getters.getFilteredWhatsAppTemplates(state)(1);
+ expect(result).toHaveLength(1);
+ expect(result[0].name).toBe('regular_template');
+ });
+
+ it('returns valid templates from fixture data', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: templates,
+ },
+ ],
+ };
+
+ const result = getters.getFilteredWhatsAppTemplates(state)(1);
+
+ // All templates in fixtures should be approved and valid
+ expect(result.length).toBeGreaterThan(0);
+
+ // Verify all returned templates are approved
+ result.forEach(template => {
+ expect(template.status).toBe('approved');
+ expect(template.components).toBeDefined();
+ expect(Array.isArray(template.components)).toBe(true);
+ });
+
+ // Verify specific templates from fixtures are included
+ const templateNames = result.map(t => t.name);
+ expect(templateNames).toContain('sample_flight_confirmation');
+ expect(templateNames).toContain('sample_issue_resolution');
+ expect(templateNames).toContain('sample_shipping_confirmation');
+ expect(templateNames).toContain('no_variable_template');
+ expect(templateNames).toContain('order_confirmation');
+ });
+
+ it('prioritizes message_templates over additional_attributes.message_templates', () => {
+ const primaryTemplates = [
+ {
+ name: 'primary_template',
+ status: 'approved',
+ components: [{ type: 'BODY', text: 'Primary' }],
+ },
+ ];
+
+ const fallbackTemplates = [
+ {
+ name: 'fallback_template',
+ status: 'approved',
+ components: [{ type: 'BODY', text: 'Fallback' }],
+ },
+ ];
+
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: primaryTemplates,
+ additional_attributes: {
+ message_templates: fallbackTemplates,
+ },
+ },
+ ],
+ };
+
+ const result = getters.getFilteredWhatsAppTemplates(state)(1);
+ expect(result).toHaveLength(1);
+ expect(result[0].name).toBe('primary_template');
+ });
+
+ it('falls back to additional_attributes.message_templates when message_templates is null', () => {
+ const fallbackTemplates = [
+ {
+ name: 'fallback_template',
+ status: 'approved',
+ components: [{ type: 'BODY', text: 'Fallback' }],
+ },
+ ];
+
+ const state = {
+ records: [
+ {
+ id: 1,
+ channel_type: 'Channel::Whatsapp',
+ message_templates: null,
+ additional_attributes: {
+ message_templates: fallbackTemplates,
+ },
+ },
+ ],
+ };
+
+ const result = getters.getFilteredWhatsAppTemplates(state)(1);
+ expect(result).toHaveLength(1);
+ expect(result[0].name).toBe('fallback_template');
+ });
+ });
});
diff --git a/app/javascript/shared/mixins/specs/whatsappTemplates/fixtures.js b/app/javascript/dashboard/store/modules/specs/inboxes/templateFixtures.js
similarity index 50%
rename from app/javascript/shared/mixins/specs/whatsappTemplates/fixtures.js
rename to app/javascript/dashboard/store/modules/specs/inboxes/templateFixtures.js
index 02c24b2bb..c4c6a0b40 100644
--- a/app/javascript/shared/mixins/specs/whatsappTemplates/fixtures.js
+++ b/app/javascript/dashboard/store/modules/specs/inboxes/templateFixtures.js
@@ -260,4 +260,285 @@ export const templates = [
],
rejected_reason: 'NONE',
},
+ {
+ name: 'order_confirmation',
+ status: 'approved',
+ category: 'TICKET_UPDATE',
+ language: 'en_US',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ type: 'HEADER',
+ format: 'IMAGE',
+ example: {
+ header_handle: ['https://example.com/shoes.jpg'],
+ },
+ },
+ {
+ text: 'Hi your order {{1}} is confirmed. Please wait for further updates',
+ type: 'BODY',
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'technician_visit',
+ status: 'approved',
+ category: 'UTILITY',
+ language: 'en_US',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: 'Technician visit',
+ type: 'HEADER',
+ format: 'TEXT',
+ },
+ {
+ text: "Hi {{1}}, we're scheduling a technician visit to {{2}} on {{3}} between {{4}} and {{5}}. Please confirm if this time slot works for you.",
+ type: 'BODY',
+ },
+ {
+ type: 'BUTTONS',
+ buttons: [
+ {
+ text: 'Confirm',
+ type: 'QUICK_REPLY',
+ },
+ {
+ text: 'Reschedule',
+ type: 'QUICK_REPLY',
+ },
+ ],
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'event_invitation_static',
+ status: 'approved',
+ category: 'MARKETING',
+ language: 'en',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: "You're invited to {{event_name}} at {{location}}, Join us for an amazing experience!",
+ type: 'BODY',
+ },
+ {
+ type: 'BUTTONS',
+ buttons: [
+ {
+ url: 'https://events.example.com/register',
+ text: 'Visit website',
+ type: 'URL',
+ },
+ {
+ url: 'https://maps.app.goo.gl/YoWAzRj1GDuxs6qz8',
+ text: 'Get Directions',
+ type: 'URL',
+ },
+ ],
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'purchase_receipt',
+ status: 'approved',
+ category: 'UTILITY',
+ language: 'en_US',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ type: 'HEADER',
+ format: 'DOCUMENT',
+ },
+ {
+ text: 'Thank you for using your {{1}} card at {{2}}. Your {{3}} is attached as a PDF.',
+ type: 'BODY',
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'discount_coupon',
+ status: 'approved',
+ category: 'MARKETING',
+ language: 'en',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: '🎉 Special offer for you! Get {{discount_percentage}}% off your next purchase. Use the code below at checkout',
+ type: 'BODY',
+ },
+ {
+ type: 'BUTTONS',
+ buttons: [
+ {
+ text: 'Copy offer code',
+ type: 'COPY_CODE',
+ },
+ ],
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'support_callback',
+ status: 'approved',
+ category: 'UTILITY',
+ language: 'en',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: 'Hello {{name}}, our support team will call you regarding ticket # {{ticket_id}}.',
+ type: 'BODY',
+ },
+ {
+ type: 'BUTTONS',
+ buttons: [
+ {
+ text: 'Call Support',
+ type: 'PHONE_NUMBER',
+ phone_number: '+16506677566',
+ },
+ ],
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'training_video',
+ status: 'approved',
+ category: 'MARKETING',
+ language: 'en',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ type: 'HEADER',
+ format: 'VIDEO',
+ },
+ {
+ text: "Hi {{name}}, here's your training video. Please watch by{{date}}.",
+ type: 'BODY',
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'product_launch',
+ status: 'approved',
+ category: 'MARKETING',
+ language: 'en',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ type: 'HEADER',
+ format: 'IMAGE',
+ },
+ {
+ text: 'New arrival! Our stunning coat now available in {{color}} color.',
+ type: 'BODY',
+ },
+ {
+ text: 'Free shipping on orders over $100. Limited time offer.',
+ type: 'FOOTER',
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'greet',
+ status: 'approved',
+ category: 'MARKETING',
+ language: 'en',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: 'Hey {{customer_name}} how may I help you?',
+ type: 'BODY',
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'hello_world',
+ status: 'approved',
+ category: 'UTILITY',
+ language: 'en_US',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: 'Hello World',
+ type: 'HEADER',
+ format: 'TEXT',
+ },
+ {
+ text: 'Welcome and congratulations!! This message demonstrates your ability to send a WhatsApp message notification from the Cloud API, hosted by Meta. Thank you for taking the time to test with us.',
+ type: 'BODY',
+ },
+ {
+ text: 'WhatsApp Business Platform sample message',
+ type: 'FOOTER',
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'feedback_request',
+ status: 'approved',
+ category: 'MARKETING',
+ language: 'en',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: "Hey {{name}}, how was your experience with Puma? We'd love your feedback!",
+ type: 'BODY',
+ },
+ {
+ type: 'BUTTONS',
+ buttons: [
+ {
+ url: 'https://feedback.example.com/survey',
+ text: 'Leave Feedback',
+ type: 'URL',
+ },
+ ],
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'address_update',
+ status: 'approved',
+ category: 'UTILITY',
+ language: 'en_US',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: 'Address update',
+ type: 'HEADER',
+ format: 'TEXT',
+ },
+ {
+ text: 'Hi {{1}}, your delivery address has been successfully updated to {{2}}. Contact {{3}} for any inquiries.',
+ type: 'BODY',
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
+ {
+ name: 'delivery_confirmation',
+ status: 'approved',
+ category: 'UTILITY',
+ language: 'en_US',
+ namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ components: [
+ {
+ text: '{{1}}, your order was successfully delivered on {{2}}.\n\nThank you for your purchase.\n',
+ type: 'BODY',
+ },
+ ],
+ rejected_reason: 'NONE',
+ },
];
diff --git a/app/javascript/sdk/IFrameHelper.js b/app/javascript/sdk/IFrameHelper.js
index 3ab1ba1f5..25f90912d 100644
--- a/app/javascript/sdk/IFrameHelper.js
+++ b/app/javascript/sdk/IFrameHelper.js
@@ -50,11 +50,35 @@ const updateCampaignReadStatus = baseDomain => {
});
};
+const sanitizeURL = url => {
+ if (url === '') return '';
+
+ try {
+ // any invalid url will not be accepted
+ // example - JaVaScRiP%0at:alert(document.domain)"
+ // this has an obfuscated javascript protocol
+ const parsedURL = new URL(url);
+
+ // filter out dangerous protocols like `javascript`, `data`, `vbscript`
+ if (!['https', 'http'].includes(parsedURL.protocol)) {
+ throw new Error('Invalid Protocol');
+ }
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.error('Invalid URL', e);
+ }
+
+ return 'about:blank'; // blank page URL
+};
+
export const IFrameHelper = {
getUrl({ baseUrl, websiteToken }) {
+ baseUrl = sanitizeURL(baseUrl);
return `${baseUrl}/widget?website_token=${websiteToken}`;
},
createFrame: ({ baseUrl, websiteToken }) => {
+ baseUrl = sanitizeURL(baseUrl);
+
if (IFrameHelper.getAppFrame()) {
return;
}
@@ -102,10 +126,12 @@ export const IFrameHelper = {
window.onmessage = e => {
if (
typeof e.data !== 'string' ||
- e.data.indexOf('chatwoot-widget:') !== 0
+ e.data.indexOf('chatwoot-widget:') !== 0 ||
+ e.origin !== window.location.origin
) {
return;
}
+
const message = JSON.parse(e.data.replace('chatwoot-widget:', ''));
if (typeof IFrameHelper.events[message.event] === 'function') {
IFrameHelper.events[message.event](message);
@@ -140,7 +166,9 @@ export const IFrameHelper = {
},
setupAudioListeners: () => {
- const { baseUrl = '' } = window.$chatwoot;
+ let { baseUrl = '' } = window.$chatwoot;
+ baseUrl = sanitizeURL(baseUrl);
+
getAlertAudio(baseUrl, { type: 'widget', alertTone: 'ding' }).then(() =>
initOnEvents.forEach(event => {
document.removeEventListener(
@@ -234,6 +262,7 @@ export const IFrameHelper = {
},
popoutChatWindow: ({ baseUrl, websiteToken, locale }) => {
+ baseUrl = sanitizeURL(baseUrl);
const cwCookie = Cookies.get('cw_conversation');
window.$chatwoot.toggle('close');
popoutChatWindow(baseUrl, websiteToken, locale, cwCookie);
diff --git a/app/javascript/shared/mixins/specs/whatsappTemplates/whatsappTemplates.spec.js b/app/javascript/shared/mixins/specs/whatsappTemplates/whatsappTemplates.spec.js
deleted file mode 100644
index adad15bf7..000000000
--- a/app/javascript/shared/mixins/specs/whatsappTemplates/whatsappTemplates.spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import TemplateParser from '../../../../dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue';
-import { templates } from './fixtures';
-import { nextTick } from 'vue';
-
-const config = {
- global: {
- stubs: {
- NextButton: { template: '' },
- WootInput: { template: '' },
- },
- },
-};
-
-describe('#WhatsAppTemplates', () => {
- it('returns all variables from a template string', async () => {
- const wrapper = shallowMount(TemplateParser, {
- ...config,
- props: { template: templates[0] },
- });
- await nextTick();
- expect(wrapper.vm.variables).toEqual(['{{1}}', '{{2}}', '{{3}}']);
- });
-
- it('returns no variables from a template string if it does not contain variables', async () => {
- const wrapper = shallowMount(TemplateParser, {
- ...config,
- props: { template: templates[12] },
- });
- await nextTick();
- expect(wrapper.vm.variables).toBeNull();
- });
-
- it('returns the body of a template', async () => {
- const wrapper = shallowMount(TemplateParser, {
- ...config,
- props: { template: templates[1] },
- });
- await nextTick();
- const expectedOutput =
- templates[1].components.find(i => i.type === 'BODY')?.text || '';
- expect(wrapper.vm.templateString).toEqual(expectedOutput);
- });
-
- it('generates the templates from variable input', async () => {
- const wrapper = shallowMount(TemplateParser, {
- ...config,
- props: { template: templates[0] },
- });
- await nextTick();
-
- // Instead of using `setData`, directly modify the `processedParams` using the component's logic
- await wrapper.vm.$nextTick();
- wrapper.vm.processedParams = { 1: 'abc', 2: 'xyz', 3: 'qwerty' };
- await wrapper.vm.$nextTick();
-
- const expectedOutput =
- 'Esta é a sua confirmação de voo para abc-xyz em qwerty.';
- expect(wrapper.vm.processedString).toEqual(expectedOutput);
- });
-});
diff --git a/app/javascript/survey/i18n/index.js b/app/javascript/survey/i18n/index.js
index 372017c5e..1a9ea1a59 100644
--- a/app/javascript/survey/i18n/index.js
+++ b/app/javascript/survey/i18n/index.js
@@ -1,4 +1,5 @@
import ar from './locale/ar.json';
+import bg from './locale/bg.json';
import ca from './locale/ca.json';
import cs from './locale/cs.json';
import da from './locale/da.json';
@@ -40,6 +41,7 @@ import zh_TW from './locale/zh_TW.json';
export default {
ar,
+ bg,
ca,
cs,
da,
diff --git a/app/javascript/widget/i18n/index.js b/app/javascript/widget/i18n/index.js
index 8e602a4b5..93a371391 100644
--- a/app/javascript/widget/i18n/index.js
+++ b/app/javascript/widget/i18n/index.js
@@ -1,4 +1,5 @@
import ar from './locale/ar.json';
+import bg from './locale/bg.json';
import ca from './locale/ca.json';
import cs from './locale/cs.json';
import da from './locale/da.json';
@@ -40,6 +41,7 @@ import zh_TW from './locale/zh_TW.json';
export default {
ar,
+ bg,
ca,
cs,
da,
diff --git a/app/javascript/widget/i18n/locale/am.json b/app/javascript/widget/i18n/locale/am.json
index 279ead47e..4f244c566 100644
--- a/app/javascript/widget/i18n/locale/am.json
+++ b/app/javascript/widget/i18n/locale/am.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/ar.json b/app/javascript/widget/i18n/locale/ar.json
index 66f271cdd..21b1c072e 100644
--- a/app/javascript/widget/i18n/locale/ar.json
+++ b/app/javascript/widget/i18n/locale/ar.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "تعذر الإرسال! حاول مرة أخرى"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "متواجدون لخدمتك",
"OFFLINE": "نحن بعيدون في الوقت الحالي"
diff --git a/app/javascript/widget/i18n/locale/az.json b/app/javascript/widget/i18n/locale/az.json
index 279ead47e..4f244c566 100644
--- a/app/javascript/widget/i18n/locale/az.json
+++ b/app/javascript/widget/i18n/locale/az.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/bg.json b/app/javascript/widget/i18n/locale/bg.json
index f289bf5eb..1fd4bc35d 100644
--- a/app/javascript/widget/i18n/locale/bg.json
+++ b/app/javascript/widget/i18n/locale/bg.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "На линия сме",
"OFFLINE": "В момента не сме на линия"
diff --git a/app/javascript/widget/i18n/locale/ca.json b/app/javascript/widget/i18n/locale/ca.json
index 4e464a6eb..6ae69fcb6 100644
--- a/app/javascript/widget/i18n/locale/ca.json
+++ b/app/javascript/widget/i18n/locale/ca.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "No s'ha pogut enviar, torna-ho a provar"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Estem en línia",
"OFFLINE": "Estem fora en aquest moment"
diff --git a/app/javascript/widget/i18n/locale/cs.json b/app/javascript/widget/i18n/locale/cs.json
index e33d963f1..adefb1eb4 100644
--- a/app/javascript/widget/i18n/locale/cs.json
+++ b/app/javascript/widget/i18n/locale/cs.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Odeslání se nezdařilo, zkuste to znovu"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Jsme online",
"OFFLINE": "V současné době jsme pryč"
diff --git a/app/javascript/widget/i18n/locale/da.json b/app/javascript/widget/i18n/locale/da.json
index 0b5cb5722..31303d4d5 100644
--- a/app/javascript/widget/i18n/locale/da.json
+++ b/app/javascript/widget/i18n/locale/da.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Kunne ikke sende, prøv igen"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Vi er online",
"OFFLINE": "Vi er ikke tilgængelige i øjeblikket"
diff --git a/app/javascript/widget/i18n/locale/de.json b/app/javascript/widget/i18n/locale/de.json
index 7508c1f77..b7490c368 100644
--- a/app/javascript/widget/i18n/locale/de.json
+++ b/app/javascript/widget/i18n/locale/de.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Senden nicht möglich, versuchen Sie es noch einmal"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Wir sind online",
"OFFLINE": "Wir sind momentan abwesend"
diff --git a/app/javascript/widget/i18n/locale/el.json b/app/javascript/widget/i18n/locale/el.json
index 5ca095053..1cb35a76b 100644
--- a/app/javascript/widget/i18n/locale/el.json
+++ b/app/javascript/widget/i18n/locale/el.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Αδυναμία αποστολής! Προσπαθήστε ξανά"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Είμαστε online",
"OFFLINE": "Προς το παρόν, είμαστε εκτός"
diff --git a/app/javascript/widget/i18n/locale/es.json b/app/javascript/widget/i18n/locale/es.json
index 62837335b..9c807ed84 100644
--- a/app/javascript/widget/i18n/locale/es.json
+++ b/app/javascript/widget/i18n/locale/es.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "¡No se pudo enviar! intente nuevamente"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Estamos en línea",
"OFFLINE": "Estamos ausentes en este momento"
diff --git a/app/javascript/widget/i18n/locale/fa.json b/app/javascript/widget/i18n/locale/fa.json
index bd1854fc5..982df19ac 100644
--- a/app/javascript/widget/i18n/locale/fa.json
+++ b/app/javascript/widget/i18n/locale/fa.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "ارسال نشد، دوباره امتحان کنید"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "ما آنلاین هستیم",
"OFFLINE": "در حال حاضر دردسترس نیستیم"
diff --git a/app/javascript/widget/i18n/locale/fi.json b/app/javascript/widget/i18n/locale/fi.json
index 59b827bac..a5d742303 100644
--- a/app/javascript/widget/i18n/locale/fi.json
+++ b/app/javascript/widget/i18n/locale/fi.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Ei voitu lähettää, yritä uudestaan"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Olemme online-tilassa",
"OFFLINE": "Olemme tällä hetkellä poissa"
diff --git a/app/javascript/widget/i18n/locale/fr.json b/app/javascript/widget/i18n/locale/fr.json
index 4552a55cc..ff25bbd45 100644
--- a/app/javascript/widget/i18n/locale/fr.json
+++ b/app/javascript/widget/i18n/locale/fr.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Échec de l'envoi, veuillez réessayer"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Nous sommes en ligne",
"OFFLINE": "Nous sommes absents pour le moment"
diff --git a/app/javascript/widget/i18n/locale/he.json b/app/javascript/widget/i18n/locale/he.json
index 8b235cffc..1eaa103db 100644
--- a/app/javascript/widget/i18n/locale/he.json
+++ b/app/javascript/widget/i18n/locale/he.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "לא ניתן לשלוח, נסה שוב"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "אנחנו אונליין",
"OFFLINE": "אנחנו לא זמינים כרגע"
diff --git a/app/javascript/widget/i18n/locale/hi.json b/app/javascript/widget/i18n/locale/hi.json
index 279ead47e..4f244c566 100644
--- a/app/javascript/widget/i18n/locale/hi.json
+++ b/app/javascript/widget/i18n/locale/hi.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/hr.json b/app/javascript/widget/i18n/locale/hr.json
index 7d468f407..37cf7b53e 100644
--- a/app/javascript/widget/i18n/locale/hr.json
+++ b/app/javascript/widget/i18n/locale/hr.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Nismo uspjeli poslati, pokušajte ponovno"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Online smo",
"OFFLINE": "Trenutačno nismo online"
diff --git a/app/javascript/widget/i18n/locale/hu.json b/app/javascript/widget/i18n/locale/hu.json
index 578639c1e..81c4b6886 100644
--- a/app/javascript/widget/i18n/locale/hu.json
+++ b/app/javascript/widget/i18n/locale/hu.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Nem sikerült az elküldés, kérjük próbáld később"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Online vagyunk",
"OFFLINE": "Jelenleg nem vagyunk elérhetőek"
diff --git a/app/javascript/widget/i18n/locale/hy.json b/app/javascript/widget/i18n/locale/hy.json
index 279ead47e..4f244c566 100644
--- a/app/javascript/widget/i18n/locale/hy.json
+++ b/app/javascript/widget/i18n/locale/hy.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/id.json b/app/javascript/widget/i18n/locale/id.json
index 363fff802..e7fbec8ad 100644
--- a/app/javascript/widget/i18n/locale/id.json
+++ b/app/javascript/widget/i18n/locale/id.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Tidak dapat mengirim, coba lagi"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Kami sedang online",
"OFFLINE": "Kami sedang tidak tersedia saat ini"
diff --git a/app/javascript/widget/i18n/locale/is.json b/app/javascript/widget/i18n/locale/is.json
index f2d295418..b794493f4 100644
--- a/app/javascript/widget/i18n/locale/is.json
+++ b/app/javascript/widget/i18n/locale/is.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Tókst ekki að senda skilaboðin, reyndu aftur"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Við erum tengd",
"OFFLINE": "Það er enginn við í augnablikinu"
diff --git a/app/javascript/widget/i18n/locale/it.json b/app/javascript/widget/i18n/locale/it.json
index 0e58af2de..7e0385220 100644
--- a/app/javascript/widget/i18n/locale/it.json
+++ b/app/javascript/widget/i18n/locale/it.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Impossibile inviare, riprova"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Siamo online",
"OFFLINE": "Siamo offline in questo momento"
diff --git a/app/javascript/widget/i18n/locale/ja.json b/app/javascript/widget/i18n/locale/ja.json
index fa97a8d63..17c4beae0 100644
--- a/app/javascript/widget/i18n/locale/ja.json
+++ b/app/javascript/widget/i18n/locale/ja.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "送信できませんでした。もう一度お試しください"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "オンライン",
"OFFLINE": "留守中"
diff --git a/app/javascript/widget/i18n/locale/ka.json b/app/javascript/widget/i18n/locale/ka.json
index 279ead47e..4f244c566 100644
--- a/app/javascript/widget/i18n/locale/ka.json
+++ b/app/javascript/widget/i18n/locale/ka.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/ko.json b/app/javascript/widget/i18n/locale/ko.json
index f1c189070..ef8d15a6e 100644
--- a/app/javascript/widget/i18n/locale/ko.json
+++ b/app/javascript/widget/i18n/locale/ko.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "전송하지 못했습니다. 다시 시도해보세요."
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "온라인",
"OFFLINE": "부재중"
diff --git a/app/javascript/widget/i18n/locale/lt.json b/app/javascript/widget/i18n/locale/lt.json
index 98b89da99..fb61b586e 100644
--- a/app/javascript/widget/i18n/locale/lt.json
+++ b/app/javascript/widget/i18n/locale/lt.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Nepavyko išsiųsti! bandykite dar kartą"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Mes prisijungę",
"OFFLINE": "Šiuo metu esame atsijungę"
diff --git a/app/javascript/widget/i18n/locale/lv.json b/app/javascript/widget/i18n/locale/lv.json
index 19ca42914..5262e87f7 100644
--- a/app/javascript/widget/i18n/locale/lv.json
+++ b/app/javascript/widget/i18n/locale/lv.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Nevarēja nosūtīt. Lūdzu, mēģiniet vēlreiz"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Mēs esam tiešsaistē",
"OFFLINE": "Šobrīd mēs neesam uz vietas"
diff --git a/app/javascript/widget/i18n/locale/ml.json b/app/javascript/widget/i18n/locale/ml.json
index c1071b35a..c4761e99f 100644
--- a/app/javascript/widget/i18n/locale/ml.json
+++ b/app/javascript/widget/i18n/locale/ml.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "അയയ്ക്കാനായില്ല, വീണ്ടും ശ്രമിക്കുക"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "ഞങ്ങൾ ഓൺലൈനിലാണ്",
"OFFLINE": "ഞങ്ങൾ ഇപ്പോൾ അകലെയാണ്"
diff --git a/app/javascript/widget/i18n/locale/ms.json b/app/javascript/widget/i18n/locale/ms.json
index 78747073d..4dcbc5f57 100644
--- a/app/javascript/widget/i18n/locale/ms.json
+++ b/app/javascript/widget/i18n/locale/ms.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/ne.json b/app/javascript/widget/i18n/locale/ne.json
index a6041a758..5201951e5 100644
--- a/app/javascript/widget/i18n/locale/ne.json
+++ b/app/javascript/widget/i18n/locale/ne.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "हामी अनलाइन छौं",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/nl.json b/app/javascript/widget/i18n/locale/nl.json
index 48f62678d..a33be5f90 100644
--- a/app/javascript/widget/i18n/locale/nl.json
+++ b/app/javascript/widget/i18n/locale/nl.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Verzenden mislukt, probeer het opnieuw"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We zijn online",
"OFFLINE": "We zijn momenteel afwezig"
diff --git a/app/javascript/widget/i18n/locale/no.json b/app/javascript/widget/i18n/locale/no.json
index f9d46a712..fa2c5a2b2 100644
--- a/app/javascript/widget/i18n/locale/no.json
+++ b/app/javascript/widget/i18n/locale/no.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Kunne ikke sende, prøv på nytt"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Vi er pålogget",
"OFFLINE": "Vi er for øyeblikket borte"
diff --git a/app/javascript/widget/i18n/locale/pl.json b/app/javascript/widget/i18n/locale/pl.json
index 704eb0567..c143e3b00 100644
--- a/app/javascript/widget/i18n/locale/pl.json
+++ b/app/javascript/widget/i18n/locale/pl.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Nie udało się wysłać! Spróbuj ponownie"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Jesteśmy dostępni",
"OFFLINE": "W tej chwili jesteśmy niedostępni"
diff --git a/app/javascript/widget/i18n/locale/pt.json b/app/javascript/widget/i18n/locale/pt.json
index fd320fbf9..c1fffb681 100644
--- a/app/javascript/widget/i18n/locale/pt.json
+++ b/app/javascript/widget/i18n/locale/pt.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Não foi possível enviar, tente novamente"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Estamos online",
"OFFLINE": "Estamos ausentes"
diff --git a/app/javascript/widget/i18n/locale/pt_BR.json b/app/javascript/widget/i18n/locale/pt_BR.json
index c3dde21aa..f979fddca 100644
--- a/app/javascript/widget/i18n/locale/pt_BR.json
+++ b/app/javascript/widget/i18n/locale/pt_BR.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Não foi possível enviar, tente novamente"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Estamos conectados",
"OFFLINE": "Estamos ausentes no momento"
diff --git a/app/javascript/widget/i18n/locale/ro.json b/app/javascript/widget/i18n/locale/ro.json
index 35150d67a..e030c50b2 100644
--- a/app/javascript/widget/i18n/locale/ro.json
+++ b/app/javascript/widget/i18n/locale/ro.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Nu s-a putut trimite! Încearcă din nou"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Suntem online",
"OFFLINE": "Suntem plecați în acest moment"
diff --git a/app/javascript/widget/i18n/locale/ru.json b/app/javascript/widget/i18n/locale/ru.json
index de468106d..734a3a035 100644
--- a/app/javascript/widget/i18n/locale/ru.json
+++ b/app/javascript/widget/i18n/locale/ru.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Не удалось отправить! Попробуйте еще раз"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Мы в сети",
"OFFLINE": "В данный момент мы отсутствуем"
diff --git a/app/javascript/widget/i18n/locale/sh.json b/app/javascript/widget/i18n/locale/sh.json
index 279ead47e..4f244c566 100644
--- a/app/javascript/widget/i18n/locale/sh.json
+++ b/app/javascript/widget/i18n/locale/sh.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/sk.json b/app/javascript/widget/i18n/locale/sk.json
index e61b9a498..60dc03162 100644
--- a/app/javascript/widget/i18n/locale/sk.json
+++ b/app/javascript/widget/i18n/locale/sk.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Sme online",
"OFFLINE": "Momentálne nie sme k dispozícii"
diff --git a/app/javascript/widget/i18n/locale/sl.json b/app/javascript/widget/i18n/locale/sl.json
index 365132414..8665e1e1b 100644
--- a/app/javascript/widget/i18n/locale/sl.json
+++ b/app/javascript/widget/i18n/locale/sl.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Ni bilo mogoče poslati, poskusite znova"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Smo na voljo",
"OFFLINE": "Trenutno nismo na voljo"
diff --git a/app/javascript/widget/i18n/locale/sq.json b/app/javascript/widget/i18n/locale/sq.json
index 994317343..34fe645e9 100644
--- a/app/javascript/widget/i18n/locale/sq.json
+++ b/app/javascript/widget/i18n/locale/sq.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Nuk mund të dërgohej, provo sërish"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Ne jemi online",
"OFFLINE": "Nuk jemi online për momentin"
diff --git a/app/javascript/widget/i18n/locale/sr.json b/app/javascript/widget/i18n/locale/sr.json
index 8ac036a95..039982ee6 100644
--- a/app/javascript/widget/i18n/locale/sr.json
+++ b/app/javascript/widget/i18n/locale/sr.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Slanje neuspešno, pokušajte ponovo"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Dostupni smo",
"OFFLINE": "Trenutno nismo dostupni"
diff --git a/app/javascript/widget/i18n/locale/sv.json b/app/javascript/widget/i18n/locale/sv.json
index 91e6c682f..d65e39358 100644
--- a/app/javascript/widget/i18n/locale/sv.json
+++ b/app/javascript/widget/i18n/locale/sv.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Kunde inte skicka, försök igen"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Vi är online",
"OFFLINE": "Vi är borta för tillfället"
diff --git a/app/javascript/widget/i18n/locale/ta.json b/app/javascript/widget/i18n/locale/ta.json
index 5cb9152f9..27e069ea7 100644
--- a/app/javascript/widget/i18n/locale/ta.json
+++ b/app/javascript/widget/i18n/locale/ta.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/th.json b/app/javascript/widget/i18n/locale/th.json
index f562571fe..a1e39c2f5 100644
--- a/app/javascript/widget/i18n/locale/th.json
+++ b/app/javascript/widget/i18n/locale/th.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "ไม่สามารถส่งได้ ลองอีกครั้ง"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "เรากำลังออนไลน์",
"OFFLINE": "เราไม่อยู่"
diff --git a/app/javascript/widget/i18n/locale/tl.json b/app/javascript/widget/i18n/locale/tl.json
index 279ead47e..4f244c566 100644
--- a/app/javascript/widget/i18n/locale/tl.json
+++ b/app/javascript/widget/i18n/locale/tl.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/tr.json b/app/javascript/widget/i18n/locale/tr.json
index be37c488b..b346504fe 100644
--- a/app/javascript/widget/i18n/locale/tr.json
+++ b/app/javascript/widget/i18n/locale/tr.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Gönderilemedi, tekrar deneyin"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Çevrimiçi",
"OFFLINE": "Şu an operatörlerimiz müsait değil"
diff --git a/app/javascript/widget/i18n/locale/uk.json b/app/javascript/widget/i18n/locale/uk.json
index 679761f6a..aed413394 100644
--- a/app/javascript/widget/i18n/locale/uk.json
+++ b/app/javascript/widget/i18n/locale/uk.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Не вдалося надіслати, спробуйте ще раз"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Ми онлайн",
"OFFLINE": "Нас наразі немає"
diff --git a/app/javascript/widget/i18n/locale/ur.json b/app/javascript/widget/i18n/locale/ur.json
index 8e3c7687c..3869ab5d6 100644
--- a/app/javascript/widget/i18n/locale/ur.json
+++ b/app/javascript/widget/i18n/locale/ur.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/ur_IN.json b/app/javascript/widget/i18n/locale/ur_IN.json
index 279ead47e..4f244c566 100644
--- a/app/javascript/widget/i18n/locale/ur_IN.json
+++ b/app/javascript/widget/i18n/locale/ur_IN.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Couldn't send, try again"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "We are online",
"OFFLINE": "We are away at the moment"
diff --git a/app/javascript/widget/i18n/locale/vi.json b/app/javascript/widget/i18n/locale/vi.json
index 952a16f1f..d12dbc8db 100644
--- a/app/javascript/widget/i18n/locale/vi.json
+++ b/app/javascript/widget/i18n/locale/vi.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "Không thể gửi, xin thử lại"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "Chúng tôi đang trực tuyến",
"OFFLINE": "Hiện tại chúng tôi đang bận chút"
diff --git a/app/javascript/widget/i18n/locale/zh_CN.json b/app/javascript/widget/i18n/locale/zh_CN.json
index f7d27c267..dd834af87 100644
--- a/app/javascript/widget/i18n/locale/zh_CN.json
+++ b/app/javascript/widget/i18n/locale/zh_CN.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "无法发送,请重试"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "在线",
"OFFLINE": "当前已离线"
diff --git a/app/javascript/widget/i18n/locale/zh_TW.json b/app/javascript/widget/i18n/locale/zh_TW.json
index 85f08a135..3f46d258f 100644
--- a/app/javascript/widget/i18n/locale/zh_TW.json
+++ b/app/javascript/widget/i18n/locale/zh_TW.json
@@ -12,6 +12,11 @@
"ERROR_MESSAGE": "無法傳送!請重新嘗試。"
}
},
+ "THUMBNAIL": {
+ "AUTHOR": {
+ "NOT_AVAILABLE": "Not available"
+ }
+ },
"TEAM_AVAILABILITY": {
"ONLINE": "我們在線上",
"OFFLINE": "我們目前不在線上"
diff --git a/app/listeners/automation_rule_listener.rb b/app/listeners/automation_rule_listener.rb
index 6974e227a..0515d6952 100644
--- a/app/listeners/automation_rule_listener.rb
+++ b/app/listeners/automation_rule_listener.rb
@@ -1,53 +1,18 @@
class AutomationRuleListener < BaseListener
def conversation_updated(event)
- return if performed_by_automation?(event)
-
- conversation = event.data[:conversation]
- account = conversation.account
- changed_attributes = event.data[:changed_attributes]
-
- return unless rule_present?('conversation_updated', account)
-
- rules = current_account_rules('conversation_updated', account)
-
- rules.each do |rule|
- conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform
- AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
- end
+ process_conversation_event(event, 'conversation_updated')
end
def conversation_created(event)
- return if performed_by_automation?(event) || ignore_auto_reply_event?(event)
-
- conversation = event.data[:conversation]
- account = conversation.account
- changed_attributes = event.data[:changed_attributes]
-
- return unless rule_present?('conversation_created', account)
-
- rules = current_account_rules('conversation_created', account)
-
- rules.each do |rule|
- conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform
- ::AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
- end
+ process_conversation_event(event, 'conversation_created')
end
def conversation_opened(event)
- return if performed_by_automation?(event) || ignore_auto_reply_event?(event)
+ process_conversation_event(event, 'conversation_opened')
+ end
- conversation = event.data[:conversation]
- account = conversation.account
- changed_attributes = event.data[:changed_attributes]
-
- return unless rule_present?('conversation_opened', account)
-
- rules = current_account_rules('conversation_opened', account)
-
- rules.each do |rule|
- conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform
- AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
- end
+ def conversation_resolved(event)
+ process_conversation_event(event, 'conversation_resolved')
end
def message_created(event)
@@ -69,6 +34,28 @@ class AutomationRuleListener < BaseListener
end
end
+ private
+
+ def process_conversation_event(event, event_name)
+ return if performed_by_automation?(event)
+
+ auto_reply_skip_events = %w[conversation_created conversation_opened]
+ return if auto_reply_skip_events.include?(event_name) && ignore_auto_reply_event?(event)
+
+ conversation = event.data[:conversation]
+ account = conversation.account
+ changed_attributes = event.data[:changed_attributes]
+
+ return unless rule_present?(event_name, account)
+
+ rules = current_account_rules(event_name, account)
+
+ rules.each do |rule|
+ conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform
+ AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
+ end
+ end
+
def rule_present?(event_name, account)
return if account.blank?
diff --git a/app/listeners/reporting_event_listener.rb b/app/listeners/reporting_event_listener.rb
index 9f22fe8de..9f683a97f 100644
--- a/app/listeners/reporting_event_listener.rb
+++ b/app/listeners/reporting_event_listener.rb
@@ -90,8 +90,47 @@ class ReportingEventListener < BaseListener
reporting_event.save!
end
+ def conversation_opened(event)
+ conversation = extract_conversation_and_account(event)[0]
+
+ # Find the most recent resolved event for this conversation
+ last_resolved_event = ReportingEvent.where(
+ conversation_id: conversation.id,
+ name: 'conversation_resolved'
+ ).order(event_end_time: :desc).first
+
+ # For first-time openings, value is 0
+ # For reopenings, calculate time since resolution
+ if last_resolved_event
+ time_since_resolved = conversation.updated_at.to_i - last_resolved_event.event_end_time.to_i
+ business_hours_value = business_hours(conversation.inbox, last_resolved_event.event_end_time, conversation.updated_at)
+ start_time = last_resolved_event.event_end_time
+ else
+ time_since_resolved = 0
+ business_hours_value = 0
+ start_time = conversation.created_at
+ end
+
+ create_conversation_opened_event(conversation, time_since_resolved, business_hours_value, start_time)
+ end
+
private
+ def create_conversation_opened_event(conversation, time_since_resolved, business_hours_value, start_time)
+ reporting_event = ReportingEvent.new(
+ name: 'conversation_opened',
+ value: time_since_resolved,
+ value_in_business_hours: business_hours_value,
+ account_id: conversation.account_id,
+ inbox_id: conversation.inbox_id,
+ user_id: conversation.assignee_id,
+ conversation_id: conversation.id,
+ event_start_time: start_time,
+ event_end_time: conversation.updated_at
+ )
+ reporting_event.save!
+ end
+
def create_bot_resolved_event(conversation, reporting_event)
return unless conversation.inbox.active_bot?
# We don't want to create a bot_resolved event if there is user interaction on the conversation
diff --git a/app/models/account.rb b/app/models/account.rb
index f8eb998f0..b84d7f526 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -61,6 +61,7 @@ class Account < ApplicationRecord
has_many :agent_bots, dependent: :destroy_async
has_many :api_channels, dependent: :destroy_async, class_name: '::Channel::Api'
has_many :articles, dependent: :destroy_async, class_name: '::Article'
+ has_many :assignment_policies, dependent: :destroy_async
has_many :automation_rules, dependent: :destroy_async
has_many :macros, dependent: :destroy_async
has_many :campaigns, dependent: :destroy_async
diff --git a/app/models/assignment_policy.rb b/app/models/assignment_policy.rb
new file mode 100644
index 000000000..c01ab91c4
--- /dev/null
+++ b/app/models/assignment_policy.rb
@@ -0,0 +1,37 @@
+# == Schema Information
+#
+# Table name: assignment_policies
+#
+# id :bigint not null, primary key
+# assignment_order :integer default(0), not null
+# conversation_priority :integer default("earliest_created"), not null
+# description :text
+# enabled :boolean default(TRUE), not null
+# fair_distribution_limit :integer default(100), not null
+# fair_distribution_window :integer default(3600), not null
+# name :string(255) not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# account_id :bigint not null
+#
+# Indexes
+#
+# index_assignment_policies_on_account_id (account_id)
+# index_assignment_policies_on_account_id_and_name (account_id,name) UNIQUE
+# index_assignment_policies_on_enabled (enabled)
+#
+class AssignmentPolicy < ApplicationRecord
+ belongs_to :account
+ has_many :inbox_assignment_policies, dependent: :destroy
+ has_many :inboxes, through: :inbox_assignment_policies
+
+ validates :name, presence: true, uniqueness: { scope: :account_id }
+ validates :fair_distribution_limit, numericality: { greater_than: 0 }
+ validates :fair_distribution_window, numericality: { greater_than: 0 }
+
+ enum conversation_priority: { earliest_created: 0, longest_waiting: 1 }
+
+ enum assignment_order: { round_robin: 0 } unless ChatwootApp.enterprise?
+end
+
+AssignmentPolicy.include_mod_with('Concerns::AssignmentPolicy')
diff --git a/app/models/attachment.rb b/app/models/attachment.rb
index 8c5750148..42ca79d6c 100644
--- a/app/models/attachment.rb
+++ b/app/models/attachment.rb
@@ -62,7 +62,12 @@ class Attachment < ApplicationRecord
def thumb_url
return '' unless file.attached? && image?
- url_for(file.representation(resize_to_fill: [250, nil]))
+ begin
+ url_for(file.representation(resize_to_fill: [250, nil]))
+ rescue ActiveStorage::UnrepresentableError => e
+ Rails.logger.warn "Unrepresentable image attachment: #{id} (#{file.filename}) - #{e.message}"
+ ''
+ end
end
def with_attached_file?
diff --git a/app/models/channel/whatsapp.rb b/app/models/channel/whatsapp.rb
index 7471cf807..7318cd978 100644
--- a/app/models/channel/whatsapp.rb
+++ b/app/models/channel/whatsapp.rb
@@ -32,7 +32,6 @@ class Channel::Whatsapp < ApplicationRecord
validates :phone_number, presence: true, uniqueness: true
validate :validate_provider_config
- before_save :setup_webhooks
after_create :sync_templates
before_destroy :teardown_webhooks
@@ -60,6 +59,13 @@ class Channel::Whatsapp < ApplicationRecord
delegate :media_url, to: :provider_service
delegate :api_headers, to: :provider_service
+ def setup_webhooks
+ perform_webhook_setup
+ rescue StandardError => e
+ Rails.logger.error "[WHATSAPP] Webhook setup failed: #{e.message}"
+ prompt_reauthorization!
+ end
+
private
def ensure_webhook_verify_token
@@ -70,34 +76,6 @@ class Channel::Whatsapp < ApplicationRecord
errors.add(:provider_config, 'Invalid Credentials') unless provider_service.validate_provider_config?
end
- def setup_webhooks
- return unless should_setup_webhooks?
-
- perform_webhook_setup
- rescue StandardError => e
- handle_webhook_setup_error(e)
- end
-
- def provider_config_changed?
- will_save_change_to_provider_config?
- end
-
- def should_setup_webhooks?
- whatsapp_cloud_provider? && embedded_signup_source? && webhook_config_present? && provider_config_changed?
- end
-
- def whatsapp_cloud_provider?
- provider == 'whatsapp_cloud'
- end
-
- def embedded_signup_source?
- provider_config['source'] == 'embedded_signup'
- end
-
- def webhook_config_present?
- provider_config['business_account_id'].present? && provider_config['api_key'].present?
- end
-
def perform_webhook_setup
business_account_id = provider_config['business_account_id']
api_key = provider_config['api_key']
@@ -105,12 +83,6 @@ class Channel::Whatsapp < ApplicationRecord
Whatsapp::WebhookSetupService.new(self, business_account_id, api_key).perform
end
- def handle_webhook_setup_error(error)
- Rails.logger.error "[WHATSAPP] Webhook setup failed: #{error.message}"
- # Don't raise the error to prevent channel creation from failing
- # Webhooks can be retried later
- end
-
def teardown_webhooks
Whatsapp::WebhookTeardownService.new(self).perform
end
diff --git a/app/models/inbox.rb b/app/models/inbox.rb
index 1c898ba7f..27f096bfa 100644
--- a/app/models/inbox.rb
+++ b/app/models/inbox.rb
@@ -67,6 +67,8 @@ class Inbox < ApplicationRecord
has_many :conversations, dependent: :destroy_async
has_many :messages, dependent: :destroy_async
+ has_one :inbox_assignment_policy, dependent: :destroy
+ has_one :assignment_policy, through: :inbox_assignment_policy
has_one :agent_bot_inbox, dependent: :destroy_async
has_one :agent_bot, through: :agent_bot_inbox
has_many :webhooks, dependent: :destroy_async
diff --git a/app/models/inbox_assignment_policy.rb b/app/models/inbox_assignment_policy.rb
new file mode 100644
index 000000000..c263ab40e
--- /dev/null
+++ b/app/models/inbox_assignment_policy.rb
@@ -0,0 +1,21 @@
+# == Schema Information
+#
+# Table name: inbox_assignment_policies
+#
+# id :bigint not null, primary key
+# created_at :datetime not null
+# updated_at :datetime not null
+# assignment_policy_id :bigint not null
+# inbox_id :bigint not null
+#
+# Indexes
+#
+# index_inbox_assignment_policies_on_assignment_policy_id (assignment_policy_id)
+# index_inbox_assignment_policies_on_inbox_id (inbox_id) UNIQUE
+#
+class InboxAssignmentPolicy < ApplicationRecord
+ belongs_to :inbox
+ belongs_to :assignment_policy
+
+ validates :inbox_id, uniqueness: true
+end
diff --git a/app/policies/assignment_policy_policy.rb b/app/policies/assignment_policy_policy.rb
new file mode 100644
index 000000000..fcd0ee9bc
--- /dev/null
+++ b/app/policies/assignment_policy_policy.rb
@@ -0,0 +1,21 @@
+class AssignmentPolicyPolicy < ApplicationPolicy
+ def index?
+ @account_user.administrator?
+ end
+
+ def show?
+ @account_user.administrator?
+ end
+
+ def create?
+ @account_user.administrator?
+ end
+
+ def update?
+ @account_user.administrator?
+ end
+
+ def destroy?
+ @account_user.administrator?
+ end
+end
diff --git a/app/services/notification/push_notification_service.rb b/app/services/notification/push_notification_service.rb
index 9878107c1..125ad9113 100644
--- a/app/services/notification/push_notification_service.rb
+++ b/app/services/notification/push_notification_service.rb
@@ -68,14 +68,23 @@ class Notification::PushNotificationService
WebPush.payload_send(**browser_push_payload(subscription))
Rails.logger.info("Browser push sent to #{user.email} with title #{push_message[:title]}")
- rescue WebPush::ExpiredSubscription, WebPush::InvalidSubscription, WebPush::Unauthorized => e
- Rails.logger.info "WebPush subscription expired: #{e.message}"
- subscription.destroy!
- rescue Errno::ECONNRESET, Net::OpenTimeout, Net::ReadTimeout => e
- Rails.logger.error "WebPush operation error: #{e.message}"
rescue StandardError => e
- ChatwootExceptionTracker.new(e, account: notification.account).capture_exception
- true
+ handle_browser_push_error(e, subscription)
+ end
+
+ def handle_browser_push_error(error, subscription)
+ case error
+ when WebPush::ExpiredSubscription, WebPush::InvalidSubscription, WebPush::Unauthorized
+ Rails.logger.info "WebPush subscription expired: #{error.message}"
+ subscription.destroy!
+ when WebPush::TooManyRequests
+ Rails.logger.warn "WebPush rate limited for #{user.email} on account #{notification.account.id}: #{error.message}"
+ when Errno::ECONNRESET, Net::OpenTimeout, Net::ReadTimeout
+ Rails.logger.error "WebPush operation error: #{error.message}"
+ else
+ ChatwootExceptionTracker.new(error, account: notification.account).capture_exception
+ true
+ end
end
def send_fcm_push(subscription)
diff --git a/app/services/whatsapp/channel_creation_service.rb b/app/services/whatsapp/channel_creation_service.rb
index 3039ca003..154f55520 100644
--- a/app/services/whatsapp/channel_creation_service.rb
+++ b/app/services/whatsapp/channel_creation_service.rb
@@ -33,15 +33,14 @@ class Whatsapp::ChannelCreationService
def create_channel_with_inbox
ActiveRecord::Base.transaction do
- channel = create_channel
+ channel = build_channel
create_inbox(channel)
- channel.reload
channel
end
end
- def create_channel
- Channel::Whatsapp.create!(
+ def build_channel
+ Channel::Whatsapp.build(
account: @account,
phone_number: @phone_info[:phone_number],
provider: 'whatsapp_cloud',
diff --git a/app/services/whatsapp/embedded_signup_service.rb b/app/services/whatsapp/embedded_signup_service.rb
index e66506638..1b882b1f1 100644
--- a/app/services/whatsapp/embedded_signup_service.rb
+++ b/app/services/whatsapp/embedded_signup_service.rb
@@ -11,16 +11,34 @@ class Whatsapp::EmbeddedSignupService
def perform
validate_parameters!
- # Exchange code for user access token
- access_token = Whatsapp::TokenExchangeService.new(@code).perform
+ access_token = exchange_code_for_token
+ phone_info = fetch_phone_info(access_token)
+ validate_token_access(access_token)
- # Fetch phone information
- phone_info = Whatsapp::PhoneInfoService.new(@waba_id, @phone_number_id, access_token).perform
+ channel = create_or_reauthorize_channel(access_token, phone_info)
+ channel.setup_webhooks
+ channel
- # Validate token has access to the WABA
+ rescue StandardError => e
+ Rails.logger.error("[WHATSAPP] Embedded signup failed: #{e.message}")
+ raise e
+ end
+
+ private
+
+ def exchange_code_for_token
+ Whatsapp::TokenExchangeService.new(@code).perform
+ end
+
+ def fetch_phone_info(access_token)
+ Whatsapp::PhoneInfoService.new(@waba_id, @phone_number_id, access_token).perform
+ end
+
+ def validate_token_access(access_token)
Whatsapp::TokenValidationService.new(access_token, @waba_id).perform
+ end
- # Reauthorization flow if inbox_id is present
+ def create_or_reauthorize_channel(access_token, phone_info)
if @inbox_id.present?
Whatsapp::ReauthorizationService.new(
account: @account,
@@ -29,17 +47,11 @@ class Whatsapp::EmbeddedSignupService
business_id: @business_id
).perform(access_token, phone_info)
else
- # Create channel for new authorization
waba_info = { waba_id: @waba_id, business_name: phone_info[:business_name] }
Whatsapp::ChannelCreationService.new(@account, waba_info, phone_info, access_token).perform
end
- rescue StandardError => e
- Rails.logger.error("[WHATSAPP] Embedded signup failed: #{e.message}")
- raise e
end
- private
-
def validate_parameters!
missing_params = []
missing_params << 'code' if @code.blank?
diff --git a/app/services/whatsapp/incoming_message_base_service.rb b/app/services/whatsapp/incoming_message_base_service.rb
index 94ad5c7d1..0aed8dba0 100644
--- a/app/services/whatsapp/incoming_message_base_service.rb
+++ b/app/services/whatsapp/incoming_message_base_service.rb
@@ -92,6 +92,9 @@ class Whatsapp::IncomingMessageBaseService
@contact_inbox = contact_inbox
@contact = contact_inbox.contact
+
+ # Update existing contact name if ProfileName is available and current name is just phone number
+ update_contact_with_profile_name(contact_params)
end
def set_conversation
@@ -171,4 +174,21 @@ class Whatsapp::IncomingMessageBaseService
)
end
end
+
+ def update_contact_with_profile_name(contact_params)
+ profile_name = contact_params.dig(:profile, :name)
+ return if profile_name.blank?
+ return if @contact.name == profile_name
+
+ # Only update if current name exactly matches the phone number or formatted phone number
+ return unless contact_name_matches_phone_number?
+
+ @contact.update!(name: profile_name)
+ end
+
+ def contact_name_matches_phone_number?
+ phone_number = "+#{@processed_params[:messages].first[:from]}"
+ formatted_phone_number = TelephoneNumber.parse(phone_number).international_number
+ @contact.name == phone_number || @contact.name == formatted_phone_number
+ end
end
diff --git a/app/services/whatsapp/oneoff_campaign_service.rb b/app/services/whatsapp/oneoff_campaign_service.rb
index 47a971f41..de2713ac0 100644
--- a/app/services/whatsapp/oneoff_campaign_service.rb
+++ b/app/services/whatsapp/oneoff_campaign_service.rb
@@ -84,7 +84,7 @@ class Whatsapp::OneoffCampaignService
namespace: namespace,
lang_code: lang_code,
parameters: processed_parameters
- })
+ }, nil)
rescue StandardError => e
Rails.logger.error "Failed to send WhatsApp template message to #{to}: #{e.message}"
diff --git a/app/services/whatsapp/providers/base_service.rb b/app/services/whatsapp/providers/base_service.rb
index 97665f7ef..9fd1f6267 100644
--- a/app/services/whatsapp/providers/base_service.rb
+++ b/app/services/whatsapp/providers/base_service.rb
@@ -15,7 +15,7 @@ class Whatsapp::Providers::BaseService
raise 'Overwrite this method in child class'
end
- def send_template(_phone_number, _template_info)
+ def send_template(_phone_number, _template_info, _message)
raise 'Overwrite this method in child class'
end
@@ -31,27 +31,27 @@ class Whatsapp::Providers::BaseService
raise 'Overwrite this method in child class'
end
- def process_response(response)
+ def process_response(response, message)
parsed_response = response.parsed_response
if response.success? && parsed_response['error'].blank?
parsed_response['messages'].first['id']
else
- handle_error(response)
+ handle_error(response, message)
nil
end
end
- def handle_error(response)
+ def handle_error(response, message)
Rails.logger.error response.body
- return if @message.blank?
+ return if message.blank?
# https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes/#sample-response
error_message = error_message(response)
return if error_message.blank?
- @message.external_error = error_message
- @message.status = :failed
- @message.save!
+ message.external_error = error_message
+ message.status = :failed
+ message.save!
end
def create_buttons(items)
diff --git a/app/services/whatsapp/providers/whatsapp_360_dialog_service.rb b/app/services/whatsapp/providers/whatsapp_360_dialog_service.rb
index beb11d556..352f2d246 100644
--- a/app/services/whatsapp/providers/whatsapp_360_dialog_service.rb
+++ b/app/services/whatsapp/providers/whatsapp_360_dialog_service.rb
@@ -10,7 +10,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
end
end
- def send_template(phone_number, template_info)
+ def send_template(phone_number, template_info, message)
response = HTTParty.post(
"#{api_base_path}/messages",
headers: api_headers,
@@ -21,7 +21,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
}.to_json
)
- process_response(response)
+ process_response(response, message)
end
def sync_templates
@@ -68,7 +68,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
}.to_json
)
- process_response(response)
+ process_response(response, message)
end
def send_attachment_message(phone_number, message)
@@ -90,7 +90,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
}.to_json
)
- process_response(response)
+ process_response(response, message)
end
def error_message(response)
@@ -123,6 +123,6 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
}.to_json
)
- process_response(response)
+ process_response(response, message)
end
end
diff --git a/app/services/whatsapp/providers/whatsapp_cloud_service.rb b/app/services/whatsapp/providers/whatsapp_cloud_service.rb
index 34939048a..68e965595 100644
--- a/app/services/whatsapp/providers/whatsapp_cloud_service.rb
+++ b/app/services/whatsapp/providers/whatsapp_cloud_service.rb
@@ -11,7 +11,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
end
end
- def send_template(phone_number, template_info)
+ def send_template(phone_number, template_info, message)
template_body = template_body_parameters(template_info)
request_body = {
@@ -28,7 +28,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
body: request_body.to_json
)
- process_response(response)
+ process_response(response, message)
end
def sync_templates
@@ -92,7 +92,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
}.to_json
)
- process_response(response)
+ process_response(response, message)
end
def send_attachment_message(phone_number, message)
@@ -115,7 +115,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
}.to_json
)
- process_response(response)
+ process_response(response, message)
end
def error_message(response)
@@ -179,6 +179,6 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
}.to_json
)
- process_response(response)
+ process_response(response, message)
end
end
diff --git a/app/services/whatsapp/send_on_whatsapp_service.rb b/app/services/whatsapp/send_on_whatsapp_service.rb
index 5f91bce16..20419c0cd 100644
--- a/app/services/whatsapp/send_on_whatsapp_service.rb
+++ b/app/services/whatsapp/send_on_whatsapp_service.rb
@@ -33,7 +33,7 @@ class Whatsapp::SendOnWhatsappService < Base::SendOnChannelService
namespace: namespace,
lang_code: lang_code,
parameters: processed_parameters
- })
+ }, message)
message.update!(source_id: message_id) if message_id.present?
end
diff --git a/app/services/whatsapp/template_parameter_converter_service.rb b/app/services/whatsapp/template_parameter_converter_service.rb
index b9a9d55d9..641a29b95 100644
--- a/app/services/whatsapp/template_parameter_converter_service.rb
+++ b/app/services/whatsapp/template_parameter_converter_service.rb
@@ -86,6 +86,9 @@ class Whatsapp::TemplateParameterConverterService
# Hash format: {"1": "John", "name": "Jane"} → {body: {"1": "John", "name": "Jane"}}
body_params = convert_hash_to_body_params(legacy_params)
enhanced['body'] = body_params unless body_params.empty?
+ when NilClass
+ # Templates without parameters (nil processed_params)
+ # Return empty enhanced structure
else
raise ArgumentError, "Unknown legacy format: #{legacy_params.class}"
end
diff --git a/app/views/api/v1/accounts/assignment_policies/_assignment_policy.json.jbuilder b/app/views/api/v1/accounts/assignment_policies/_assignment_policy.json.jbuilder
new file mode 100644
index 000000000..b48307a94
--- /dev/null
+++ b/app/views/api/v1/accounts/assignment_policies/_assignment_policy.json.jbuilder
@@ -0,0 +1,10 @@
+json.id assignment_policy.id
+json.name assignment_policy.name
+json.description assignment_policy.description
+json.assignment_order assignment_policy.assignment_order
+json.conversation_priority assignment_policy.conversation_priority
+json.fair_distribution_limit assignment_policy.fair_distribution_limit
+json.fair_distribution_window assignment_policy.fair_distribution_window
+json.enabled assignment_policy.enabled
+json.created_at assignment_policy.created_at.to_i
+json.updated_at assignment_policy.updated_at.to_i
diff --git a/app/views/api/v1/accounts/assignment_policies/create.json.jbuilder b/app/views/api/v1/accounts/assignment_policies/create.json.jbuilder
new file mode 100644
index 000000000..8fd9543c3
--- /dev/null
+++ b/app/views/api/v1/accounts/assignment_policies/create.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'assignment_policy', assignment_policy: @assignment_policy
diff --git a/app/views/api/v1/accounts/assignment_policies/inboxes/create.json.jbuilder b/app/views/api/v1/accounts/assignment_policies/inboxes/create.json.jbuilder
new file mode 100644
index 000000000..c5aede050
--- /dev/null
+++ b/app/views/api/v1/accounts/assignment_policies/inboxes/create.json.jbuilder
@@ -0,0 +1,5 @@
+json.id @inbox_assignment_policy.id
+json.inbox_id @inbox_assignment_policy.inbox_id
+json.assignment_policy_id @inbox_assignment_policy.assignment_policy_id
+json.created_at @inbox_assignment_policy.created_at.to_i
+json.updated_at @inbox_assignment_policy.updated_at.to_i
diff --git a/app/views/api/v1/accounts/assignment_policies/inboxes/index.json.jbuilder b/app/views/api/v1/accounts/assignment_policies/inboxes/index.json.jbuilder
new file mode 100644
index 000000000..5a22aa917
--- /dev/null
+++ b/app/views/api/v1/accounts/assignment_policies/inboxes/index.json.jbuilder
@@ -0,0 +1,3 @@
+json.inboxes @inboxes do |inbox|
+ json.partial! 'api/v1/models/inbox', formats: [:json], resource: inbox
+end
diff --git a/app/views/api/v1/accounts/assignment_policies/index.json.jbuilder b/app/views/api/v1/accounts/assignment_policies/index.json.jbuilder
new file mode 100644
index 000000000..0be431f87
--- /dev/null
+++ b/app/views/api/v1/accounts/assignment_policies/index.json.jbuilder
@@ -0,0 +1,3 @@
+json.array! @assignment_policies do |assignment_policy|
+ json.partial! 'assignment_policy', assignment_policy: assignment_policy
+end
diff --git a/app/views/api/v1/accounts/assignment_policies/show.json.jbuilder b/app/views/api/v1/accounts/assignment_policies/show.json.jbuilder
new file mode 100644
index 000000000..8fd9543c3
--- /dev/null
+++ b/app/views/api/v1/accounts/assignment_policies/show.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'assignment_policy', assignment_policy: @assignment_policy
diff --git a/app/views/api/v1/accounts/assignment_policies/update.json.jbuilder b/app/views/api/v1/accounts/assignment_policies/update.json.jbuilder
new file mode 100644
index 000000000..8fd9543c3
--- /dev/null
+++ b/app/views/api/v1/accounts/assignment_policies/update.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'assignment_policy', assignment_policy: @assignment_policy
diff --git a/app/views/api/v1/accounts/inboxes/assignment_policies/create.json.jbuilder b/app/views/api/v1/accounts/inboxes/assignment_policies/create.json.jbuilder
new file mode 100644
index 000000000..105658704
--- /dev/null
+++ b/app/views/api/v1/accounts/inboxes/assignment_policies/create.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'api/v1/accounts/assignment_policies/assignment_policy', formats: [:json], assignment_policy: @assignment_policy
diff --git a/app/views/api/v1/accounts/inboxes/assignment_policies/show.json.jbuilder b/app/views/api/v1/accounts/inboxes/assignment_policies/show.json.jbuilder
new file mode 100644
index 000000000..105658704
--- /dev/null
+++ b/app/views/api/v1/accounts/inboxes/assignment_policies/show.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'api/v1/accounts/assignment_policies/assignment_policy', formats: [:json], assignment_policy: @assignment_policy
diff --git a/config/app.yml b/config/app.yml
index e6fc39be3..c9b08cd07 100644
--- a/config/app.yml
+++ b/config/app.yml
@@ -1,5 +1,5 @@
shared: &shared
- version: '4.4.0'
+ version: '4.5.0'
development:
<<: *shared
diff --git a/config/application.rb b/config/application.rb
index 92dd9a011..3eca267f0 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -12,7 +12,7 @@ Bundler.require(*Rails.groups)
# We rely on DOTENV to load the environment variables
# We need these environment variables to load the specific APM agent
Dotenv::Rails.load
-require 'ddtrace' if ENV.fetch('DD_TRACE_AGENT_URL', false).present?
+require 'datadog' if ENV.fetch('DD_TRACE_AGENT_URL', false).present?
require 'elastic-apm' if ENV.fetch('ELASTIC_APM_SECRET_TOKEN', false).present?
require 'scout_apm' if ENV.fetch('SCOUT_KEY', false).present?
diff --git a/config/features.yml b/config/features.yml
index db2d46700..d6f2d24a3 100644
--- a/config/features.yml
+++ b/config/features.yml
@@ -191,3 +191,7 @@
display_name: CRM V2
enabled: false
chatwoot_internal: true
+- name: assignment_v2
+ display_name: Assignment V2
+ enabled: false
+ chatwoot_internal: true
diff --git a/config/initializers/ai_agents.rb b/config/initializers/ai_agents.rb
new file mode 100644
index 000000000..37bdd589f
--- /dev/null
+++ b/config/initializers/ai_agents.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'agents'
+
+Rails.application.config.after_initialize do
+ api_key = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_API_KEY')&.value
+ model = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_MODEL')&.value.presence || OpenAiConstants::DEFAULT_MODEL
+ api_endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value || OpenAiConstants::DEFAULT_ENDPOINT
+
+ if api_key.present?
+ Agents.configure do |config|
+ config.openai_api_key = api_key
+ if api_endpoint.present?
+ api_base = "#{api_endpoint.chomp('/')}/v1"
+ config.openai_api_base = api_base
+ end
+ config.default_model = model
+ config.debug = false
+ end
+ end
+rescue StandardError => e
+ Rails.logger.error "Failed to configure AI Agents SDK: #{e.message}"
+end
diff --git a/config/initializers/languages.rb b/config/initializers/languages.rb
index 899359a4c..c34f7a605 100644
--- a/config/initializers/languages.rb
+++ b/config/initializers/languages.rb
@@ -41,7 +41,8 @@ LANGUAGES_CONFIG = {
36 => { name: 'íslenska (is)', iso_639_3_code: 'isl', iso_639_1_code: 'is', enabled: true },
37 => { name: 'עִברִית (he)', iso_639_3_code: 'heb', iso_639_1_code: 'he', enabled: true },
38 => { name: 'lietuvių (lt)', iso_639_3_code: 'lit', iso_639_1_code: 'lt', enabled: true },
- 39 => { name: 'Српски (sr)', iso_639_3_code: 'srp', iso_639_1_code: 'sr', enabled: true }
+ 39 => { name: 'Српски (sr)', iso_639_3_code: 'srp', iso_639_1_code: 'sr', enabled: true },
+ 40 => { name: 'български (bg)', iso_639_3_code: 'bul', iso_639_1_code: 'bg', enabled: true }
}.filter { |_key, val| val[:enabled] }.freeze
Rails.configuration.i18n.available_locales = LANGUAGES_CONFIG.map { |_index, lang| lang[:iso_639_1_code].to_sym }
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 1d8347679..e55132709 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -53,6 +53,8 @@ en:
email_already_exists: 'You have already signed up for an account with %{email}'
invalid_params: 'Invalid, please check the signup paramters and try again'
failed: Signup failed
+ assignment_policy:
+ not_found: Assignment policy not found
data_import:
data_type:
invalid: Invalid data type
diff --git a/config/routes.rb b/config/routes.rb
index 10749062e..1409b11fd 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -217,6 +217,15 @@ Rails.application.routes.draw do
end
end
+ # Assignment V2 Routes
+ resources :assignment_policies do
+ resources :inboxes, only: [:index, :create, :destroy], module: :assignment_policies
+ end
+
+ resources :inboxes, only: [] do
+ resource :assignment_policy, only: [:show, :create, :destroy], module: :inboxes
+ end
+
namespace :twitter do
resource :authorization, only: [:create]
end
diff --git a/enterprise/app/jobs/captain/conversation/response_builder_job.rb b/enterprise/app/jobs/captain/conversation/response_builder_job.rb
index b207bd2a4..7ede1201d 100644
--- a/enterprise/app/jobs/captain/conversation/response_builder_job.rb
+++ b/enterprise/app/jobs/captain/conversation/response_builder_job.rb
@@ -26,9 +26,15 @@ class Captain::Conversation::ResponseBuilderJob < ApplicationJob
delegate :account, :inbox, to: :@conversation
def generate_and_process_response
- @response = Captain::Llm::AssistantChatService.new(assistant: @assistant).generate_response(
- message_history: collect_previous_messages
- )
+ @response = if captain_v2_enabled?
+ Captain::Assistant::AgentRunnerService.new(assistant: @assistant, conversation: @conversation).generate_response(
+ message_history: collect_previous_messages
+ )
+ else
+ Captain::Llm::AssistantChatService.new(assistant: @assistant).generate_response(
+ message_history: collect_previous_messages
+ )
+ end
return process_action('handoff') if handoff_requested?
@@ -104,4 +110,8 @@ class Captain::Conversation::ResponseBuilderJob < ApplicationJob
def log_error(error)
ChatwootExceptionTracker.new(error, account: account).capture_exception
end
+
+ def captain_v2_enabled?
+ return account.feature_enabled?('captain_integration_v2')
+ end
end
diff --git a/enterprise/app/models/captain/assistant.rb b/enterprise/app/models/captain/assistant.rb
index cdf2b53f3..0423abf67 100644
--- a/enterprise/app/models/captain/assistant.rb
+++ b/enterprise/app/models/captain/assistant.rb
@@ -19,6 +19,7 @@
class Captain::Assistant < ApplicationRecord
include Avatarable
include Concerns::CaptainToolsHelpers
+ include Concerns::Agentable
self.table_name = 'captain_assistants'
@@ -35,6 +36,8 @@ class Captain::Assistant < ApplicationRecord
has_many :copilot_threads, dependent: :destroy_async
has_many :scenarios, class_name: 'Captain::Scenario', dependent: :destroy_async
+ store_accessor :config, :temperature, :feature_faq, :feature_memory, :product_name
+
validates :name, presence: true
validates :description, presence: true
validates :account_id, presence: true
@@ -71,6 +74,33 @@ class Captain::Assistant < ApplicationRecord
private
+ def agent_name
+ name
+ end
+
+ def agent_tools
+ [
+ self.class.resolve_tool_class('faq_lookup').new(self),
+ self.class.resolve_tool_class('handoff').new(self)
+ ]
+ end
+
+ def prompt_context
+ {
+ name: name,
+ description: description,
+ product_name: config['product_name'] || 'this product',
+ scenarios: scenarios.enabled.map do |scenario|
+ {
+ key: scenario.title.parameterize.underscore,
+ description: scenario.description
+ }
+ end,
+ response_guidelines: response_guidelines || [],
+ guardrails: guardrails || []
+ }
+ end
+
def default_avatar_url
"#{ENV.fetch('FRONTEND_URL', nil)}/assets/images/dashboard/captain/logo.svg"
end
diff --git a/enterprise/app/models/captain/scenario.rb b/enterprise/app/models/captain/scenario.rb
index ecfba396f..aac7e2411 100644
--- a/enterprise/app/models/captain/scenario.rb
+++ b/enterprise/app/models/captain/scenario.rb
@@ -22,6 +22,7 @@
#
class Captain::Scenario < ApplicationRecord
include Concerns::CaptainToolsHelpers
+ include Concerns::Agentable
self.table_name = 'captain_scenarios'
@@ -37,10 +38,43 @@ class Captain::Scenario < ApplicationRecord
scope :enabled, -> { where(enabled: true) }
+ delegate :temperature, :feature_faq, :feature_memory, :product_name, to: :assistant
+
before_save :resolve_tool_references
+ def prompt_context
+ {
+ title: title,
+ instructions: resolved_instructions,
+ tools: resolved_tools
+ }
+ end
+
private
+ def agent_name
+ "#{title} Agent".titleize
+ end
+
+ def agent_tools
+ resolved_tools.map { |tool| self.class.resolve_tool_class(tool[:id]) }.map { |tool| tool.new(assistant) }
+ end
+
+ def resolved_instructions
+ instruction.gsub(TOOL_REFERENCE_REGEX) do |match|
+ "#{match} tool "
+ end
+ end
+
+ def resolved_tools
+ return [] if tools.blank?
+
+ available_tools = self.class.available_agent_tools
+ tools.filter_map do |tool_id|
+ available_tools.find { |tool| tool[:id] == tool_id }
+ end
+ end
+
# Validates that all tool references in the instruction are valid.
# Parses the instruction for tool references and checks if they exist
# in the available tools configuration.
diff --git a/enterprise/app/models/concerns/agentable.rb b/enterprise/app/models/concerns/agentable.rb
new file mode 100644
index 000000000..dab76a726
--- /dev/null
+++ b/enterprise/app/models/concerns/agentable.rb
@@ -0,0 +1,56 @@
+module Concerns::Agentable
+ extend ActiveSupport::Concern
+
+ def agent
+ Agents::Agent.new(
+ name: agent_name,
+ instructions: ->(context) { agent_instructions(context) },
+ tools: agent_tools,
+ model: agent_model,
+ temperature: temperature.to_f || 0.7,
+ response_schema: agent_response_schema
+ )
+ end
+
+ def agent_instructions(context = nil)
+ enhanced_context = prompt_context
+
+ if context
+ state = context.context[:state] || {}
+ conversation_data = state[:conversation] || {}
+ contact_data = state[:contact] || {}
+ enhanced_context = enhanced_context.merge(
+ conversation: conversation_data,
+ contact: contact_data
+ )
+ end
+
+ Captain::PromptRenderer.render(template_name, enhanced_context.with_indifferent_access)
+ end
+
+ private
+
+ def agent_name
+ raise NotImplementedError, "#{self.class} must implement agent_name"
+ end
+
+ def template_name
+ self.class.name.demodulize.underscore
+ end
+
+ def agent_tools
+ [] # Default implementation, override if needed
+ end
+
+ def agent_model
+ InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_MODEL')&.value.presence || OpenAiConstants::DEFAULT_MODEL
+ end
+
+ def agent_response_schema
+ Captain::ResponseSchema
+ end
+
+ def prompt_context
+ raise NotImplementedError, "#{self.class} must implement prompt_context"
+ end
+end
diff --git a/enterprise/app/models/enterprise/concerns/assignment_policy.rb b/enterprise/app/models/enterprise/concerns/assignment_policy.rb
new file mode 100644
index 000000000..bddcc5e75
--- /dev/null
+++ b/enterprise/app/models/enterprise/concerns/assignment_policy.rb
@@ -0,0 +1,7 @@
+module Enterprise::Concerns::AssignmentPolicy
+ extend ActiveSupport::Concern
+
+ included do
+ enum assignment_order: { round_robin: 0, balanced: 1 } if ChatwootApp.enterprise?
+ end
+end
diff --git a/enterprise/app/services/captain/assistant/agent_runner_service.rb b/enterprise/app/services/captain/assistant/agent_runner_service.rb
new file mode 100644
index 000000000..7a35e6d07
--- /dev/null
+++ b/enterprise/app/services/captain/assistant/agent_runner_service.rb
@@ -0,0 +1,161 @@
+require 'agents'
+
+class Captain::Assistant::AgentRunnerService
+ CONVERSATION_STATE_ATTRIBUTES = %i[
+ id display_id inbox_id contact_id status priority
+ label_list custom_attributes additional_attributes
+ ].freeze
+
+ CONTACT_STATE_ATTRIBUTES = %i[
+ id name email phone_number identifier contact_type
+ custom_attributes additional_attributes
+ ].freeze
+
+ def initialize(assistant:, conversation: nil, callbacks: {})
+ @assistant = assistant
+ @conversation = conversation
+ @callbacks = callbacks
+ end
+
+ def generate_response(message_history: [])
+ agents = build_and_wire_agents
+ context = build_context(message_history)
+ message_to_process = extract_last_user_message(message_history)
+ runner = Agents::Runner.with_agents(*agents)
+ runner = add_callbacks_to_runner(runner) if @callbacks.any?
+ result = runner.run(message_to_process, context: context)
+
+ process_agent_result(result)
+ rescue StandardError => e
+ # when running the agent runner service in a rake task, the conversation might not have an account associated
+ # for regular production usage, it will run just fine
+ ChatwootExceptionTracker.new(e, account: @conversation&.account).capture_exception
+ Rails.logger.error "[Captain V2] AgentRunnerService error: #{e.message}"
+ Rails.logger.error e.backtrace.join("\n")
+
+ error_response(e.message)
+ end
+
+ private
+
+ def build_context(message_history)
+ conversation_history = message_history.map do |msg|
+ content = extract_text_from_content(msg[:content])
+
+ {
+ role: msg[:role].to_sym,
+ content: content,
+ agent_name: msg[:agent_name]
+ }
+ end
+
+ {
+ conversation_history: conversation_history,
+ state: build_state
+ }
+ end
+
+ def extract_last_user_message(message_history)
+ last_user_msg = message_history.reverse.find { |msg| msg[:role] == 'user' }
+
+ extract_text_from_content(last_user_msg[:content])
+ end
+
+ def extract_text_from_content(content)
+ # Handle structured output from agents
+ return content[:response] || content['response'] || content.to_s if content.is_a?(Hash)
+
+ return content unless content.is_a?(Array)
+
+ text_parts = content.select { |part| part[:type] == 'text' }.pluck(:text)
+ text_parts.join(' ')
+ end
+
+ # Response formatting methods
+ def process_agent_result(result)
+ Rails.logger.info "[Captain V2] Agent result: #{result.inspect}"
+ format_response(result.output)
+ end
+
+ def format_response(output)
+ return output.with_indifferent_access if output.is_a?(Hash)
+
+ # Fallback for backwards compatibility
+ {
+ 'response' => output.to_s,
+ 'reasoning' => 'Processed by agent'
+ }
+ end
+
+ def error_response(error_message)
+ {
+ 'response' => 'conversation_handoff',
+ 'reasoning' => "Error occurred: #{error_message}"
+ }
+ end
+
+ def build_state
+ state = {
+ account_id: @assistant.account_id,
+ assistant_id: @assistant.id,
+ assistant_config: @assistant.config
+ }
+
+ if @conversation
+ state[:conversation] = @conversation.attributes.symbolize_keys.slice(*CONVERSATION_STATE_ATTRIBUTES)
+ state[:contact] = @conversation.contact.attributes.symbolize_keys.slice(*CONTACT_STATE_ATTRIBUTES) if @conversation.contact
+ end
+
+ state
+ end
+
+ def build_and_wire_agents
+ assistant_agent = @assistant.agent
+ scenario_agents = @assistant.scenarios.enabled.map(&:agent)
+
+ assistant_agent.register_handoffs(*scenario_agents) if scenario_agents.any?
+ scenario_agents.each { |scenario_agent| scenario_agent.register_handoffs(assistant_agent) }
+
+ [assistant_agent] + scenario_agents
+ end
+
+ def add_callbacks_to_runner(runner)
+ runner = add_agent_thinking_callback(runner) if @callbacks[:on_agent_thinking]
+ runner = add_tool_start_callback(runner) if @callbacks[:on_tool_start]
+ runner = add_tool_complete_callback(runner) if @callbacks[:on_tool_complete]
+ runner = add_agent_handoff_callback(runner) if @callbacks[:on_agent_handoff]
+ runner
+ end
+
+ def add_agent_thinking_callback(runner)
+ runner.on_agent_thinking do |*args|
+ @callbacks[:on_agent_thinking].call(*args)
+ rescue StandardError => e
+ Rails.logger.warn "[Captain] Callback error for agent_thinking: #{e.message}"
+ end
+ end
+
+ def add_tool_start_callback(runner)
+ runner.on_tool_start do |*args|
+ @callbacks[:on_tool_start].call(*args)
+ rescue StandardError => e
+ Rails.logger.warn "[Captain] Callback error for tool_start: #{e.message}"
+ end
+ end
+
+ def add_tool_complete_callback(runner)
+ runner.on_tool_complete do |*args|
+ @callbacks[:on_tool_complete].call(*args)
+ rescue StandardError => e
+ Rails.logger.warn "[Captain] Callback error for tool_complete: #{e.message}"
+ end
+ end
+
+ def add_agent_handoff_callback(runner)
+ runner.on_agent_handoff do |*args|
+ @callbacks[:on_agent_handoff].call(*args)
+ rescue StandardError => e
+ Rails.logger.warn "[Captain] Callback error for agent_handoff: #{e.message}"
+ end
+ end
+end
diff --git a/enterprise/lib/captain/prompt_renderer.rb b/enterprise/lib/captain/prompt_renderer.rb
new file mode 100644
index 000000000..1a73ddd15
--- /dev/null
+++ b/enterprise/lib/captain/prompt_renderer.rb
@@ -0,0 +1,25 @@
+require 'liquid'
+
+class Captain::PromptRenderer
+ class << self
+ def render(template_name, context = {})
+ template = load_template(template_name)
+ liquid_template = Liquid::Template.parse(template)
+ liquid_template.render(stringify_keys(context))
+ end
+
+ private
+
+ def load_template(template_name)
+ template_path = Rails.root.join('enterprise', 'lib', 'captain', 'prompts', "#{template_name}.liquid")
+
+ raise "Template not found: #{template_name}" unless File.exist?(template_path)
+
+ File.read(template_path)
+ end
+
+ def stringify_keys(hash)
+ hash.deep_stringify_keys
+ end
+ end
+end
diff --git a/enterprise/lib/captain/prompts/assistant.liquid b/enterprise/lib/captain/prompts/assistant.liquid
new file mode 100644
index 000000000..69c967d73
--- /dev/null
+++ b/enterprise/lib/captain/prompts/assistant.liquid
@@ -0,0 +1,80 @@
+# System Context
+You are part of Captain, a multi-agent AI system designed for seamless agent coordination and task execution. You can transfer conversations to specialized agents using handoff functions (e.g., `handoff_to_[agent_name]`). These transfers happen in the background - never mention or draw attention to them in your responses.
+
+# Your Identity
+You are {{name}}, a helpful and knowledgeable assistant. Your role is to provide accurate information, assist with tasks, and ensure users get the help they need.
+
+{{ description }}
+
+Don't digress away from your instructions, and use all the available tools at your disposal for solving customer issues. If you are to state something factual about {{product_name}} ensure you source that information from the FAQs only. Use the faq_lookup tool for this.
+
+# Current Context
+
+Here's the metadata we have about the current conversation and the contact associated with it:
+
+{% if conversation -%}
+{% render 'conversation' %}
+{% endif -%}
+
+{% if contact -%}
+{% render 'contact' %}
+{% endif -%}
+
+{% if response_guidelines.size > 0 -%}
+# Response Guidelines
+Your responses should follow these guidelines:
+{% for guideline in response_guidelines -%}
+- {{ guideline }}
+{% endfor %}
+{% endif -%}
+
+{% if guardrails.size > 0 -%}
+# Guardrails
+Always respect these boundaries:
+{% for guardrail in guardrails -%}
+- {{ guardrail }}
+{% endfor %}
+{% endif -%}
+
+# Decision Framework
+
+## 1. Analyze the Request
+First, understand what the user is asking:
+- **Intent**: What are they trying to achieve?
+- **Type**: Is it a question, task, complaint, or request?
+- **Complexity**: Can you handle it or does it need specialized expertise?
+
+## 2. Check for Specialized Scenarios First
+Before using any tools, check if the request matches any of these scenarios. If unclear, ask clarifying questions to determine if a scenario applies:
+
+{% for scenario in scenarios -%}
+### handoff_to_{{ scenario.key }}
+{{ scenario.description }}
+{% endfor -%}
+
+## 3. Handle the Request
+If no specialized scenario clearly matches, handle it yourself:
+
+### For Questions and Information Requests
+1. **First, check existing knowledge**: Use `faq_lookup` tool to search for relevant information
+2. **If not found in FAQs**: Provide your best answer based on available context
+3. **If unable to answer**: Use `handoff` tool to transfer to a human expert
+
+### For Complex or Unclear Requests
+1. **Ask clarifying questions**: Gather more information if needed
+2. **Break down complex tasks**: Handle step by step or hand off if too complex
+3. **Escalate when necessary**: Use `handoff` tool for issues beyond your capabilities
+
+## Response Best Practices
+- Be conversational but professional
+- Provide actionable information
+- Include relevant details from tool responses
+
+# Human Handoff Protocol
+Transfer to a human agent when:
+- User explicitly requests human assistance
+- You cannot find needed information after checking FAQs
+- The issue requires specialized knowledge or permissions you don't have
+- Multiple attempts to help have been unsuccessful
+
+When using the `handoff` tool, provide a clear reason that helps the human agent understand the context.
diff --git a/enterprise/lib/captain/prompts/scenario.liquid b/enterprise/lib/captain/prompts/scenario.liquid
new file mode 100644
index 000000000..339820b83
--- /dev/null
+++ b/enterprise/lib/captain/prompts/scenario.liquid
@@ -0,0 +1,24 @@
+# System context
+You are part of a multi-agent system where you've been handed off a conversation to handle a specific task.
+The handoff was seamless - the user is not aware of any transfer. Continue the conversation naturally.
+
+# Your Role
+You are a specialized agent called {{ title }}, your task is to handle the following scenario:
+
+{{ instructions }}
+
+{% if conversation -%}
+{% render 'conversation' %}
+
+{% if contact -%}
+{% render 'contact' %}
+{% endif -%}
+{% endif -%}
+
+{% if tools.size > 0 -%}
+# Available Tools
+You have access to these tools:
+{% for tool in tools -%}
+- {{ tool.id }}: {{ tool.description }}
+{% endfor %}
+{%- endif %}
diff --git a/enterprise/lib/captain/prompts/snippets/contact.liquid b/enterprise/lib/captain/prompts/snippets/contact.liquid
new file mode 100644
index 000000000..372389cbb
--- /dev/null
+++ b/enterprise/lib/captain/prompts/snippets/contact.liquid
@@ -0,0 +1,17 @@
+# Contact Information
+- Contact ID: {{ contact.id }}
+- Name: {{ contact.name || "Unknown" }}
+- Email: {{ contact.email || "None" }}
+- Phone: {{ contact.phone_number || "None" }}
+- Identifier: {{ contact.identifier || "None" }}
+- Type: {{ contact.contact_type || "visitor" }}
+{% if contact.custom_attributes -%}
+ {% for attribute in contact.custom_attributes -%}
+- {{ attribute[0] }}: {{ attribute[1] }}
+ {% endfor -%}
+{% endif -%}
+{% if contact.additional_attributes -%}
+ {% for attribute in contact.additional_attributes -%}
+- {{ attribute[0] }}: {{ attribute[1] }}
+ {% endfor -%}
+{% endif -%}
\ No newline at end of file
diff --git a/enterprise/lib/captain/prompts/snippets/conversation.liquid b/enterprise/lib/captain/prompts/snippets/conversation.liquid
new file mode 100644
index 000000000..b5faee7d6
--- /dev/null
+++ b/enterprise/lib/captain/prompts/snippets/conversation.liquid
@@ -0,0 +1,18 @@
+# Current Conversation Context
+- Conversation ID: {{ conversation.display_id }}
+- Contact ID: {{ conversation.contact_id }}
+- Status: {{ conversation.status }}
+- Priority: {{ conversation.priority || "None" }}
+{% if conversation.label_list.size > 0 -%}
+- Labels: {{ conversation.label_list | join: ", " }}
+{% endif -%}
+{% if conversation.custom_attributes -%}
+ {% for attribute in conversation.custom_attributes -%}
+- {{ attribute[0] }}: {{ attribute[1] }}
+ {% endfor -%}
+{% endif -%}
+{% if conversation.additional_attributes -%}
+ {% for attribute in conversation.additional_attributes -%}
+- {{ attribute[0] }}: {{ attribute[1] }}
+ {% endfor -%}
+{% endif -%}
\ No newline at end of file
diff --git a/enterprise/lib/captain/response_schema.rb b/enterprise/lib/captain/response_schema.rb
new file mode 100644
index 000000000..651eb7e23
--- /dev/null
+++ b/enterprise/lib/captain/response_schema.rb
@@ -0,0 +1,6 @@
+# TODO: Wrap the schema lib under ai-agents
+# So we can extend it as Agents::Schema
+class Captain::ResponseSchema < RubyLLM::Schema
+ string :response, description: 'The message to send to the user'
+ string :reasoning, description: "Agent's thought process"
+end
diff --git a/lib/open_ai_constants.rb b/lib/open_ai_constants.rb
new file mode 100644
index 000000000..2c87f3378
--- /dev/null
+++ b/lib/open_ai_constants.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module OpenAiConstants
+ DEFAULT_MODEL = 'gpt-4.1-mini'
+ DEFAULT_ENDPOINT = 'https://api.openai.com'
+end
diff --git a/lib/tasks/captain_chat.rake b/lib/tasks/captain_chat.rake
new file mode 100644
index 000000000..cfe257196
--- /dev/null
+++ b/lib/tasks/captain_chat.rake
@@ -0,0 +1,235 @@
+require 'io/console'
+require 'readline'
+
+namespace :captain do
+ desc 'Start interactive chat with Captain assistant - Usage: rake captain:chat[assistant_id] or rake captain:chat -- assistant_id'
+ task :chat, [:assistant_id] => :environment do |_, args|
+ assistant_id = args[:assistant_id] || ARGV[1]
+
+ unless assistant_id
+ puts '❌ Please provide an assistant ID'
+ puts 'Usage: rake captain:chat[assistant_id]'
+ puts "\nAvailable assistants:"
+ Captain::Assistant.includes(:account).each do |assistant|
+ puts " ID: #{assistant.id} - #{assistant.name} (Account: #{assistant.account.name})"
+ end
+ exit 1
+ end
+
+ assistant = Captain::Assistant.find_by(id: assistant_id)
+ unless assistant
+ puts "❌ Assistant with ID #{assistant_id} not found"
+ exit 1
+ end
+
+ # Clear ARGV to prevent gets from reading files
+ ARGV.clear
+
+ chat_session = CaptainChatSession.new(assistant)
+ chat_session.start
+ end
+end
+
+class CaptainChatSession
+ def initialize(assistant)
+ @assistant = assistant
+ @message_history = []
+ end
+
+ def start
+ show_assistant_info
+ show_instructions
+ chat_loop
+ show_exit_message
+ end
+
+ private
+
+ def show_instructions
+ puts "💡 Type 'exit', 'quit', or 'bye' to end the session"
+ puts "💡 Type 'clear' to clear message history"
+ puts('-' * 50)
+ end
+
+ def chat_loop
+ loop do
+ puts '' # Add spacing before prompt
+ user_input = Readline.readline('👤 You: ', true)
+ next unless user_input # Handle Ctrl+D
+
+ break unless handle_user_input(user_input.strip)
+ end
+ end
+
+ def handle_user_input(user_input)
+ case user_input.downcase
+ when 'exit', 'quit', 'bye'
+ false
+ when 'clear'
+ clear_history
+ true
+ when ''
+ true
+ else
+ process_user_message(user_input)
+ true
+ end
+ end
+
+ def show_exit_message
+ puts "\nChat session ended"
+ puts "Final conversation log has #{@message_history.length} messages"
+ end
+
+ def show_assistant_info
+ show_basic_info
+ show_scenarios
+ show_available_tools
+ puts ''
+ end
+
+ def show_basic_info
+ puts "🤖 Starting chat with #{@assistant.name}"
+ puts "🏢 Account: #{@assistant.account.name}"
+ puts "🆔 Assistant ID: #{@assistant.id}"
+ end
+
+ def show_scenarios
+ scenarios = @assistant.scenarios.enabled
+ if scenarios.any?
+ puts "⚡ Enabled Scenarios (#{scenarios.count}):"
+ scenarios.each { |scenario| display_scenario(scenario) }
+ else
+ puts '⚡ No scenarios enabled'
+ end
+ end
+
+ def display_scenario(scenario)
+ tools_count = scenario.tools&.length || 0
+ puts " • #{scenario.title} (#{tools_count} tools)"
+ return if scenario.description.blank?
+
+ description = truncate_description(scenario.description)
+ puts " #{description}"
+ end
+
+ def truncate_description(description)
+ description.length > 60 ? "#{description[0..60]}..." : description
+ end
+
+ def show_available_tools
+ available_tools = Captain::Assistant.available_tool_ids
+ if available_tools.any?
+ puts "🔧 Available Tools (#{available_tools.count}): #{available_tools.join(', ')}"
+ else
+ puts '🔧 No tools available'
+ end
+ end
+
+ def process_user_message(user_input)
+ add_to_history('user', user_input)
+
+ begin
+ print "🤖 #{@assistant.name}: "
+ @current_system_messages = []
+
+ result = generate_assistant_response
+ display_response(result)
+ rescue StandardError => e
+ handle_error(e)
+ end
+ end
+
+ def generate_assistant_response
+ runner = Captain::Assistant::AgentRunnerService.new(assistant: @assistant, callbacks: build_callbacks)
+ runner.generate_response(message_history: @message_history)
+ end
+
+ def build_callbacks
+ {
+ on_agent_thinking: method(:handle_agent_thinking),
+ on_tool_start: method(:handle_tool_start),
+ on_tool_complete: method(:handle_tool_complete),
+ on_agent_handoff: method(:handle_agent_handoff)
+ }
+ end
+
+ def handle_agent_thinking(agent, _input)
+ agent_name = extract_name(agent)
+ @current_system_messages << "#{agent_name} is thinking..."
+ add_to_history('system', "#{agent_name} is thinking...")
+ end
+
+ def handle_tool_start(tool, _args)
+ tool_name = extract_tool_name(tool)
+ @current_system_messages << "Using tool: #{tool_name}"
+ add_to_history('system', "Using tool: #{tool_name}")
+ end
+
+ def handle_tool_complete(tool, _result)
+ tool_name = extract_tool_name(tool)
+ @current_system_messages << "Tool #{tool_name} completed"
+ add_to_history('system', "Tool #{tool_name} completed")
+ end
+
+ def handle_agent_handoff(from, to, reason)
+ @current_system_messages << "Handoff: #{extract_name(from)} → #{extract_name(to)} (#{reason})"
+ add_to_history('system', "Agent handoff: #{extract_name(from)} → #{extract_name(to)} (#{reason})")
+ end
+
+ def display_response(result)
+ response_text = result['response'] || 'No response generated'
+ reasoning = result['reasoning']
+
+ puts dim_text("\n#{@current_system_messages.join("\n")}") if @current_system_messages.any?
+ puts response_text
+ puts dim_italic_text("(Reasoning: #{reasoning})") if reasoning && reasoning != 'Processed by agent'
+
+ add_to_history('assistant', response_text, reasoning: reasoning)
+ end
+
+ def handle_error(error)
+ error_msg = "Error: #{error.message}"
+ puts "❌ #{error_msg}"
+ add_to_history('system', error_msg)
+ end
+
+ def add_to_history(role, content, agent_name: nil, reasoning: nil)
+ message = {
+ role: role,
+ content: content,
+ timestamp: Time.current,
+ agent_name: agent_name || (role == 'assistant' ? @assistant.name : nil)
+ }
+ message[:reasoning] = reasoning if reasoning
+
+ @message_history << message
+ end
+
+ def clear_history
+ @message_history.clear
+ puts 'Message history cleared'
+ end
+
+ def dim_text(text)
+ # ANSI escape code for very dim gray text (bright black/dark gray)
+ "\e[90m#{text}\e[0m"
+ end
+
+ def dim_italic_text(text)
+ # ANSI escape codes for dim gray + italic text
+ "\e[90m\e[3m#{text}\e[0m"
+ end
+
+ def extract_tool_name(tool)
+ return tool if tool.is_a?(String)
+
+ tool.class.name.split('::').last.gsub('Tool', '')
+ rescue StandardError
+ tool.to_s
+ end
+
+ def extract_name(obj)
+ obj.respond_to?(:name) ? obj.name : obj.to_s
+ end
+end
diff --git a/package.json b/package.json
index 2aefb3d53..441dcbd90 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@chatwoot/chatwoot",
- "version": "4.4.0",
+ "version": "4.5.0",
"license": "MIT",
"scripts": {
"eslint": "eslint app/**/*.{js,vue}",
diff --git a/spec/controllers/api/v1/accounts/assignment_policies/inboxes_controller_spec.rb b/spec/controllers/api/v1/accounts/assignment_policies/inboxes_controller_spec.rb
new file mode 100644
index 000000000..6b3c677c5
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/assignment_policies/inboxes_controller_spec.rb
@@ -0,0 +1,63 @@
+require 'rails_helper'
+
+RSpec.describe 'Assignment Policy Inboxes API', type: :request do
+ let(:account) { create(:account) }
+ let(:assignment_policy) { create(:assignment_policy, account: account) }
+
+ describe 'GET /api/v1/accounts/{account_id}/assignment_policies/{assignment_policy_id}/inboxes' do
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}/inboxes"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ context 'when assignment policy has associated inboxes' do
+ before do
+ inbox1 = create(:inbox, account: account)
+ inbox2 = create(:inbox, account: account)
+ create(:inbox_assignment_policy, inbox: inbox1, assignment_policy: assignment_policy)
+ create(:inbox_assignment_policy, inbox: inbox2, assignment_policy: assignment_policy)
+ end
+
+ it 'returns all inboxes associated with the assignment policy' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}/inboxes",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['inboxes']).to be_an(Array)
+ expect(json_response['inboxes'].length).to eq(2)
+ end
+ end
+
+ context 'when assignment policy has no associated inboxes' do
+ it 'returns empty array' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}/inboxes",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['inboxes']).to eq([])
+ end
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}/inboxes",
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/api/v1/accounts/assignment_policies_controller_spec.rb b/spec/controllers/api/v1/accounts/assignment_policies_controller_spec.rb
new file mode 100644
index 000000000..f882ec992
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/assignment_policies_controller_spec.rb
@@ -0,0 +1,326 @@
+require 'rails_helper'
+
+RSpec.describe 'Assignment Policies API', type: :request do
+ let(:account) { create(:account) }
+
+ describe 'GET /api/v1/accounts/{account.id}/assignment_policies' do
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ before do
+ create_list(:assignment_policy, 3, account: account)
+ end
+
+ it 'returns all assignment policies for the account' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response.length).to eq(3)
+ expect(json_response.first.keys).to include('id', 'name', 'description')
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies",
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'GET /api/v1/accounts/{account.id}/assignment_policies/:id' do
+ let(:assignment_policy) { create(:assignment_policy, account: account) }
+
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ it 'returns the assignment policy' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['id']).to eq(assignment_policy.id)
+ expect(json_response['name']).to eq(assignment_policy.name)
+ end
+
+ it 'returns not found for non-existent policy' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies/999999",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'POST /api/v1/accounts/{account.id}/assignment_policies' do
+ let(:valid_params) do
+ {
+ assignment_policy: {
+ name: 'New Assignment Policy',
+ description: 'Policy for new team',
+ conversation_priority: 'longest_waiting',
+ fair_distribution_limit: 15,
+ enabled: true
+ }
+ }
+ end
+
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ post "/api/v1/accounts/#{account.id}/assignment_policies", params: valid_params
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ it 'creates a new assignment policy' do
+ expect do
+ post "/api/v1/accounts/#{account.id}/assignment_policies",
+ headers: admin.create_new_auth_token,
+ params: valid_params,
+ as: :json
+ end.to change(AssignmentPolicy, :count).by(1)
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['name']).to eq('New Assignment Policy')
+ expect(json_response['conversation_priority']).to eq('longest_waiting')
+ end
+
+ it 'creates policy with minimal required params' do
+ minimal_params = { assignment_policy: { name: 'Minimal Policy' } }
+
+ expect do
+ post "/api/v1/accounts/#{account.id}/assignment_policies",
+ headers: admin.create_new_auth_token,
+ params: minimal_params,
+ as: :json
+ end.to change(AssignmentPolicy, :count).by(1)
+
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'prevents duplicate policy names within account' do
+ create(:assignment_policy, account: account, name: 'Duplicate Policy')
+ duplicate_params = { assignment_policy: { name: 'Duplicate Policy' } }
+
+ expect do
+ post "/api/v1/accounts/#{account.id}/assignment_policies",
+ headers: admin.create_new_auth_token,
+ params: duplicate_params,
+ as: :json
+ end.not_to change(AssignmentPolicy, :count)
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ end
+
+ it 'validates required fields' do
+ invalid_params = { assignment_policy: { name: '' } }
+
+ post "/api/v1/accounts/#{account.id}/assignment_policies",
+ headers: admin.create_new_auth_token,
+ params: invalid_params,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ post "/api/v1/accounts/#{account.id}/assignment_policies",
+ headers: agent.create_new_auth_token,
+ params: valid_params,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'PUT /api/v1/accounts/{account.id}/assignment_policies/:id' do
+ let(:assignment_policy) { create(:assignment_policy, account: account, name: 'Original Policy') }
+ let(:update_params) do
+ {
+ assignment_policy: {
+ name: 'Updated Policy',
+ description: 'Updated description',
+ fair_distribution_limit: 20
+ }
+ }
+ end
+
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ put "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ params: update_params
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ it 'updates the assignment policy' do
+ put "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: admin.create_new_auth_token,
+ params: update_params,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ assignment_policy.reload
+ expect(assignment_policy.name).to eq('Updated Policy')
+ expect(assignment_policy.fair_distribution_limit).to eq(20)
+ end
+
+ it 'allows partial updates' do
+ partial_params = { assignment_policy: { enabled: false } }
+
+ put "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: admin.create_new_auth_token,
+ params: partial_params,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(assignment_policy.reload.enabled).to be(false)
+ expect(assignment_policy.name).to eq('Original Policy') # unchanged
+ end
+
+ it 'prevents duplicate names during update' do
+ create(:assignment_policy, account: account, name: 'Existing Policy')
+ duplicate_params = { assignment_policy: { name: 'Existing Policy' } }
+
+ put "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: admin.create_new_auth_token,
+ params: duplicate_params,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ end
+
+ it 'returns not found for non-existent policy' do
+ put "/api/v1/accounts/#{account.id}/assignment_policies/999999",
+ headers: admin.create_new_auth_token,
+ params: update_params,
+ as: :json
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ put "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: agent.create_new_auth_token,
+ params: update_params,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'DELETE /api/v1/accounts/{account.id}/assignment_policies/:id' do
+ let(:assignment_policy) { create(:assignment_policy, account: account) }
+
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ delete "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ it 'deletes the assignment policy' do
+ assignment_policy # create it first
+
+ expect do
+ delete "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: admin.create_new_auth_token,
+ as: :json
+ end.to change(AssignmentPolicy, :count).by(-1)
+
+ expect(response).to have_http_status(:ok)
+ end
+
+ it 'cascades deletion to associated inbox assignment policies' do
+ inbox = create(:inbox, account: account)
+ create(:inbox_assignment_policy, inbox: inbox, assignment_policy: assignment_policy)
+
+ expect do
+ delete "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: admin.create_new_auth_token,
+ as: :json
+ end.to change(InboxAssignmentPolicy, :count).by(-1)
+
+ expect(response).to have_http_status(:ok)
+ end
+
+ it 'returns not found for non-existent policy' do
+ delete "/api/v1/accounts/#{account.id}/assignment_policies/999999",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ delete "/api/v1/accounts/#{account.id}/assignment_policies/#{assignment_policy.id}",
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/api/v1/accounts/inboxes/assignment_policies_controller_spec.rb b/spec/controllers/api/v1/accounts/inboxes/assignment_policies_controller_spec.rb
new file mode 100644
index 000000000..71ff464f8
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/inboxes/assignment_policies_controller_spec.rb
@@ -0,0 +1,195 @@
+require 'rails_helper'
+
+RSpec.describe 'Inbox Assignment Policies API', type: :request do
+ let(:account) { create(:account) }
+ let(:inbox) { create(:inbox, account: account) }
+ let(:assignment_policy) { create(:assignment_policy, account: account) }
+
+ describe 'GET /api/v1/accounts/{account_id}/inboxes/{inbox_id}/assignment_policy' do
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ context 'when inbox has an assignment policy' do
+ before do
+ create(:inbox_assignment_policy, inbox: inbox, assignment_policy: assignment_policy)
+ end
+
+ it 'returns the assignment policy for the inbox' do
+ get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['id']).to eq(assignment_policy.id)
+ expect(json_response['name']).to eq(assignment_policy.name)
+ end
+ end
+
+ context 'when inbox has no assignment policy' do
+ it 'returns not found' do
+ get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'POST /api/v1/accounts/{account_id}/inboxes/{inbox_id}/assignment_policy' do
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ params: { assignment_policy_id: assignment_policy.id }
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ it 'assigns a policy to the inbox' do
+ expect do
+ post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ params: { assignment_policy_id: assignment_policy.id },
+ headers: admin.create_new_auth_token,
+ as: :json
+ end.to change(InboxAssignmentPolicy, :count).by(1)
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['id']).to eq(assignment_policy.id)
+ end
+
+ it 'replaces existing assignment policy for inbox' do
+ other_policy = create(:assignment_policy, account: account)
+ create(:inbox_assignment_policy, inbox: inbox, assignment_policy: other_policy)
+
+ expect do
+ post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ params: { assignment_policy_id: assignment_policy.id },
+ headers: admin.create_new_auth_token,
+ as: :json
+ end.not_to change(InboxAssignmentPolicy, :count)
+
+ expect(response).to have_http_status(:success)
+ expect(inbox.reload.inbox_assignment_policy.assignment_policy).to eq(assignment_policy)
+ end
+
+ it 'returns not found for invalid assignment policy' do
+ post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ params: { assignment_policy_id: 999_999 },
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:not_found)
+ end
+
+ it 'returns not found for invalid inbox' do
+ post "/api/v1/accounts/#{account.id}/inboxes/999999/assignment_policy",
+ params: { assignment_policy_id: assignment_policy.id },
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ params: { assignment_policy_id: assignment_policy.id },
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'DELETE /api/v1/accounts/{account_id}/inboxes/{inbox_id}/assignment_policy' do
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ delete "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated admin' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ context 'when inbox has an assignment policy' do
+ before do
+ create(:inbox_assignment_policy, inbox: inbox, assignment_policy: assignment_policy)
+ end
+
+ it 'removes the assignment policy from inbox' do
+ expect do
+ delete "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ headers: admin.create_new_auth_token,
+ as: :json
+ end.to change(InboxAssignmentPolicy, :count).by(-1)
+
+ expect(response).to have_http_status(:success)
+ expect(inbox.reload.inbox_assignment_policy).to be_nil
+ end
+ end
+
+ context 'when inbox has no assignment policy' do
+ it 'returns error' do
+ expect do
+ delete "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ headers: admin.create_new_auth_token,
+ as: :json
+ end.not_to change(InboxAssignmentPolicy, :count)
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ it 'returns not found for invalid inbox' do
+ delete "/api/v1/accounts/#{account.id}/inboxes/999999/assignment_policy",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context 'when it is an agent' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'returns unauthorized' do
+ delete "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/assignment_policy",
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/jobs/captain/conversation/response_builder_job_spec.rb b/spec/enterprise/jobs/captain/conversation/response_builder_job_spec.rb
index c21205d52..23ed2ecec 100644
--- a/spec/enterprise/jobs/captain/conversation/response_builder_job_spec.rb
+++ b/spec/enterprise/jobs/captain/conversation/response_builder_job_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe Captain::Conversation::ResponseBuilderJob, type: :job do
describe '#perform' do
let(:conversation) { create(:conversation, inbox: inbox, account: account) }
let(:mock_llm_chat_service) { instance_double(Captain::Llm::AssistantChatService) }
+ let(:mock_agent_runner_service) { instance_double(Captain::Assistant::AgentRunnerService) }
before do
create(:message, conversation: conversation, content: 'Hello', message_type: :incoming)
@@ -16,19 +17,79 @@ RSpec.describe Captain::Conversation::ResponseBuilderJob, type: :job do
allow(inbox).to receive(:captain_active?).and_return(true)
allow(Captain::Llm::AssistantChatService).to receive(:new).and_return(mock_llm_chat_service)
allow(mock_llm_chat_service).to receive(:generate_response).and_return({ 'response' => 'Hey, welcome to Captain Specs' })
+ allow(Captain::Assistant::AgentRunnerService).to receive(:new).and_return(mock_agent_runner_service)
+ allow(mock_agent_runner_service).to receive(:generate_response).and_return({ 'response' => 'Hey, welcome to Captain V2' })
end
- it 'generates and processes response' do
- described_class.perform_now(conversation, assistant)
- expect(conversation.messages.count).to eq(2)
- expect(conversation.messages.outgoing.count).to eq(1)
- expect(conversation.messages.last.content).to eq('Hey, welcome to Captain Specs')
+ context 'when captain_v2 is disabled' do
+ before do
+ allow(account).to receive(:feature_enabled?).and_return(false)
+ allow(account).to receive(:feature_enabled?).with('captain_integration_v2').and_return(false)
+ end
+
+ it 'uses Captain::Llm::AssistantChatService' do
+ expect(Captain::Llm::AssistantChatService).to receive(:new).with(assistant: assistant)
+ expect(Captain::Assistant::AgentRunnerService).not_to receive(:new)
+
+ described_class.perform_now(conversation, assistant)
+ expect(conversation.messages.last.content).to eq('Hey, welcome to Captain Specs')
+ end
+
+ it 'generates and processes response' do
+ described_class.perform_now(conversation, assistant)
+ expect(conversation.messages.count).to eq(2)
+ expect(conversation.messages.outgoing.count).to eq(1)
+ expect(conversation.messages.last.content).to eq('Hey, welcome to Captain Specs')
+ end
+
+ it 'increments usage response' do
+ described_class.perform_now(conversation, assistant)
+ account.reload
+ expect(account.usage_limits[:captain][:responses][:consumed]).to eq(1)
+ end
end
- it 'increments usage response' do
- described_class.perform_now(conversation, assistant)
- account.reload
- expect(account.usage_limits[:captain][:responses][:consumed]).to eq(1)
+ context 'when captain_v2 is enabled' do
+ before do
+ allow(account).to receive(:feature_enabled?).and_return(false)
+ allow(account).to receive(:feature_enabled?).with('captain_integration_v2').and_return(true)
+ end
+
+ it 'uses Captain::Assistant::AgentRunnerService' do
+ expect(Captain::Assistant::AgentRunnerService).to receive(:new).with(
+ assistant: assistant,
+ conversation: conversation
+ )
+ expect(Captain::Llm::AssistantChatService).not_to receive(:new)
+
+ described_class.perform_now(conversation, assistant)
+ expect(conversation.messages.last.content).to eq('Hey, welcome to Captain V2')
+ end
+
+ it 'passes message history to agent runner service' do
+ expected_messages = [
+ { content: 'Hello', role: 'user' }
+ ]
+
+ expect(mock_agent_runner_service).to receive(:generate_response).with(
+ message_history: expected_messages
+ )
+
+ described_class.perform_now(conversation, assistant)
+ end
+
+ it 'generates and processes response' do
+ described_class.perform_now(conversation, assistant)
+ expect(conversation.messages.count).to eq(2)
+ expect(conversation.messages.outgoing.count).to eq(1)
+ expect(conversation.messages.last.content).to eq('Hey, welcome to Captain V2')
+ end
+
+ it 'increments usage response' do
+ described_class.perform_now(conversation, assistant)
+ account.reload
+ expect(account.usage_limits[:captain][:responses][:consumed]).to eq(1)
+ end
end
context 'when message contains an image' do
diff --git a/spec/enterprise/lib/captain/prompt_renderer_spec.rb b/spec/enterprise/lib/captain/prompt_renderer_spec.rb
new file mode 100644
index 000000000..761d55f99
--- /dev/null
+++ b/spec/enterprise/lib/captain/prompt_renderer_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Captain::PromptRenderer do
+ let(:template_name) { 'test_template' }
+ let(:template_content) { 'Hello {{name}}, your balance is {{balance}}' }
+ let(:template_path) { Rails.root.join('enterprise', 'lib', 'captain', 'prompts', "#{template_name}.liquid") }
+ let(:context) { { name: 'John', balance: 100 } }
+
+ before do
+ allow(File).to receive(:exist?).and_return(false)
+ allow(File).to receive(:exist?).with(template_path).and_return(true)
+ allow(File).to receive(:read).with(template_path).and_return(template_content)
+ end
+
+ describe '.render' do
+ it 'renders template with context' do
+ result = described_class.render(template_name, context)
+
+ expect(result).to eq('Hello John, your balance is 100')
+ end
+
+ it 'handles string keys in context' do
+ string_context = { 'name' => 'Jane', 'balance' => 200 }
+ result = described_class.render(template_name, string_context)
+
+ expect(result).to eq('Hello Jane, your balance is 200')
+ end
+
+ it 'handles mixed symbol and string keys' do
+ mixed_context = { :name => 'Bob', 'balance' => 300 }
+ result = described_class.render(template_name, mixed_context)
+
+ expect(result).to eq('Hello Bob, your balance is 300')
+ end
+
+ it 'handles nested hash context' do
+ nested_template = 'User: {{user.name}}, Account: {{user.account.type}}'
+ nested_context = { user: { name: 'Alice', account: { type: 'premium' } } }
+
+ allow(File).to receive(:read).with(template_path).and_return(nested_template)
+
+ result = described_class.render(template_name, nested_context)
+
+ expect(result).to eq('User: Alice, Account: premium')
+ end
+
+ it 'handles empty context' do
+ simple_template = 'Hello World'
+ allow(File).to receive(:read).with(template_path).and_return(simple_template)
+
+ result = described_class.render(template_name, {})
+
+ expect(result).to eq('Hello World')
+ end
+
+ it 'loads and parses liquid template' do
+ liquid_template_double = instance_double(Liquid::Template)
+ allow(Liquid::Template).to receive(:parse).with(template_content).and_return(liquid_template_double)
+ allow(liquid_template_double).to receive(:render).with(hash_including('name', 'balance')).and_return('rendered')
+
+ result = described_class.render(template_name, context)
+
+ expect(result).to eq('rendered')
+ expect(Liquid::Template).to have_received(:parse).with(template_content)
+ end
+ end
+
+ describe '.load_template' do
+ it 'reads template file from correct path' do
+ described_class.send(:load_template, template_name)
+
+ expect(File).to have_received(:read).with(template_path)
+ end
+
+ it 'raises error when template does not exist' do
+ allow(File).to receive(:exist?).with(template_path).and_return(false)
+
+ expect { described_class.send(:load_template, template_name) }
+ .to raise_error("Template not found: #{template_name}")
+ end
+
+ it 'constructs correct template path' do
+ expected_path = Rails.root.join('enterprise/lib/captain/prompts/my_template.liquid')
+ allow(File).to receive(:exist?).with(expected_path).and_return(true)
+ allow(File).to receive(:read).with(expected_path).and_return('test content')
+
+ described_class.send(:load_template, 'my_template')
+
+ expect(File).to have_received(:exist?).with(expected_path)
+ end
+ end
+
+ describe '.stringify_keys' do
+ it 'converts symbol keys to strings' do
+ hash = { name: 'John', age: 30 }
+ result = described_class.send(:stringify_keys, hash)
+
+ expect(result).to eq({ 'name' => 'John', 'age' => 30 })
+ end
+
+ it 'handles nested hashes' do
+ hash = { user: { name: 'John', profile: { age: 30 } } }
+ result = described_class.send(:stringify_keys, hash)
+
+ expect(result).to eq({ 'user' => { 'name' => 'John', 'profile' => { 'age' => 30 } } })
+ end
+
+ it 'handles arrays with hashes' do
+ hash = { users: [{ name: 'John' }, { name: 'Jane' }] }
+ result = described_class.send(:stringify_keys, hash)
+
+ expect(result).to eq({ 'users' => [{ 'name' => 'John' }, { 'name' => 'Jane' }] })
+ end
+
+ it 'handles empty hash' do
+ result = described_class.send(:stringify_keys, {})
+
+ expect(result).to eq({})
+ end
+ end
+end
diff --git a/spec/enterprise/models/assignment_policy_spec.rb b/spec/enterprise/models/assignment_policy_spec.rb
new file mode 100644
index 000000000..0d21cc3c4
--- /dev/null
+++ b/spec/enterprise/models/assignment_policy_spec.rb
@@ -0,0 +1,18 @@
+require 'rails_helper'
+
+RSpec.describe AssignmentPolicy do
+ let(:account) { create(:account) }
+
+ describe 'enum values' do
+ let(:assignment_policy) { create(:assignment_policy, account: account) }
+
+ describe 'assignment_order' do
+ it 'can be set to balanced' do
+ assignment_policy.update!(assignment_order: :balanced)
+ expect(assignment_policy.assignment_order).to eq('balanced')
+ expect(assignment_policy.round_robin?).to be false
+ expect(assignment_policy.balanced?).to be true
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/models/concerns/agentable_spec.rb b/spec/enterprise/models/concerns/agentable_spec.rb
new file mode 100644
index 000000000..767e51d44
--- /dev/null
+++ b/spec/enterprise/models/concerns/agentable_spec.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Concerns::Agentable do
+ let(:dummy_class) do
+ Class.new do
+ include Concerns::Agentable
+
+ attr_accessor :temperature
+
+ def initialize(name: 'Test Agent', temperature: 0.8)
+ @name = name
+ @temperature = temperature
+ end
+
+ def self.name
+ 'DummyClass'
+ end
+
+ private
+
+ def agent_name
+ @name
+ end
+
+ def prompt_context
+ { base_key: 'base_value' }
+ end
+ end
+ end
+
+ let(:dummy_instance) { dummy_class.new }
+ let(:mock_agents_agent) { instance_double(Agents::Agent) }
+ let(:mock_installation_config) { instance_double(InstallationConfig, value: 'gpt-4-turbo') }
+
+ before do
+ allow(Agents::Agent).to receive(:new).and_return(mock_agents_agent)
+ allow(InstallationConfig).to receive(:find_by).with(name: 'CAPTAIN_OPEN_AI_MODEL').and_return(mock_installation_config)
+ allow(Captain::PromptRenderer).to receive(:render).and_return('rendered_template')
+ end
+
+ describe '#agent' do
+ it 'creates an Agents::Agent with correct parameters' do
+ expect(Agents::Agent).to receive(:new).with(
+ name: 'Test Agent',
+ instructions: instance_of(Proc),
+ tools: [],
+ model: 'gpt-4-turbo',
+ temperature: 0.8,
+ response_schema: Captain::ResponseSchema
+ )
+
+ dummy_instance.agent
+ end
+
+ it 'converts nil temperature to 0.0' do
+ dummy_instance.temperature = nil
+
+ expect(Agents::Agent).to receive(:new).with(
+ hash_including(temperature: 0.0)
+ )
+
+ dummy_instance.agent
+ end
+
+ it 'converts temperature to float' do
+ dummy_instance.temperature = '0.5'
+
+ expect(Agents::Agent).to receive(:new).with(
+ hash_including(temperature: 0.5)
+ )
+
+ dummy_instance.agent
+ end
+ end
+
+ describe '#agent_instructions' do
+ it 'calls Captain::PromptRenderer with base context' do
+ expect(Captain::PromptRenderer).to receive(:render).with(
+ 'dummy_class',
+ hash_including(base_key: 'base_value')
+ )
+
+ dummy_instance.agent_instructions
+ end
+
+ it 'merges context state when provided' do
+ context_double = instance_double(Agents::RunContext,
+ context: {
+ state: {
+ conversation: { id: 123 },
+ contact: { name: 'John' }
+ }
+ })
+
+ expected_context = {
+ base_key: 'base_value',
+ conversation: { id: 123 },
+ contact: { name: 'John' }
+ }
+
+ expect(Captain::PromptRenderer).to receive(:render).with(
+ 'dummy_class',
+ hash_including(expected_context)
+ )
+
+ dummy_instance.agent_instructions(context_double)
+ end
+
+ it 'handles context without state' do
+ context_double = instance_double(Agents::RunContext, context: {})
+
+ expect(Captain::PromptRenderer).to receive(:render).with(
+ 'dummy_class',
+ hash_including(
+ base_key: 'base_value',
+ conversation: {},
+ contact: {}
+ )
+ )
+
+ dummy_instance.agent_instructions(context_double)
+ end
+ end
+
+ describe '#template_name' do
+ it 'returns underscored class name' do
+ expect(dummy_instance.send(:template_name)).to eq('dummy_class')
+ end
+ end
+
+ describe '#agent_tools' do
+ it 'returns empty array by default' do
+ expect(dummy_instance.send(:agent_tools)).to eq([])
+ end
+ end
+
+ describe '#agent_model' do
+ it 'returns value from InstallationConfig when present' do
+ expect(dummy_instance.send(:agent_model)).to eq('gpt-4-turbo')
+ end
+
+ it 'returns default model when config not found' do
+ allow(InstallationConfig).to receive(:find_by).and_return(nil)
+
+ expect(dummy_instance.send(:agent_model)).to eq('gpt-4.1-mini')
+ end
+
+ it 'returns default model when config value is nil' do
+ allow(mock_installation_config).to receive(:value).and_return(nil)
+
+ expect(dummy_instance.send(:agent_model)).to eq('gpt-4.1-mini')
+ end
+ end
+
+ describe '#agent_response_schema' do
+ it 'returns Captain::ResponseSchema' do
+ expect(dummy_instance.send(:agent_response_schema)).to eq(Captain::ResponseSchema)
+ end
+ end
+
+ describe 'required methods' do
+ let(:incomplete_class) do
+ Class.new do
+ include Concerns::Agentable
+ end
+ end
+
+ let(:incomplete_instance) { incomplete_class.new }
+
+ describe '#agent_name' do
+ it 'raises NotImplementedError when not implemented' do
+ expect { incomplete_instance.send(:agent_name) }
+ .to raise_error(NotImplementedError, /must implement agent_name/)
+ end
+ end
+
+ describe '#prompt_context' do
+ it 'raises NotImplementedError when not implemented' do
+ expect { incomplete_instance.send(:prompt_context) }
+ .to raise_error(NotImplementedError, /must implement prompt_context/)
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/services/captain/assistant/agent_runner_service_spec.rb b/spec/enterprise/services/captain/assistant/agent_runner_service_spec.rb
new file mode 100644
index 000000000..f31177fc2
--- /dev/null
+++ b/spec/enterprise/services/captain/assistant/agent_runner_service_spec.rb
@@ -0,0 +1,320 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Captain::Assistant::AgentRunnerService do
+ let(:account) { create(:account) }
+ let(:inbox) { create(:inbox, account: account) }
+ let(:contact) { create(:contact, account: account) }
+ let(:conversation) { create(:conversation, account: account, inbox: inbox, contact: contact) }
+ let(:assistant) { create(:captain_assistant, account: account) }
+ let(:scenario) { create(:captain_scenario, assistant: assistant, enabled: true) }
+
+ let(:mock_runner) { instance_double(Agents::Runner) }
+ let(:mock_agent) { instance_double(Agents::Agent) }
+ let(:mock_scenario_agent) { instance_double(Agents::Agent) }
+ let(:mock_result) { instance_double(Agents::RunResult, output: { 'response' => 'Test response' }) }
+
+ let(:message_history) do
+ [
+ { role: 'user', content: 'Hello there' },
+ { role: 'assistant', content: 'Hi! How can I help you?', agent_name: 'Assistant' },
+ { role: 'user', content: 'I need help with my account' }
+ ]
+ end
+
+ before do
+ allow(assistant).to receive(:agent).and_return(mock_agent)
+ scenarios_relation = instance_double(Captain::Scenario)
+ allow(scenarios_relation).to receive(:enabled).and_return([scenario])
+ allow(assistant).to receive(:scenarios).and_return(scenarios_relation)
+ allow(scenario).to receive(:agent).and_return(mock_scenario_agent)
+ allow(Agents::Runner).to receive(:with_agents).and_return(mock_runner)
+ allow(mock_runner).to receive(:run).and_return(mock_result)
+ allow(mock_agent).to receive(:register_handoffs)
+ allow(mock_scenario_agent).to receive(:register_handoffs)
+ end
+
+ describe '#initialize' do
+ it 'sets instance variables correctly' do
+ service = described_class.new(assistant: assistant, conversation: conversation)
+
+ expect(service.instance_variable_get(:@assistant)).to eq(assistant)
+ expect(service.instance_variable_get(:@conversation)).to eq(conversation)
+ expect(service.instance_variable_get(:@callbacks)).to eq({})
+ end
+
+ it 'accepts callbacks parameter' do
+ callbacks = { on_agent_thinking: proc { |x| x } }
+ service = described_class.new(assistant: assistant, callbacks: callbacks)
+
+ expect(service.instance_variable_get(:@callbacks)).to eq(callbacks)
+ end
+ end
+
+ describe '#generate_response' do
+ subject(:service) { described_class.new(assistant: assistant, conversation: conversation) }
+
+ it 'builds agents and wires them together' do
+ expect(assistant).to receive(:agent).and_return(mock_agent)
+ scenarios_relation = instance_double(Captain::Scenario)
+ allow(scenarios_relation).to receive(:enabled).and_return([scenario])
+ expect(assistant).to receive(:scenarios).and_return(scenarios_relation)
+ expect(scenario).to receive(:agent).and_return(mock_scenario_agent)
+ expect(mock_agent).to receive(:register_handoffs).with(mock_scenario_agent)
+ expect(mock_scenario_agent).to receive(:register_handoffs).with(mock_agent)
+
+ service.generate_response(message_history: message_history)
+ end
+
+ it 'creates runner with agents' do
+ expect(Agents::Runner).to receive(:with_agents).with(mock_agent, mock_scenario_agent)
+
+ service.generate_response(message_history: message_history)
+ end
+
+ it 'runs agent with extracted user message and context' do
+ expected_context = {
+ conversation_history: [
+ { role: :user, content: 'Hello there', agent_name: nil },
+ { role: :assistant, content: 'Hi! How can I help you?', agent_name: 'Assistant' },
+ { role: :user, content: 'I need help with my account', agent_name: nil }
+ ],
+ state: hash_including(
+ account_id: account.id,
+ assistant_id: assistant.id,
+ conversation: hash_including(id: conversation.id),
+ contact: hash_including(id: contact.id)
+ )
+ }
+
+ expect(mock_runner).to receive(:run).with(
+ 'I need help with my account',
+ context: expected_context
+ )
+
+ service.generate_response(message_history: message_history)
+ end
+
+ it 'processes and formats agent result' do
+ result = service.generate_response(message_history: message_history)
+
+ expect(result).to eq({ 'response' => 'Test response' })
+ end
+
+ context 'when no scenarios are enabled' do
+ before do
+ scenarios_relation = instance_double(Captain::Scenario)
+ allow(scenarios_relation).to receive(:enabled).and_return([])
+ allow(assistant).to receive(:scenarios).and_return(scenarios_relation)
+ end
+
+ it 'only uses assistant agent' do
+ expect(Agents::Runner).to receive(:with_agents).with(mock_agent)
+ expect(mock_agent).not_to receive(:register_handoffs)
+
+ service.generate_response(message_history: message_history)
+ end
+ end
+
+ context 'when agent result is a string' do
+ let(:mock_result) { instance_double(Agents::RunResult, output: 'Simple string response') }
+
+ it 'formats string response correctly' do
+ result = service.generate_response(message_history: message_history)
+
+ expect(result).to eq({
+ 'response' => 'Simple string response',
+ 'reasoning' => 'Processed by agent'
+ })
+ end
+ end
+
+ context 'when an error occurs' do
+ let(:error) { StandardError.new('Test error') }
+
+ before do
+ allow(mock_runner).to receive(:run).and_raise(error)
+ allow(ChatwootExceptionTracker).to receive(:new).and_return(
+ instance_double(ChatwootExceptionTracker, capture_exception: true)
+ )
+ end
+
+ it 'captures exception and returns error response' do
+ expect(ChatwootExceptionTracker).to receive(:new).with(error, account: conversation.account)
+
+ result = service.generate_response(message_history: message_history)
+
+ expect(result).to eq({
+ 'response' => 'conversation_handoff',
+ 'reasoning' => 'Error occurred: Test error'
+ })
+ end
+
+ it 'logs error details' do
+ expect(Rails.logger).to receive(:error).with('[Captain V2] AgentRunnerService error: Test error')
+ expect(Rails.logger).to receive(:error).with(kind_of(String))
+
+ service.generate_response(message_history: message_history)
+ end
+
+ context 'when conversation is nil' do
+ subject(:service) { described_class.new(assistant: assistant, conversation: nil) }
+
+ it 'handles missing conversation gracefully' do
+ expect(ChatwootExceptionTracker).to receive(:new).with(error, account: nil)
+
+ result = service.generate_response(message_history: message_history)
+
+ expect(result).to eq({
+ 'response' => 'conversation_handoff',
+ 'reasoning' => 'Error occurred: Test error'
+ })
+ end
+ end
+ end
+ end
+
+ describe '#build_context' do
+ subject(:service) { described_class.new(assistant: assistant, conversation: conversation) }
+
+ it 'builds context with conversation history and state' do
+ context = service.send(:build_context, message_history)
+
+ expect(context).to include(
+ conversation_history: array_including(
+ { role: :user, content: 'Hello there', agent_name: nil },
+ { role: :assistant, content: 'Hi! How can I help you?', agent_name: 'Assistant' }
+ ),
+ state: hash_including(
+ account_id: account.id,
+ assistant_id: assistant.id
+ )
+ )
+ end
+
+ context 'with multimodal content' do
+ let(:multimodal_message_history) do
+ [
+ {
+ role: 'user',
+ content: [
+ { type: 'text', text: 'Can you help with this image?' },
+ { type: 'image_url', image_url: { url: 'https://example.com/image.jpg' } }
+ ]
+ }
+ ]
+ end
+
+ it 'extracts text content from multimodal messages' do
+ context = service.send(:build_context, multimodal_message_history)
+
+ expect(context[:conversation_history].first[:content]).to eq('Can you help with this image?')
+ end
+ end
+ end
+
+ describe '#extract_last_user_message' do
+ subject(:service) { described_class.new(assistant: assistant, conversation: conversation) }
+
+ it 'extracts the last user message' do
+ result = service.send(:extract_last_user_message, message_history)
+
+ expect(result).to eq('I need help with my account')
+ end
+ end
+
+ describe '#extract_text_from_content' do
+ subject(:service) { described_class.new(assistant: assistant, conversation: conversation) }
+
+ it 'extracts text from string content' do
+ result = service.send(:extract_text_from_content, 'Simple text')
+
+ expect(result).to eq('Simple text')
+ end
+
+ it 'extracts response from hash content' do
+ content = { 'response' => 'Hash response' }
+ result = service.send(:extract_text_from_content, content)
+
+ expect(result).to eq('Hash response')
+ end
+
+ it 'extracts text from multimodal array content' do
+ content = [
+ { type: 'text', text: 'First part' },
+ { type: 'image_url', image_url: { url: 'image.jpg' } },
+ { type: 'text', text: 'Second part' }
+ ]
+
+ result = service.send(:extract_text_from_content, content)
+
+ expect(result).to eq('First part Second part')
+ end
+ end
+
+ describe '#build_state' do
+ subject(:service) { described_class.new(assistant: assistant, conversation: conversation) }
+
+ it 'builds state with assistant and account information' do
+ state = service.send(:build_state)
+
+ expect(state).to include(
+ account_id: account.id,
+ assistant_id: assistant.id,
+ assistant_config: assistant.config
+ )
+ end
+
+ it 'includes conversation attributes when conversation is present' do
+ state = service.send(:build_state)
+
+ expect(state[:conversation]).to include(
+ id: conversation.id,
+ inbox_id: inbox.id,
+ contact_id: contact.id,
+ status: conversation.status
+ )
+ end
+
+ it 'includes contact attributes when contact is present' do
+ state = service.send(:build_state)
+
+ expect(state[:contact]).to include(
+ id: contact.id,
+ name: contact.name,
+ email: contact.email
+ )
+ end
+
+ context 'when conversation is nil' do
+ subject(:service) { described_class.new(assistant: assistant, conversation: nil) }
+
+ it 'builds state without conversation and contact' do
+ state = service.send(:build_state)
+
+ expect(state).to include(
+ account_id: account.id,
+ assistant_id: assistant.id,
+ assistant_config: assistant.config
+ )
+ expect(state).not_to have_key(:conversation)
+ expect(state).not_to have_key(:contact)
+ end
+ end
+ end
+
+ describe 'constants' do
+ it 'defines conversation state attributes' do
+ expect(described_class::CONVERSATION_STATE_ATTRIBUTES).to include(
+ :id, :display_id, :inbox_id, :contact_id, :status, :priority
+ )
+ end
+
+ it 'defines contact state attributes' do
+ expect(described_class::CONTACT_STATE_ATTRIBUTES).to include(
+ :id, :name, :email, :phone_number, :identifier, :contact_type
+ )
+ end
+ end
+end
diff --git a/spec/factories/assignment_policies.rb b/spec/factories/assignment_policies.rb
new file mode 100644
index 000000000..6a696caa4
--- /dev/null
+++ b/spec/factories/assignment_policies.rb
@@ -0,0 +1,12 @@
+FactoryBot.define do
+ factory :assignment_policy do
+ account
+ sequence(:name) { |n| "Assignment Policy #{n}" }
+ description { 'Test assignment policy description' }
+ assignment_order { 0 }
+ conversation_priority { 0 }
+ fair_distribution_limit { 10 }
+ fair_distribution_window { 3600 }
+ enabled { true }
+ end
+end
diff --git a/spec/factories/channel/channel_whatsapp.rb b/spec/factories/channel/channel_whatsapp.rb
index ad2bab241..dae7eb04f 100644
--- a/spec/factories/channel/channel_whatsapp.rb
+++ b/spec/factories/channel/channel_whatsapp.rb
@@ -63,6 +63,25 @@ FactoryBot.define do
],
'sub_category' => 'CUSTOM',
'parameter_format' => 'NAMED'
+ },
+ {
+ 'name' => 'test_no_params_template',
+ 'status' => 'APPROVED',
+ 'category' => 'UTILITY',
+ 'language' => 'en',
+ 'namespace' => 'ed41a221_133a_4558_a1d6_192960e3aee9',
+ 'id' => '9876543210987654',
+ 'length' => 1,
+ 'parameter_format' => 'POSITIONAL',
+ 'previous_category' => 'MARKETING',
+ 'sub_category' => 'CUSTOM',
+ 'components' => [
+ {
+ 'text' => 'Thank you for contacting us! Your request has been processed successfully. Have a great day! 🙂',
+ 'type' => 'BODY'
+ }
+ ],
+ 'rejected_reason' => 'NONE'
}]
end
message_templates_last_updated { Time.now.utc }
diff --git a/spec/factories/inbox_assignment_policies.rb b/spec/factories/inbox_assignment_policies.rb
new file mode 100644
index 000000000..80bcae223
--- /dev/null
+++ b/spec/factories/inbox_assignment_policies.rb
@@ -0,0 +1,6 @@
+FactoryBot.define do
+ factory :inbox_assignment_policy do
+ inbox
+ assignment_policy
+ end
+end
diff --git a/spec/helpers/reporting_event_helper_spec.rb b/spec/helpers/reporting_event_helper_spec.rb
new file mode 100644
index 000000000..e4a3c0255
--- /dev/null
+++ b/spec/helpers/reporting_event_helper_spec.rb
@@ -0,0 +1,177 @@
+require 'rails_helper'
+
+RSpec.describe ReportingEventHelper, type: :helper do
+ describe '#last_non_human_activity' do
+ let(:account) { create(:account) }
+ let(:inbox) { create(:inbox, account: account) }
+ let(:user) { create(:user, account: account) }
+ let(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: user) }
+
+ context 'when conversation has no events' do
+ it 'returns conversation created_at' do
+ expect(helper.last_non_human_activity(conversation)).to eq(conversation.created_at)
+ end
+ end
+
+ context 'when conversation has bot handoff event' do
+ let!(:handoff_event) do
+ create(:reporting_event,
+ name: 'conversation_bot_handoff',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ event_end_time: 2.hours.ago)
+ end
+
+ it 'returns handoff event end time' do
+ expect(helper.last_non_human_activity(conversation).to_i).to eq(handoff_event.event_end_time.to_i)
+ end
+ end
+
+ context 'when conversation has bot resolved event' do
+ let!(:bot_resolved_event) do
+ create(:reporting_event,
+ name: 'conversation_bot_resolved',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ event_end_time: 3.hours.ago)
+ end
+
+ it 'returns bot resolved event end time' do
+ expect(helper.last_non_human_activity(conversation).to_i).to eq(bot_resolved_event.event_end_time.to_i)
+ end
+ end
+
+ context 'when conversation is reopened after bot resolution' do
+ let(:creation_time) { 5.days.ago }
+ let(:bot_resolution_time) { 5.days.ago + 5.minutes }
+ let(:reopening_time) { 1.hour.ago }
+
+ let!(:conversation) do
+ create(:conversation,
+ account: account,
+ inbox: inbox,
+ assignee: user,
+ created_at: creation_time)
+ end
+
+ before do
+ # First opened event
+ create(:reporting_event,
+ name: 'conversation_opened',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ value: 0,
+ event_start_time: creation_time,
+ event_end_time: creation_time)
+
+ # Bot resolved event
+ create(:reporting_event,
+ name: 'conversation_bot_resolved',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ event_start_time: creation_time,
+ event_end_time: bot_resolution_time)
+
+ # Resolved event
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ event_start_time: creation_time,
+ event_end_time: bot_resolution_time)
+
+ # Reopened event
+ create(:reporting_event,
+ name: 'conversation_opened',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ value: (reopening_time - bot_resolution_time).to_i,
+ event_start_time: bot_resolution_time,
+ event_end_time: reopening_time)
+ end
+
+ it 'returns the reopening event time, not the creation time' do
+ # This is the key test: last_non_human_activity should return the reopening time
+ # so that first response time is calculated from when the conversation was reopened,
+ # not from when it was originally created
+ expect(helper.last_non_human_activity(conversation).to_i).to eq(reopening_time.to_i)
+
+ # Verify it's not returning the creation time or bot resolution time
+ expect(helper.last_non_human_activity(conversation).to_i).not_to eq(creation_time.to_i)
+ expect(helper.last_non_human_activity(conversation).to_i).not_to eq(bot_resolution_time.to_i)
+ end
+ end
+
+ context 'when conversation has multiple types of events' do
+ let(:opened_event_time) { 1.hour.ago }
+
+ before do
+ create(:reporting_event,
+ name: 'conversation_bot_resolved',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ event_end_time: 4.hours.ago)
+
+ create(:reporting_event,
+ name: 'conversation_bot_handoff',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ event_end_time: 3.hours.ago)
+
+ create(:reporting_event,
+ name: 'conversation_opened',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ event_end_time: opened_event_time)
+ end
+
+ it 'returns the most recent handoff or opened event' do
+ # opened_event is more recent than handoff_event
+ expect(helper.last_non_human_activity(conversation).to_i).to eq(opened_event_time.to_i)
+ end
+ end
+
+ context 'when conversation has multiple reopenings' do
+ let(:third_opened_time) { 30.minutes.ago }
+
+ before do
+ create(:reporting_event,
+ name: 'conversation_opened',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ value: 0,
+ event_end_time: 5.days.ago)
+
+ create(:reporting_event,
+ name: 'conversation_opened',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ value: 3600,
+ event_end_time: 2.days.ago)
+
+ create(:reporting_event,
+ name: 'conversation_opened',
+ conversation_id: conversation.id,
+ account_id: account.id,
+ inbox_id: inbox.id,
+ value: 7200,
+ event_end_time: third_opened_time)
+ end
+
+ it 'returns the most recent opened event' do
+ expect(helper.last_non_human_activity(conversation).to_i).to eq(third_opened_time.to_i)
+ end
+ end
+ end
+end
diff --git a/spec/jobs/webhooks/instagram_events_job_spec.rb b/spec/jobs/webhooks/instagram_events_job_spec.rb
index 9edd9a34d..21f042f1f 100644
--- a/spec/jobs/webhooks/instagram_events_job_spec.rb
+++ b/spec/jobs/webhooks/instagram_events_job_spec.rb
@@ -10,23 +10,6 @@ describe Webhooks::InstagramEventsJob do
end
let!(:account) { create(:account) }
- let!(:instagram_messenger_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') }
- let!(:instagram_messenger_inbox) { create(:inbox, channel: instagram_messenger_channel, account: account, greeting_enabled: false) }
- let!(:instagram_channel) { create(:channel_instagram, account: account, instagram_id: 'chatwoot-app-user-id-1') }
- let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) }
- # Combined message events into one helper
- let(:message_events) do
- {
- dm: build(:instagram_message_create_event).with_indifferent_access,
- standby: build(:instagram_message_standby_event).with_indifferent_access,
- unsend: build(:instagram_message_unsend_event).with_indifferent_access,
- attachment: build(:instagram_message_attachment_event).with_indifferent_access,
- story_mention: build(:instagram_story_mention_event).with_indifferent_access,
- story_mention_echo: build(:instagram_story_mention_event_with_echo).with_indifferent_access,
- messaging_seen: build(:messaging_seen_event).with_indifferent_access,
- unsupported: build(:instagram_message_unsupported_event).with_indifferent_access
- }
- end
def return_object_for(sender_id)
{ name: 'Jane',
@@ -38,21 +21,19 @@ describe Webhooks::InstagramEventsJob do
describe '#perform' do
context 'when handling messaging events for Instagram via Facebook page' do
+ let!(:instagram_messenger_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') }
+ let!(:instagram_messenger_inbox) { create(:inbox, channel: instagram_messenger_channel, account: account, greeting_enabled: false) }
let(:fb_object) { double }
- before do
- instagram_inbox.destroy
- end
-
it 'creates incoming message in the instagram inbox' do
+ dm_event = build(:instagram_message_create_event).with_indifferent_access
+ sender_id = dm_event[:entry][0][:messaging][0][:sender][:id]
+
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
- sender_id = message_events[:dm][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return(
return_object_for(sender_id).with_indifferent_access
)
- instagram_webhook.perform_now(message_events[:dm][:entry])
-
- instagram_messenger_inbox.reload
+ instagram_webhook.perform_now(dm_event[:entry])
expect(instagram_messenger_inbox.contacts.count).to be 1
expect(instagram_messenger_inbox.contacts.last.additional_attributes['social_instagram_user_name']).to eq 'some_user_name'
@@ -62,14 +43,14 @@ describe Webhooks::InstagramEventsJob do
end
it 'creates standby message in the instagram inbox' do
+ standby_event = build(:instagram_message_standby_event).with_indifferent_access
+ sender_id = standby_event[:entry][0][:standby][0][:sender][:id]
+
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
- sender_id = message_events[:standby][:entry][0][:standby][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return(
return_object_for(sender_id).with_indifferent_access
)
- instagram_webhook.perform_now(message_events[:standby][:entry])
-
- instagram_messenger_inbox.reload
+ instagram_webhook.perform_now(standby_event[:entry])
expect(instagram_messenger_inbox.contacts.count).to be 1
expect(instagram_messenger_inbox.contacts.last.additional_attributes['social_instagram_user_name']).to eq 'some_user_name'
@@ -81,9 +62,11 @@ describe Webhooks::InstagramEventsJob do
end
it 'handle instagram unsend message event' do
+ unsend_event = build(:instagram_message_unsend_event).with_indifferent_access
+ sender_id = unsend_event[:entry][0][:messaging][0][:sender][:id]
+
message = create(:message, inbox_id: instagram_messenger_inbox.id, source_id: 'message-id-to-delete')
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
- sender_id = message_events[:unsend][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return(
{
name: 'Jane',
@@ -96,7 +79,7 @@ describe Webhooks::InstagramEventsJob do
expect(instagram_messenger_inbox.messages.count).to be 1
- instagram_webhook.perform_now(message_events[:unsend][:entry])
+ instagram_webhook.perform_now(unsend_event[:entry])
expect(instagram_messenger_inbox.messages.last.content).to eq 'This message was deleted'
expect(instagram_messenger_inbox.messages.last.deleted).to be true
@@ -105,14 +88,14 @@ describe Webhooks::InstagramEventsJob do
end
it 'creates incoming message with attachments in the instagram inbox' do
+ attachment_event = build(:instagram_message_attachment_event).with_indifferent_access
+ sender_id = attachment_event[:entry][0][:messaging][0][:sender][:id]
+
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
- sender_id = message_events[:attachment][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return(
return_object_for(sender_id).with_indifferent_access
)
- instagram_webhook.perform_now(message_events[:attachment][:entry])
-
- instagram_messenger_inbox.reload
+ instagram_webhook.perform_now(attachment_event[:entry])
expect(instagram_messenger_inbox.contacts.count).to be 1
expect(instagram_messenger_inbox.messages.count).to be 1
@@ -120,8 +103,10 @@ describe Webhooks::InstagramEventsJob do
end
it 'creates incoming message with attachments in the instagram inbox for story mention' do
+ story_mention_event = build(:instagram_story_mention_event).with_indifferent_access
+ sender_id = story_mention_event[:entry][0][:messaging][0][:sender][:id]
+
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
- sender_id = message_events[:story_mention][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return(
return_object_for(sender_id).with_indifferent_access,
{ story:
@@ -137,9 +122,7 @@ describe Webhooks::InstagramEventsJob do
id: 'instagram-message-id-1234' }.with_indifferent_access
)
- instagram_webhook.perform_now(message_events[:story_mention][:entry])
-
- instagram_messenger_inbox.reload
+ instagram_webhook.perform_now(story_mention_event[:entry])
expect(instagram_messenger_inbox.messages.count).to be 1
expect(instagram_messenger_inbox.messages.last.attachments.count).to be 1
@@ -149,12 +132,12 @@ describe Webhooks::InstagramEventsJob do
end
it 'does not create contact or messages when Facebook API call fails' do
+ story_mention_echo_event = build(:instagram_story_mention_event_with_echo).with_indifferent_access
+
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_raise(Koala::Facebook::ClientError)
- instagram_webhook.perform_now(message_events[:story_mention_echo][:entry])
-
- instagram_messenger_inbox.reload
+ instagram_webhook.perform_now(story_mention_echo_event[:entry])
expect(instagram_messenger_inbox.contacts.count).to be 0
expect(instagram_messenger_inbox.contact_inboxes.count).to be 0
@@ -162,21 +145,23 @@ describe Webhooks::InstagramEventsJob do
end
it 'handle messaging_seen callback' do
- expect(Instagram::ReadStatusService).to receive(:new).with(params: message_events[:messaging_seen][:entry][0][:messaging][0],
+ messaging_seen_event = build(:messaging_seen_event).with_indifferent_access
+
+ expect(Instagram::ReadStatusService).to receive(:new).with(params: messaging_seen_event[:entry][0][:messaging][0],
channel: instagram_messenger_inbox.channel).and_call_original
- instagram_webhook.perform_now(message_events[:messaging_seen][:entry])
+ instagram_webhook.perform_now(messaging_seen_event[:entry])
end
it 'handles unsupported message' do
+ unsupported_event = build(:instagram_message_unsupported_event).with_indifferent_access
+ sender_id = unsupported_event[:entry][0][:messaging][0][:sender][:id]
+
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
- sender_id = message_events[:unsupported][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return(
return_object_for(sender_id).with_indifferent_access
)
- instagram_webhook.perform_now(message_events[:unsupported][:entry])
- instagram_messenger_inbox.reload
-
+ instagram_webhook.perform_now(unsupported_event[:entry])
expect(instagram_messenger_inbox.contacts.count).to be 1
expect(instagram_messenger_inbox.contacts.last.additional_attributes['social_instagram_user_name']).to eq 'some_user_name'
expect(instagram_messenger_inbox.conversations.count).to be 1
@@ -186,6 +171,9 @@ describe Webhooks::InstagramEventsJob do
end
context 'when handling messaging events for Instagram via Instagram login' do
+ let!(:instagram_channel) { create(:channel_instagram, account: account, instagram_id: 'chatwoot-app-user-id-1') }
+ let!(:instagram_inbox) { instagram_channel.inbox }
+
before do
instagram_channel.update(access_token: 'valid_instagram_token')
@@ -210,9 +198,8 @@ describe Webhooks::InstagramEventsJob do
end
it 'creates incoming message with correct contact info in the instagram direct inbox' do
- instagram_webhook.perform_now(message_events[:dm][:entry])
- instagram_inbox.reload
-
+ dm_event = build(:instagram_message_create_event).with_indifferent_access
+ instagram_webhook.perform_now(dm_event[:entry])
expect(instagram_inbox.contacts.count).to eq 1
expect(instagram_inbox.contacts.last.additional_attributes['social_instagram_user_name']).to eq 'some_user_name'
expect(instagram_inbox.conversations.count).to eq 1
@@ -221,7 +208,8 @@ describe Webhooks::InstagramEventsJob do
end
it 'sets correct instagram attributes on contact' do
- instagram_webhook.perform_now(message_events[:dm][:entry])
+ dm_event = build(:instagram_message_create_event).with_indifferent_access
+ instagram_webhook.perform_now(dm_event[:entry])
instagram_inbox.reload
contact = instagram_inbox.contacts.last
@@ -233,6 +221,8 @@ describe Webhooks::InstagramEventsJob do
end
it 'handle instagram unsend message event' do
+ unsend_event = build(:instagram_message_unsend_event).with_indifferent_access
+
message = create(:message, inbox_id: instagram_inbox.id, source_id: 'message-id-to-delete', content: 'random_text')
# Create attachment correctly with account association
@@ -244,7 +234,7 @@ describe Webhooks::InstagramEventsJob do
expect(instagram_inbox.messages.count).to be 1
- instagram_webhook.perform_now(message_events[:unsend][:entry])
+ instagram_webhook.perform_now(unsend_event[:entry])
message.reload
@@ -254,9 +244,8 @@ describe Webhooks::InstagramEventsJob do
end
it 'creates incoming message with attachments in the instagram direct inbox' do
- instagram_webhook.perform_now(message_events[:attachment][:entry])
-
- instagram_inbox.reload
+ attachment_event = build(:instagram_message_attachment_event).with_indifferent_access
+ instagram_webhook.perform_now(attachment_event[:entry])
expect(instagram_inbox.contacts.count).to be 1
expect(instagram_inbox.messages.count).to be 1
@@ -264,9 +253,8 @@ describe Webhooks::InstagramEventsJob do
end
it 'handles unsupported message' do
- instagram_webhook.perform_now(message_events[:unsupported][:entry])
- instagram_inbox.reload
-
+ unsupported_event = build(:instagram_message_unsupported_event).with_indifferent_access
+ instagram_webhook.perform_now(unsupported_event[:entry])
expect(instagram_inbox.contacts.count).to be 1
expect(instagram_inbox.contacts.last.additional_attributes['social_instagram_user_name']).to eq 'some_user_name'
expect(instagram_inbox.conversations.count).to be 1
@@ -275,12 +263,12 @@ describe Webhooks::InstagramEventsJob do
end
it 'does not create contact or messages when Instagram API call fails' do
+ story_mention_echo_event = build(:instagram_story_mention_event_with_echo).with_indifferent_access
+
stub_request(:get, %r{https://graph\.instagram\.com/v22\.0/.*\?.*})
.to_return(status: 401, body: { error: { message: 'Invalid OAuth access token' } }.to_json)
- instagram_webhook.perform_now(message_events[:story_mention_echo][:entry])
-
- instagram_inbox.reload
+ instagram_webhook.perform_now(story_mention_echo_event[:entry])
expect(instagram_inbox.contacts.count).to be 0
expect(instagram_inbox.contact_inboxes.count).to be 0
@@ -288,19 +276,20 @@ describe Webhooks::InstagramEventsJob do
end
it 'handles messaging_seen callback' do
- expect(Instagram::ReadStatusService).to receive(:new).with(params: message_events[:messaging_seen][:entry][0][:messaging][0],
+ messaging_seen_event = build(:messaging_seen_event).with_indifferent_access
+
+ expect(Instagram::ReadStatusService).to receive(:new).with(params: messaging_seen_event[:entry][0][:messaging][0],
channel: instagram_inbox.channel).and_call_original
- instagram_webhook.perform_now(message_events[:messaging_seen][:entry])
+ instagram_webhook.perform_now(messaging_seen_event[:entry])
end
it 'creates contact when Instagram API call returns `No matching Instagram user` (9010 error code)' do
stub_request(:get, %r{https://graph\.instagram\.com/v22\.0/.*\?.*})
.to_return(status: 401, body: { error: { message: 'No matching Instagram user', code: 9010 } }.to_json)
- sender_id = message_events[:dm][:entry][0][:messaging][0][:sender][:id]
- instagram_webhook.perform_now(message_events[:dm][:entry])
-
- instagram_inbox.reload
+ dm_event = build(:instagram_message_create_event).with_indifferent_access
+ sender_id = dm_event[:entry][0][:messaging][0][:sender][:id]
+ instagram_webhook.perform_now(dm_event[:entry])
expect(instagram_inbox.contacts.count).to be 1
expect(instagram_inbox.contacts.last.name).to eq "Unknown (IG: #{sender_id})"
diff --git a/spec/listeners/automation_rule_listener_spec.rb b/spec/listeners/automation_rule_listener_spec.rb
index e1c365f94..57a096a10 100644
--- a/spec/listeners/automation_rule_listener_spec.rb
+++ b/spec/listeners/automation_rule_listener_spec.rb
@@ -130,6 +130,42 @@ describe AutomationRuleListener do
end
end
+ describe 'conversation_resolved' do
+ let!(:automation_rule) { create(:automation_rule, event_name: 'conversation_resolved', account: account) }
+ let(:event) do
+ Events::Base.new('conversation_resolved', Time.zone.now, { conversation: conversation,
+ changed_attributes: { status: %w[Snoozed Open] } })
+ end
+
+ context 'when matching rules are present' do
+ it 'calls AutomationRules::ActionService if conditions match' do
+ allow(condition_match).to receive(:present?).and_return(true)
+ listener.conversation_resolved(event)
+ expect(AutomationRules::ActionService).to have_received(:new).with(automation_rule, account, conversation)
+ end
+
+ it 'does not call AutomationRules::ActionService if conditions do not match' do
+ allow(condition_match).to receive(:present?).and_return(false)
+ listener.conversation_resolved(event)
+ expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation)
+ end
+
+ it 'calls AutomationRules::ActionService for each rule when multiple rules are present' do
+ create(:automation_rule, event_name: 'conversation_resolved', account: account)
+ allow(condition_match).to receive(:present?).and_return(true)
+ listener.conversation_resolved(event)
+ expect(AutomationRules::ActionService).to have_received(:new).twice
+ end
+
+ it 'does not call AutomationRules::ActionService if performed by automation' do
+ event.data[:performed_by] = automation_rule
+ allow(condition_match).to receive(:present?).and_return(true)
+ listener.conversation_resolved(event)
+ expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation)
+ end
+ end
+ end
+
describe 'message_created' do
let!(:automation_rule) { create(:automation_rule, event_name: 'message_created', account: account) }
let!(:message) { create(:message, account: account, conversation: conversation) }
diff --git a/spec/listeners/reporting_event_listener_spec.rb b/spec/listeners/reporting_event_listener_spec.rb
index 0edf79556..8004349bf 100644
--- a/spec/listeners/reporting_event_listener_spec.rb
+++ b/spec/listeners/reporting_event_listener_spec.rb
@@ -267,4 +267,177 @@ describe ReportingEventListener do
end
end
end
+
+ describe '#conversation_opened' do
+ context 'when conversation is opened for the first time' do
+ let(:new_conversation) { create(:conversation, account: account, inbox: inbox, assignee: user) }
+
+ it 'creates conversation_opened event with value 0' do
+ expect(account.reporting_events.where(name: 'conversation_opened').count).to be 0
+ event = Events::Base.new('conversation.opened', Time.zone.now, conversation: new_conversation)
+ listener.conversation_opened(event)
+ expect(account.reporting_events.where(name: 'conversation_opened').count).to be 1
+
+ opened_event = account.reporting_events.where(name: 'conversation_opened').first
+ expect(opened_event.value).to eq 0
+ expect(opened_event.value_in_business_hours).to eq 0
+ expect(opened_event.event_start_time).to be_within(1.second).of(new_conversation.created_at)
+ expect(opened_event.event_end_time).to be_within(1.second).of(new_conversation.updated_at)
+ end
+ end
+
+ context 'when conversation is reopened after being resolved' do
+ let(:resolved_time) { 2.hours.ago }
+ let(:reopened_time) { 1.hour.ago }
+ let(:reopened_conversation) do
+ create(:conversation, account: account, inbox: inbox, assignee: user, updated_at: reopened_time)
+ end
+
+ before do
+ # Create a resolved event first
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account_id: account.id,
+ inbox_id: inbox.id,
+ conversation_id: reopened_conversation.id,
+ user_id: user.id,
+ value: 3600,
+ event_start_time: reopened_conversation.created_at,
+ event_end_time: resolved_time)
+ end
+
+ it 'creates conversation_opened event' do
+ expect(account.reporting_events.where(name: 'conversation_opened').count).to be 0
+ event = Events::Base.new('conversation.opened', reopened_time, conversation: reopened_conversation)
+ listener.conversation_opened(event)
+ expect(account.reporting_events.where(name: 'conversation_opened').count).to be 1
+ end
+
+ it 'calculates correct time since resolution' do
+ event = Events::Base.new('conversation.opened', reopened_time, conversation: reopened_conversation)
+ listener.conversation_opened(event)
+
+ reopened_event = account.reporting_events.where(name: 'conversation_opened').first
+ expect(reopened_event.value).to be_within(1).of(3600) # 1 hour = 3600 seconds
+ expect(reopened_event.event_start_time).to be_within(1.second).of(resolved_time)
+ expect(reopened_event.event_end_time).to be_within(1.second).of(reopened_time)
+ end
+
+ it 'sets correct attributes for conversation_opened event' do
+ event = Events::Base.new('conversation.opened', reopened_time, conversation: reopened_conversation)
+ listener.conversation_opened(event)
+
+ reopened_event = account.reporting_events.where(name: 'conversation_opened').first
+ expect(reopened_event.account_id).to eq(account.id)
+ expect(reopened_event.inbox_id).to eq(inbox.id)
+ expect(reopened_event.conversation_id).to eq(reopened_conversation.id)
+ expect(reopened_event.user_id).to eq(user.id)
+ end
+
+ context 'when business hours enabled for inbox' do
+ let(:resolved_time) { Time.zone.parse('March 20, 2022 12:00') }
+ let(:reopened_time) { Time.zone.parse('March 21, 2022 14:00') }
+ let!(:business_hours_inbox) { create(:inbox, working_hours_enabled: true, account: account) }
+ let!(:business_hours_conversation) do
+ create(:conversation, account: account, inbox: business_hours_inbox, assignee: user, updated_at: reopened_time)
+ end
+
+ before do
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account_id: account.id,
+ inbox_id: business_hours_inbox.id,
+ conversation_id: business_hours_conversation.id,
+ user_id: user.id,
+ value: 3600,
+ event_start_time: business_hours_conversation.created_at,
+ event_end_time: resolved_time)
+ end
+
+ it 'creates conversation_opened event with business hour value' do
+ event = Events::Base.new('conversation.opened', reopened_time, conversation: business_hours_conversation)
+ listener.conversation_opened(event)
+
+ reopened_event = account.reporting_events.where(name: 'conversation_opened').first
+ expect(reopened_event.value_in_business_hours).to be 18_000.0 # 5 business hours (26 hours total - 21 non-business hours)
+ end
+ end
+ end
+
+ context 'when conversation has multiple resolutions' do
+ let(:first_resolved_time) { 3.hours.ago }
+ let(:second_resolved_time) { 1.hour.ago }
+ let(:reopened_time) { 30.minutes.ago }
+ let(:multiple_resolution_conversation) do
+ create(:conversation, account: account, inbox: inbox, assignee: user, updated_at: reopened_time)
+ end
+
+ before do
+ # Create first resolved event
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account_id: account.id,
+ inbox_id: inbox.id,
+ conversation_id: multiple_resolution_conversation.id,
+ user_id: user.id,
+ value: 3600,
+ event_start_time: multiple_resolution_conversation.created_at,
+ event_end_time: first_resolved_time)
+
+ # Create second resolved event (more recent)
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account_id: account.id,
+ inbox_id: inbox.id,
+ conversation_id: multiple_resolution_conversation.id,
+ user_id: user.id,
+ value: 1800,
+ event_start_time: first_resolved_time,
+ event_end_time: second_resolved_time)
+ end
+
+ it 'uses the most recent resolved event for calculation' do
+ event = Events::Base.new('conversation.opened', reopened_time, conversation: multiple_resolution_conversation)
+ listener.conversation_opened(event)
+
+ reopened_event = account.reporting_events.where(name: 'conversation_opened').first
+ expect(reopened_event.value).to be_within(1).of(1800) # 30 minutes from second resolution
+ expect(reopened_event.event_start_time).to be_within(1.second).of(second_resolved_time)
+ end
+ end
+
+ context 'when agent bot resolves and conversation is reopened' do
+ # This implicitly tests that the first_response time is correctly calculated
+ # By checking that a conversation reopened event is created with the correct values
+ let(:agent_bot) { create(:agent_bot, account: account) }
+ let(:agent_bot_inbox) { create(:inbox, account: account) }
+ let(:bot_resolved_time) { 2.hours.ago }
+ let(:reopened_time) { 1.hour.ago }
+ let(:bot_conversation) do
+ create(:conversation, account: account, inbox: agent_bot_inbox, assignee: user, updated_at: reopened_time)
+ end
+
+ before do
+ create(:agent_bot_inbox, agent_bot: agent_bot, inbox: agent_bot_inbox)
+
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account_id: account.id,
+ inbox_id: agent_bot_inbox.id,
+ conversation_id: bot_conversation.id,
+ user_id: user.id,
+ event_end_time: bot_resolved_time)
+ end
+
+ it 'creates conversation_opened event for agent bot reopening' do
+ event = Events::Base.new('conversation.opened', reopened_time, conversation: bot_conversation)
+ listener.conversation_opened(event)
+
+ reopened_event = account.reporting_events.where(name: 'conversation_opened').first
+ expect(reopened_event.value).to be_within(1).of(3600) # 1 hour since resolution
+ expect(reopened_event.event_start_time).to be_within(1.second).of(bot_resolved_time)
+ expect(reopened_event.event_end_time).to be_within(1.second).of(reopened_time)
+ end
+ end
+ end
end
diff --git a/spec/models/assignment_policy_spec.rb b/spec/models/assignment_policy_spec.rb
new file mode 100644
index 000000000..1a97bbda0
--- /dev/null
+++ b/spec/models/assignment_policy_spec.rb
@@ -0,0 +1,56 @@
+require 'rails_helper'
+
+RSpec.describe AssignmentPolicy do
+ describe 'associations' do
+ it { is_expected.to belong_to(:account) }
+ it { is_expected.to have_many(:inbox_assignment_policies).dependent(:destroy) }
+ it { is_expected.to have_many(:inboxes).through(:inbox_assignment_policies) }
+ end
+
+ describe 'validations' do
+ subject { build(:assignment_policy) }
+
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_uniqueness_of(:name).scoped_to(:account_id) }
+ end
+
+ describe 'fair distribution validations' do
+ it 'requires fair_distribution_limit to be greater than 0' do
+ policy = build(:assignment_policy, fair_distribution_limit: 0)
+ expect(policy).not_to be_valid
+ expect(policy.errors[:fair_distribution_limit]).to include('must be greater than 0')
+ end
+
+ it 'requires fair_distribution_window to be greater than 0' do
+ policy = build(:assignment_policy, fair_distribution_window: -1)
+ expect(policy).not_to be_valid
+ expect(policy.errors[:fair_distribution_window]).to include('must be greater than 0')
+ end
+ end
+
+ describe 'enum values' do
+ let(:assignment_policy) { create(:assignment_policy) }
+
+ describe 'conversation_priority' do
+ it 'can be set to earliest_created' do
+ assignment_policy.update!(conversation_priority: :earliest_created)
+ expect(assignment_policy.conversation_priority).to eq('earliest_created')
+ expect(assignment_policy.earliest_created?).to be true
+ end
+
+ it 'can be set to longest_waiting' do
+ assignment_policy.update!(conversation_priority: :longest_waiting)
+ expect(assignment_policy.conversation_priority).to eq('longest_waiting')
+ expect(assignment_policy.longest_waiting?).to be true
+ end
+ end
+
+ describe 'assignment_order' do
+ it 'can be set to round_robin' do
+ assignment_policy.update!(assignment_order: :round_robin)
+ expect(assignment_policy.assignment_order).to eq('round_robin')
+ expect(assignment_policy.round_robin?).to be true
+ end
+ end
+ end
+end
diff --git a/spec/models/attachment_spec.rb b/spec/models/attachment_spec.rb
index 0b03a56ad..cc00eab5d 100644
--- a/spec/models/attachment_spec.rb
+++ b/spec/models/attachment_spec.rb
@@ -82,6 +82,16 @@ RSpec.describe Attachment do
expect(attachment.thumb_url).to be_present
end
+
+ it 'handles unrepresentable images gracefully' do
+ attachment = message.attachments.create!(account_id: message.account_id, file_type: :image)
+ attachment.file.attach(io: StringIO.new('fake image'), filename: 'test.jpg', content_type: 'image/jpeg')
+
+ allow(attachment.file).to receive(:representation).and_raise(ActiveStorage::UnrepresentableError.new('Cannot represent'))
+
+ expect(Rails.logger).to receive(:warn).with(/Unrepresentable image attachment: #{attachment.id}/)
+ expect(attachment.thumb_url).to eq('')
+ end
end
describe 'meta data handling' do
diff --git a/spec/services/whatsapp/channel_creation_service_spec.rb b/spec/services/whatsapp/channel_creation_service_spec.rb
index 1c1f46232..403fb71b8 100644
--- a/spec/services/whatsapp/channel_creation_service_spec.rb
+++ b/spec/services/whatsapp/channel_creation_service_spec.rb
@@ -16,6 +16,11 @@ describe Whatsapp::ChannelCreationService do
describe '#perform' do
before do
+ # Stub the webhook teardown service to prevent HTTP calls during cleanup
+ teardown_service = instance_double(Whatsapp::WebhookTeardownService)
+ allow(Whatsapp::WebhookTeardownService).to receive(:new).and_return(teardown_service)
+ allow(teardown_service).to receive(:perform)
+
# Clean up any existing channels to avoid phone number conflicts
Channel::Whatsapp.destroy_all
diff --git a/spec/services/whatsapp/embedded_signup_service_spec.rb b/spec/services/whatsapp/embedded_signup_service_spec.rb
index 12a4d32df..1db94928e 100644
--- a/spec/services/whatsapp/embedded_signup_service_spec.rb
+++ b/spec/services/whatsapp/embedded_signup_service_spec.rb
@@ -10,121 +10,100 @@ describe Whatsapp::EmbeddedSignupService do
phone_number_id: 'test_phone_number_id'
}
end
- let(:service) do
- described_class.new(
- account: account,
- params: params
- )
+ let(:service) { described_class.new(account: account, params: params) }
+ let(:access_token) { 'test_access_token' }
+ let(:phone_info) do
+ {
+ phone_number_id: params[:phone_number_id],
+ phone_number: '+1234567890',
+ verified: true,
+ business_name: 'Test Business'
+ }
end
+ let(:channel) { instance_double(Channel::Whatsapp) }
describe '#perform' do
- let(:access_token) { 'test_access_token' }
- let(:phone_info) do
- {
- phone_number_id: params[:phone_number_id],
- phone_number: '+1234567890',
- verified: true,
- business_name: 'Test Business'
- }
- end
- let(:channel) { instance_double(Channel::Whatsapp) }
- let(:service_doubles) do
- {
- token_exchange: instance_double(Whatsapp::TokenExchangeService),
- phone_info: instance_double(Whatsapp::PhoneInfoService),
- token_validation: instance_double(Whatsapp::TokenValidationService),
- channel_creation: instance_double(Whatsapp::ChannelCreationService)
- }
- end
-
before do
allow(GlobalConfig).to receive(:clear_cache)
- allow(Whatsapp::TokenExchangeService).to receive(:new).with(params[:code]).and_return(service_doubles[:token_exchange])
- allow(service_doubles[:token_exchange]).to receive(:perform).and_return(access_token)
+ # Mock service dependencies
+ token_exchange = instance_double(Whatsapp::TokenExchangeService)
+ allow(Whatsapp::TokenExchangeService).to receive(:new).with(params[:code]).and_return(token_exchange)
+ allow(token_exchange).to receive(:perform).and_return(access_token)
+ phone_service = instance_double(Whatsapp::PhoneInfoService)
allow(Whatsapp::PhoneInfoService).to receive(:new)
- .with(params[:waba_id], params[:phone_number_id], access_token).and_return(service_doubles[:phone_info])
- allow(service_doubles[:phone_info]).to receive(:perform).and_return(phone_info)
+ .with(params[:waba_id], params[:phone_number_id], access_token).and_return(phone_service)
+ allow(phone_service).to receive(:perform).and_return(phone_info)
+ validation_service = instance_double(Whatsapp::TokenValidationService)
allow(Whatsapp::TokenValidationService).to receive(:new)
- .with(access_token, params[:waba_id]).and_return(service_doubles[:token_validation])
- allow(service_doubles[:token_validation]).to receive(:perform)
+ .with(access_token, params[:waba_id]).and_return(validation_service)
+ allow(validation_service).to receive(:perform)
+ channel_creation = instance_double(Whatsapp::ChannelCreationService)
allow(Whatsapp::ChannelCreationService).to receive(:new)
.with(account, { waba_id: params[:waba_id], business_name: 'Test Business' }, phone_info, access_token)
- .and_return(service_doubles[:channel_creation])
- allow(service_doubles[:channel_creation]).to receive(:perform).and_return(channel)
+ .and_return(channel_creation)
+ allow(channel_creation).to receive(:perform).and_return(channel)
- # Webhook setup is now handled in the channel after_create callback
- # So we stub it at the model level
- webhook_service = instance_double(Whatsapp::WebhookSetupService)
- allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service)
- allow(webhook_service).to receive(:perform)
+ allow(channel).to receive(:setup_webhooks)
end
- it 'orchestrates all services in the correct order' do
- expect(service_doubles[:token_exchange]).to receive(:perform).ordered
- expect(service_doubles[:phone_info]).to receive(:perform).ordered
- expect(service_doubles[:token_validation]).to receive(:perform).ordered
- expect(service_doubles[:channel_creation]).to receive(:perform).ordered
+ it 'creates channel and sets up webhooks' do
+ expect(channel).to receive(:setup_webhooks)
result = service.perform
expect(result).to eq(channel)
end
- context 'when required parameters are missing' do
- it 'raises error when code is blank' do
- service = described_class.new(
- account: account,
- params: params.merge(code: '')
- )
- expect { service.perform }.to raise_error(ArgumentError, /Required parameters are missing: code/)
- end
-
- it 'raises error when business_id is blank' do
- service = described_class.new(
- account: account,
- params: params.merge(business_id: '')
- )
- expect { service.perform }.to raise_error(ArgumentError, /Required parameters are missing: business_id/)
- end
-
- it 'raises error when waba_id is blank' do
- service = described_class.new(
- account: account,
- params: params.merge(waba_id: '')
- )
- expect { service.perform }.to raise_error(ArgumentError, /Required parameters are missing: waba_id/)
- end
-
- it 'raises error when multiple parameters are blank' do
- service = described_class.new(
- account: account,
- params: params.merge(code: '', business_id: '')
- )
- expect { service.perform }.to raise_error(ArgumentError, /Required parameters are missing: code, business_id/)
+ context 'when parameters are invalid' do
+ it 'raises ArgumentError for missing parameters' do
+ invalid_service = described_class.new(account: account, params: { code: '', business_id: '', waba_id: '' })
+ expect { invalid_service.perform }.to raise_error(ArgumentError, /Required parameters are missing/)
end
end
- context 'when any service fails' do
- it 'logs and re-raises the error' do
- allow(service_doubles[:token_exchange]).to receive(:perform).and_raise('Token error')
+ context 'when service fails' do
+ it 'logs and re-raises errors' do
+ token_exchange = instance_double(Whatsapp::TokenExchangeService)
+ allow(Whatsapp::TokenExchangeService).to receive(:new).and_return(token_exchange)
+ allow(token_exchange).to receive(:perform).and_raise('Token error')
expect(Rails.logger).to receive(:error).with('[WHATSAPP] Embedded signup failed: Token error')
expect { service.perform }.to raise_error('Token error')
end
+
+ it 'prompts reauthorization when webhook setup fails' do
+ # Create a real channel to test the actual webhook failure behavior
+ real_channel = create(:channel_whatsapp, account: account, phone_number: '+1234567890',
+ validate_provider_config: false, sync_templates: false)
+
+ # Mock the channel creation to return our real channel
+ channel_creation = instance_double(Whatsapp::ChannelCreationService)
+ allow(Whatsapp::ChannelCreationService).to receive(:new).and_return(channel_creation)
+ allow(channel_creation).to receive(:perform).and_return(real_channel)
+
+ # Mock webhook setup to fail
+ allow(real_channel).to receive(:perform_webhook_setup).and_raise('Webhook setup error')
+
+ # Verify channel is not marked for reauthorization initially
+ expect(real_channel.reauthorization_required?).to be false
+
+ # The service completes successfully even if webhook fails (webhook error is rescued in setup_webhooks)
+ result = service.perform
+ expect(result).to eq(real_channel)
+
+ # Verify the channel is now marked for reauthorization
+ expect(real_channel.reauthorization_required?).to be true
+ end
end
- context 'when inbox_id is provided (reauthorization flow)' do
+ context 'with reauthorization flow' do
let(:inbox_id) { 123 }
let(:reauth_service) { instance_double(Whatsapp::ReauthorizationService) }
let(:service_with_inbox) do
- described_class.new(
- account: account,
- params: params,
- inbox_id: inbox_id
- )
+ described_class.new(account: account, params: params, inbox_id: inbox_id)
end
before do
@@ -137,16 +116,45 @@ describe Whatsapp::EmbeddedSignupService do
allow(reauth_service).to receive(:perform).with(access_token, phone_info).and_return(channel)
end
- it 'uses ReauthorizationService instead of ChannelCreationService' do
- expect(service_doubles[:token_exchange]).to receive(:perform).ordered
- expect(service_doubles[:phone_info]).to receive(:perform).ordered
- expect(service_doubles[:token_validation]).to receive(:perform).ordered
- expect(reauth_service).to receive(:perform).with(access_token, phone_info).ordered
- expect(service_doubles[:channel_creation]).not_to receive(:perform)
+ it 'uses ReauthorizationService and sets up webhooks' do
+ expect(reauth_service).to receive(:perform)
+ expect(channel).to receive(:setup_webhooks)
result = service_with_inbox.perform
expect(result).to eq(channel)
end
+
+ it 'clears reauthorization flag' do
+ inbox = create(:inbox, account: account)
+ whatsapp_channel = create(:channel_whatsapp, account: account, phone_number: '+1234567890',
+ validate_provider_config: false, sync_templates: false)
+ inbox.update!(channel: whatsapp_channel)
+ whatsapp_channel.prompt_reauthorization!
+
+ service_with_real_inbox = described_class.new(account: account, params: params, inbox_id: inbox.id)
+
+ # Mock the ReauthorizationService to return our test channel
+ reauth_service = instance_double(Whatsapp::ReauthorizationService)
+ allow(Whatsapp::ReauthorizationService).to receive(:new).with(
+ account: account,
+ inbox_id: inbox.id,
+ phone_number_id: params[:phone_number_id],
+ business_id: params[:business_id]
+ ).and_return(reauth_service)
+
+ # Perform the reauthorization and clear the flag
+ allow(reauth_service).to receive(:perform) do
+ whatsapp_channel.reauthorized!
+ whatsapp_channel
+ end
+
+ allow(whatsapp_channel).to receive(:setup_webhooks).and_return(true)
+
+ expect(whatsapp_channel.reauthorization_required?).to be true
+ result = service_with_real_inbox.perform
+ expect(result).to eq(whatsapp_channel)
+ expect(whatsapp_channel.reauthorization_required?).to be false
+ end
end
end
end
diff --git a/spec/services/whatsapp/incoming_message_service_spec.rb b/spec/services/whatsapp/incoming_message_service_spec.rb
index 4035a47df..ede1ba824 100644
--- a/spec/services/whatsapp/incoming_message_service_spec.rb
+++ b/spec/services/whatsapp/incoming_message_service_spec.rb
@@ -371,5 +371,100 @@ describe Whatsapp::IncomingMessageService do
Redis::Alfred.delete(key)
end
end
+
+ context 'when profile name is available for contact updates' do
+ let(:wa_id) { '1234567890' }
+ let(:phone_number) { "+#{wa_id}" }
+
+ it 'updates existing contact name when current name matches phone number' do
+ # Create contact with phone number as name
+ existing_contact = create(:contact,
+ account: whatsapp_channel.inbox.account,
+ name: phone_number,
+ phone_number: phone_number)
+ create(:contact_inbox,
+ contact: existing_contact,
+ inbox: whatsapp_channel.inbox,
+ source_id: wa_id)
+
+ params = {
+ 'contacts' => [{ 'profile' => { 'name' => 'Jane Smith' }, 'wa_id' => wa_id }],
+ 'messages' => [{ 'from' => wa_id, 'id' => 'message123', 'text' => { 'body' => 'Hello' },
+ 'timestamp' => '1633034394', 'type' => 'text' }]
+ }.with_indifferent_access
+
+ described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
+ existing_contact.reload
+ expect(existing_contact.name).to eq('Jane Smith')
+ end
+
+ it 'does not update contact name when current name is different from phone number' do
+ # Create contact with human name
+ existing_contact = create(:contact,
+ account: whatsapp_channel.inbox.account,
+ name: 'John Doe',
+ phone_number: phone_number)
+ create(:contact_inbox,
+ contact: existing_contact,
+ inbox: whatsapp_channel.inbox,
+ source_id: wa_id)
+
+ params = {
+ 'contacts' => [{ 'profile' => { 'name' => 'Jane Smith' }, 'wa_id' => wa_id }],
+ 'messages' => [{ 'from' => wa_id, 'id' => 'message123', 'text' => { 'body' => 'Hello' },
+ 'timestamp' => '1633034394', 'type' => 'text' }]
+ }.with_indifferent_access
+
+ described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
+ existing_contact.reload
+ expect(existing_contact.name).to eq('John Doe') # Should not change
+ end
+
+ it 'updates contact name when current name matches formatted phone number' do
+ formatted_number = TelephoneNumber.parse(phone_number).international_number
+
+ # Create contact with formatted phone number as name
+ existing_contact = create(:contact,
+ account: whatsapp_channel.inbox.account,
+ name: formatted_number,
+ phone_number: phone_number)
+ create(:contact_inbox,
+ contact: existing_contact,
+ inbox: whatsapp_channel.inbox,
+ source_id: wa_id)
+
+ params = {
+ 'contacts' => [{ 'profile' => { 'name' => 'Alice Johnson' }, 'wa_id' => wa_id }],
+ 'messages' => [{ 'from' => wa_id, 'id' => 'message123', 'text' => { 'body' => 'Hello' },
+ 'timestamp' => '1633034394', 'type' => 'text' }]
+ }.with_indifferent_access
+
+ described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
+ existing_contact.reload
+ expect(existing_contact.name).to eq('Alice Johnson')
+ end
+
+ it 'does not update when profile name is blank' do
+ # Create contact with phone number as name
+ existing_contact = create(:contact,
+ account: whatsapp_channel.inbox.account,
+ name: phone_number,
+ phone_number: phone_number)
+ create(:contact_inbox,
+ contact: existing_contact,
+ inbox: whatsapp_channel.inbox,
+ source_id: wa_id)
+
+ params = {
+ 'contacts' => [{ 'profile' => { 'name' => '' }, 'wa_id' => wa_id }],
+ 'messages' => [{ 'from' => wa_id, 'id' => 'message123', 'text' => { 'body' => 'Hello' },
+ 'timestamp' => '1633034394', 'type' => 'text' }]
+ }.with_indifferent_access
+
+ described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
+ existing_contact.reload
+ expect(existing_contact.name).to eq(phone_number) # Should not change
+ end
+ end
end
end
diff --git a/spec/services/whatsapp/oneoff_campaign_service_spec.rb b/spec/services/whatsapp/oneoff_campaign_service_spec.rb
index 599081e23..dd8d51c54 100644
--- a/spec/services/whatsapp/oneoff_campaign_service_spec.rb
+++ b/spec/services/whatsapp/oneoff_campaign_service_spec.rb
@@ -133,7 +133,8 @@ describe Whatsapp::OneoffCampaignService do
)
)
)
- )
+ ),
+ nil
)
described_class.new(campaign: campaign).perform
@@ -164,8 +165,8 @@ describe Whatsapp::OneoffCampaignService do
allow(whatsapp_channel).to receive(:send_template).and_return(nil)
- expect(whatsapp_channel).to receive(:send_template).with(contact_error.phone_number, anything).and_raise(StandardError, error_message)
- expect(whatsapp_channel).to receive(:send_template).with(contact_success.phone_number, anything).once
+ expect(whatsapp_channel).to receive(:send_template).with(contact_error.phone_number, anything, nil).and_raise(StandardError, error_message)
+ expect(whatsapp_channel).to receive(:send_template).with(contact_success.phone_number, anything, nil).once
expect(Rails.logger).to receive(:error)
.with("Failed to send WhatsApp template message to #{contact_error.phone_number}: #{error_message}")
diff --git a/spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb b/spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb
index 8735ccfbb..69ba69379 100644
--- a/spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb
+++ b/spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb
@@ -187,7 +187,7 @@ describe Whatsapp::Providers::WhatsappCloudService do
)
.to_return(status: 200, body: whatsapp_response.to_json, headers: response_headers)
- expect(service.send_template('+123456789', template_info)).to eq('message_id')
+ expect(service.send_template('+123456789', template_info, message)).to eq('message_id')
end
end
end
@@ -287,7 +287,7 @@ describe Whatsapp::Providers::WhatsappCloudService do
context 'when there is a message' do
it 'logs error and updates message status' do
service.instance_variable_set(:@message, message)
- service.send(:handle_error, error_response_object)
+ service.send(:handle_error, error_response_object, message)
expect(message.reload.status).to eq('failed')
expect(message.reload.external_error).to eq(error_message)
@@ -305,7 +305,7 @@ describe Whatsapp::Providers::WhatsappCloudService do
it 'logs error but does not update message' do
service.instance_variable_set(:@message, message)
- service.send(:handle_error, error_response_object)
+ service.send(:handle_error, error_response_object, message)
expect(message.reload.status).not_to eq('failed')
expect(message.reload.external_error).to be_nil
diff --git a/spec/services/whatsapp/template_parameter_converter_service_spec.rb b/spec/services/whatsapp/template_parameter_converter_service_spec.rb
index 2994bb472..570c5c6cc 100644
--- a/spec/services/whatsapp/template_parameter_converter_service_spec.rb
+++ b/spec/services/whatsapp/template_parameter_converter_service_spec.rb
@@ -133,6 +133,48 @@ describe Whatsapp::TemplateParameterConverterService do
end
end
+ context 'when processed_params is nil (parameter-less templates)' do
+ let(:nil_params) do
+ {
+ 'processed_params' => nil
+ }
+ end
+
+ let(:parameterless_template) do
+ {
+ 'name' => 'test_no_params_template',
+ 'language' => 'en',
+ 'parameter_format' => 'POSITIONAL',
+ 'id' => '9876543210987654',
+ 'status' => 'APPROVED',
+ 'category' => 'UTILITY',
+ 'previous_category' => 'MARKETING',
+ 'sub_category' => 'CUSTOM',
+ 'components' => [
+ {
+ 'type' => 'BODY',
+ 'text' => 'Thank you for contacting us! Your request has been processed successfully. Have a great day! 🙂'
+ }
+ ]
+ }
+ end
+
+ it 'converts nil to empty enhanced format' do
+ converter = described_class.new(nil_params, parameterless_template)
+ result = converter.normalize_to_enhanced
+
+ expect(result['processed_params']).to eq({})
+ expect(result['format_version']).to eq('legacy')
+ end
+
+ it 'does not raise ArgumentError for nil processed_params' do
+ expect do
+ converter = described_class.new(nil_params, parameterless_template)
+ converter.normalize_to_enhanced
+ end.not_to raise_error
+ end
+ end
+
context 'when invalid format' do
let(:invalid_params) do
{
@@ -174,6 +216,26 @@ describe Whatsapp::TemplateParameterConverterService do
end
describe 'simplified conversion methods' do
+ describe '#convert_legacy_to_enhanced' do
+ it 'handles nil processed_params without raising error' do
+ converter = described_class.new({}, template)
+ result = converter.send(:convert_legacy_to_enhanced, nil, template)
+ expect(result).to eq({})
+ end
+
+ it 'returns empty hash for parameter-less templates' do
+ parameterless_template = {
+ 'name' => 'no_params_template',
+ 'language' => 'en',
+ 'components' => [{ 'type' => 'BODY', 'text' => 'Hello World!' }]
+ }
+
+ converter = described_class.new({}, parameterless_template)
+ result = converter.send(:convert_legacy_to_enhanced, nil, parameterless_template)
+ expect(result).to eq({})
+ end
+ end
+
describe '#convert_array_to_body_params' do
it 'converts empty array' do
converter = described_class.new({}, template)
diff --git a/spec/services/whatsapp/webhook_setup_service_spec.rb b/spec/services/whatsapp/webhook_setup_service_spec.rb
index 7cee115c2..e6a246e5d 100644
--- a/spec/services/whatsapp/webhook_setup_service_spec.rb
+++ b/spec/services/whatsapp/webhook_setup_service_spec.rb
@@ -5,7 +5,7 @@ describe Whatsapp::WebhookSetupService do
create(:channel_whatsapp,
phone_number: '+1234567890',
provider_config: {
- 'phone_number_id' => 'test_phone_id',
+ 'phone_number_id' => '123456789',
'webhook_verify_token' => 'test_verify_token'
},
provider: 'whatsapp_cloud',
@@ -18,9 +18,14 @@ describe Whatsapp::WebhookSetupService do
let(:api_client) { instance_double(Whatsapp::FacebookApiClient) }
before do
+ # Stub webhook teardown to prevent HTTP calls during cleanup
+ stub_request(:delete, /graph.facebook.com/).to_return(status: 200, body: '{}', headers: {})
+
# Clean up any existing channels to avoid phone number conflicts
Channel::Whatsapp.destroy_all
allow(Whatsapp::FacebookApiClient).to receive(:new).and_return(api_client)
+ # Default stub for phone_number_verified? with any argument
+ allow(api_client).to receive(:phone_number_verified?).and_return(false)
end
describe '#perform' do
@@ -148,5 +153,87 @@ describe Whatsapp::WebhookSetupService do
end
end
end
+
+ context 'when webhook setup fails and should trigger reauthorization' do
+ before do
+ allow(api_client).to receive(:phone_number_verified?).with('123456789').and_return(true)
+ allow(api_client).to receive(:subscribe_waba_webhook).and_raise('Invalid access token')
+ end
+
+ it 'raises error with webhook setup failure message' do
+ with_modified_env FRONTEND_URL: 'https://app.chatwoot.com' do
+ expect { service.perform }.to raise_error(/Webhook setup failed: Invalid access token/)
+ end
+ end
+
+ it 'logs the webhook setup failure' do
+ with_modified_env FRONTEND_URL: 'https://app.chatwoot.com' do
+ expect(Rails.logger).to receive(:error).with('[WHATSAPP] Webhook setup failed: Invalid access token')
+ expect { service.perform }.to raise_error(/Webhook setup failed/)
+ end
+ end
+ end
+
+ context 'when used during reauthorization flow' do
+ let(:existing_channel) do
+ create(:channel_whatsapp,
+ phone_number: '+1234567890',
+ provider_config: {
+ 'phone_number_id' => '123456789',
+ 'webhook_verify_token' => 'existing_verify_token',
+ 'business_id' => 'existing_business_id',
+ 'waba_id' => 'existing_waba_id'
+ },
+ provider: 'whatsapp_cloud',
+ sync_templates: false,
+ validate_provider_config: false)
+ end
+ let(:new_access_token) { 'new_access_token' }
+ let(:service_reauth) { described_class.new(existing_channel, waba_id, new_access_token) }
+
+ before do
+ allow(api_client).to receive(:phone_number_verified?).with('123456789').and_return(true)
+ allow(api_client).to receive(:subscribe_waba_webhook)
+ .with(waba_id, anything, 'existing_verify_token').and_return({ 'success' => true })
+ end
+
+ it 'successfully reauthorizes with new access token' do
+ with_modified_env FRONTEND_URL: 'https://app.chatwoot.com' do
+ expect(api_client).not_to receive(:register_phone_number)
+ expect(api_client).to receive(:subscribe_waba_webhook)
+ .with(waba_id, 'https://app.chatwoot.com/webhooks/whatsapp/+1234567890', 'existing_verify_token')
+ service_reauth.perform
+ end
+ end
+
+ it 'uses the existing webhook verify token during reauthorization' do
+ with_modified_env FRONTEND_URL: 'https://app.chatwoot.com' do
+ expect(api_client).to receive(:subscribe_waba_webhook)
+ .with(waba_id, anything, 'existing_verify_token')
+ service_reauth.perform
+ end
+ end
+ end
+
+ context 'when webhook setup is successful in creation flow' do
+ before do
+ allow(api_client).to receive(:phone_number_verified?).with('123456789').and_return(true)
+ allow(api_client).to receive(:subscribe_waba_webhook)
+ .with(waba_id, anything, 'test_verify_token').and_return({ 'success' => true })
+ end
+
+ it 'completes successfully without errors' do
+ with_modified_env FRONTEND_URL: 'https://app.chatwoot.com' do
+ expect { service.perform }.not_to raise_error
+ end
+ end
+
+ it 'does not log any errors' do
+ with_modified_env FRONTEND_URL: 'https://app.chatwoot.com' do
+ expect(Rails.logger).not_to receive(:error)
+ service.perform
+ end
+ end
+ end
end
end
diff --git a/swagger/definitions/request/automation_rule/create_update_payload.yml b/swagger/definitions/request/automation_rule/create_update_payload.yml
index 091fa2aaa..75aedd41b 100644
--- a/swagger/definitions/request/automation_rule/create_update_payload.yml
+++ b/swagger/definitions/request/automation_rule/create_update_payload.yml
@@ -13,6 +13,7 @@ properties:
enum:
- conversation_created
- conversation_updated
+ - conversation_resolved
- message_created
example: message_created
description: The event when you want to execute the automation actions
diff --git a/swagger/definitions/request/conversation/create_message_payload.yml b/swagger/definitions/request/conversation/create_message_payload.yml
index 4b1851293..71d073d75 100644
--- a/swagger/definitions/request/conversation/create_message_payload.yml
+++ b/swagger/definitions/request/conversation/create_message_payload.yml
@@ -30,22 +30,64 @@ properties:
example: 1
template_params:
type: object
- description: The template params for the message in case of whatsapp Channel
+ description: WhatsApp template parameters for sending structured messages
+ required:
+ - name
+ - category
+ - language
+ - processed_params
properties:
name:
type: string
- description: Name of the template
- example: 'sample_issue_resolution'
+ description: Name of the WhatsApp template (must be approved in WhatsApp Business Manager)
+ example: 'purchase_receipt'
category:
type: string
+ enum: ['UTILITY', 'MARKETING', 'SHIPPING_UPDATE', 'TICKET_UPDATE', 'ISSUE_RESOLUTION']
description: Category of the template
- example: UTILITY
+ example: 'UTILITY'
language:
type: string
- description: Language of the template
- example: en_US
+ description: Language code of the template (BCP 47 format)
+ example: 'en_US'
processed_params:
type: object
- description: The processed param values for template variables in template
- example:
- 1: 'Chatwoot'
\ No newline at end of file
+ description: Processed template parameters organized by component type
+ properties:
+ body:
+ type: object
+ description: Body component parameters with variable placeholders
+ additionalProperties:
+ type: string
+ example:
+ '1': 'Visa'
+ '2': 'Nike'
+ '3': 'Bill'
+ header:
+ type: object
+ description: Header component parameters for media templates
+ properties:
+ media_url:
+ type: string
+ format: uri
+ description: Publicly accessible URL for IMAGE, VIDEO, or DOCUMENT headers
+ example: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf'
+ media_type:
+ type: string
+ enum: ['image', 'video', 'document']
+ description: Type of media for the header
+ example: 'document'
+ buttons:
+ type: array
+ description: Button component parameters for interactive templates
+ items:
+ type: object
+ properties:
+ type:
+ type: string
+ enum: ['url', 'copy_code']
+ description: Type of button parameter
+ parameter:
+ type: string
+ description: Dynamic parameter value for the button
+ example: 'SSFSDFSD'
\ No newline at end of file
diff --git a/swagger/definitions/resource/automation_rule.yml b/swagger/definitions/resource/automation_rule.yml
index b561441ff..ad96c58ff 100644
--- a/swagger/definitions/resource/automation_rule.yml
+++ b/swagger/definitions/resource/automation_rule.yml
@@ -10,4 +10,4 @@ properties:
- type: object
description: Single automation rule (for show/create/update endpoints)
allOf:
- - $ref: '#/components/schemas/automation_rule_item'
\ No newline at end of file
+ - $ref: '#/components/schemas/automation_rule_item'
diff --git a/swagger/paths/application/conversation/messages/create.yml b/swagger/paths/application/conversation/messages/create.yml
index f8cd35f3c..1b8272585 100644
--- a/swagger/paths/application/conversation/messages/create.yml
+++ b/swagger/paths/application/conversation/messages/create.yml
@@ -2,7 +2,57 @@ tags:
- Messages
operationId: create-a-new-message-in-a-conversation
summary: Create New Message
-description: Create a new message in the conversation
+description: |
+ Create a new message in the conversation.
+
+ ## WhatsApp Template Messages
+
+ For WhatsApp channels, you can send structured template messages using the `template_params` field.
+ Templates must be pre-approved in WhatsApp Business Manager.
+
+ ### Example Templates
+
+ **Text with Image Header:**
+ ```json
+ {
+ "content": "Hi your order 121212 is confirmed. Please wait for further updates",
+ "template_params": {
+ "name": "order_confirmation",
+ "category": "MARKETING",
+ "language": "en",
+ "processed_params": {
+ "body": {
+ "1": "121212"
+ },
+ "header": {
+ "media_url": "https://picsum.photos/200/300",
+ "media_type": "image"
+ }
+ }
+ }
+ }
+ ```
+
+ **Text with Copy Code Button:**
+ ```json
+ {
+ "content": "Special offer! Get 30% off your next purchase. Use the code below",
+ "template_params": {
+ "name": "discount_coupon",
+ "category": "MARKETING",
+ "language": "en",
+ "processed_params": {
+ "body": {
+ "discount_percentage": "30"
+ },
+ "buttons": [{
+ "type": "copy_code",
+ "parameter": "SAVE20"
+ }]
+ }
+ }
+ }
+ ```
security:
- userApiKey: []
- agentBotApiKey: []
diff --git a/swagger/swagger.json b/swagger/swagger.json
index e849f8119..aa7455e7f 100644
--- a/swagger/swagger.json
+++ b/swagger/swagger.json
@@ -5937,7 +5937,7 @@
],
"operationId": "create-a-new-message-in-a-conversation",
"summary": "Create New Message",
- "description": "Create a new message in the conversation",
+ "description": "Create a new message in the conversation.\n\n## WhatsApp Template Messages\n\nFor WhatsApp channels, you can send structured template messages using the `template_params` field. \nTemplates must be pre-approved in WhatsApp Business Manager.\n\n### Example Templates\n\n**Text with Image Header:**\n```json\n{\n \"content\": \"Hi your order 121212 is confirmed. Please wait for further updates\",\n \"template_params\": {\n \"name\": \"order_confirmation\",\n \"category\": \"MARKETING\",\n \"language\": \"en\",\n \"processed_params\": {\n \"body\": {\n \"1\": \"121212\"\n },\n \"header\": {\n \"media_url\": \"https://picsum.photos/200/300\",\n \"media_type\": \"image\"\n }\n }\n }\n}\n```\n\n**Text with Copy Code Button:**\n```json\n{\n \"content\": \"Special offer! Get 30% off your next purchase. Use the code below\",\n \"template_params\": {\n \"name\": \"discount_coupon\",\n \"category\": \"MARKETING\",\n \"language\": \"en\",\n \"processed_params\": {\n \"body\": {\n \"discount_percentage\": \"30\"\n },\n \"buttons\": [{\n \"type\": \"copy_code\",\n \"parameter\": \"SAVE20\"\n }]\n }\n }\n}\n```\n",
"security": [
{
"userApiKey": []
@@ -10148,28 +10148,96 @@
},
"template_params": {
"type": "object",
- "description": "The template params for the message in case of whatsapp Channel",
+ "description": "WhatsApp template parameters for sending structured messages",
+ "required": [
+ "name",
+ "category",
+ "language",
+ "processed_params"
+ ],
"properties": {
"name": {
"type": "string",
- "description": "Name of the template",
- "example": "sample_issue_resolution"
+ "description": "Name of the WhatsApp template (must be approved in WhatsApp Business Manager)",
+ "example": "purchase_receipt"
},
"category": {
"type": "string",
+ "enum": [
+ "UTILITY",
+ "MARKETING",
+ "SHIPPING_UPDATE",
+ "TICKET_UPDATE",
+ "ISSUE_RESOLUTION"
+ ],
"description": "Category of the template",
"example": "UTILITY"
},
"language": {
"type": "string",
- "description": "Language of the template",
+ "description": "Language code of the template (BCP 47 format)",
"example": "en_US"
},
"processed_params": {
"type": "object",
- "description": "The processed param values for template variables in template",
- "example": {
- "1": "Chatwoot"
+ "description": "Processed template parameters organized by component type",
+ "properties": {
+ "body": {
+ "type": "object",
+ "description": "Body component parameters with variable placeholders",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "example": {
+ "1": "Visa",
+ "2": "Nike",
+ "3": "Bill"
+ }
+ },
+ "header": {
+ "type": "object",
+ "description": "Header component parameters for media templates",
+ "properties": {
+ "media_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Publicly accessible URL for IMAGE, VIDEO, or DOCUMENT headers",
+ "example": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
+ },
+ "media_type": {
+ "type": "string",
+ "enum": [
+ "image",
+ "video",
+ "document"
+ ],
+ "description": "Type of media for the header",
+ "example": "document"
+ }
+ }
+ },
+ "buttons": {
+ "type": "array",
+ "description": "Button component parameters for interactive templates",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "url",
+ "copy_code"
+ ],
+ "description": "Type of button parameter"
+ },
+ "parameter": {
+ "type": "string",
+ "description": "Dynamic parameter value for the button",
+ "example": "SSFSDFSD"
+ }
+ }
+ }
+ }
}
}
}
@@ -10542,6 +10610,7 @@
"enum": [
"conversation_created",
"conversation_updated",
+ "conversation_resolved",
"message_created"
],
"example": "message_created",
diff --git a/swagger/tag_groups/application_swagger.json b/swagger/tag_groups/application_swagger.json
index f06819d1d..a36443f81 100644
--- a/swagger/tag_groups/application_swagger.json
+++ b/swagger/tag_groups/application_swagger.json
@@ -4334,7 +4334,7 @@
],
"operationId": "create-a-new-message-in-a-conversation",
"summary": "Create New Message",
- "description": "Create a new message in the conversation",
+ "description": "Create a new message in the conversation.\n\n## WhatsApp Template Messages\n\nFor WhatsApp channels, you can send structured template messages using the `template_params` field. \nTemplates must be pre-approved in WhatsApp Business Manager.\n\n### Example Templates\n\n**Text with Image Header:**\n```json\n{\n \"content\": \"Hi your order 121212 is confirmed. Please wait for further updates\",\n \"template_params\": {\n \"name\": \"order_confirmation\",\n \"category\": \"MARKETING\",\n \"language\": \"en\",\n \"processed_params\": {\n \"body\": {\n \"1\": \"121212\"\n },\n \"header\": {\n \"media_url\": \"https://picsum.photos/200/300\",\n \"media_type\": \"image\"\n }\n }\n }\n}\n```\n\n**Text with Copy Code Button:**\n```json\n{\n \"content\": \"Special offer! Get 30% off your next purchase. Use the code below\",\n \"template_params\": {\n \"name\": \"discount_coupon\",\n \"category\": \"MARKETING\",\n \"language\": \"en\",\n \"processed_params\": {\n \"body\": {\n \"discount_percentage\": \"30\"\n },\n \"buttons\": [{\n \"type\": \"copy_code\",\n \"parameter\": \"SAVE20\"\n }]\n }\n }\n}\n```\n",
"security": [
{
"userApiKey": []
@@ -8509,28 +8509,96 @@
},
"template_params": {
"type": "object",
- "description": "The template params for the message in case of whatsapp Channel",
+ "description": "WhatsApp template parameters for sending structured messages",
+ "required": [
+ "name",
+ "category",
+ "language",
+ "processed_params"
+ ],
"properties": {
"name": {
"type": "string",
- "description": "Name of the template",
- "example": "sample_issue_resolution"
+ "description": "Name of the WhatsApp template (must be approved in WhatsApp Business Manager)",
+ "example": "purchase_receipt"
},
"category": {
"type": "string",
+ "enum": [
+ "UTILITY",
+ "MARKETING",
+ "SHIPPING_UPDATE",
+ "TICKET_UPDATE",
+ "ISSUE_RESOLUTION"
+ ],
"description": "Category of the template",
"example": "UTILITY"
},
"language": {
"type": "string",
- "description": "Language of the template",
+ "description": "Language code of the template (BCP 47 format)",
"example": "en_US"
},
"processed_params": {
"type": "object",
- "description": "The processed param values for template variables in template",
- "example": {
- "1": "Chatwoot"
+ "description": "Processed template parameters organized by component type",
+ "properties": {
+ "body": {
+ "type": "object",
+ "description": "Body component parameters with variable placeholders",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "example": {
+ "1": "Visa",
+ "2": "Nike",
+ "3": "Bill"
+ }
+ },
+ "header": {
+ "type": "object",
+ "description": "Header component parameters for media templates",
+ "properties": {
+ "media_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Publicly accessible URL for IMAGE, VIDEO, or DOCUMENT headers",
+ "example": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
+ },
+ "media_type": {
+ "type": "string",
+ "enum": [
+ "image",
+ "video",
+ "document"
+ ],
+ "description": "Type of media for the header",
+ "example": "document"
+ }
+ }
+ },
+ "buttons": {
+ "type": "array",
+ "description": "Button component parameters for interactive templates",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "url",
+ "copy_code"
+ ],
+ "description": "Type of button parameter"
+ },
+ "parameter": {
+ "type": "string",
+ "description": "Dynamic parameter value for the button",
+ "example": "SSFSDFSD"
+ }
+ }
+ }
+ }
}
}
}
@@ -8903,6 +8971,7 @@
"enum": [
"conversation_created",
"conversation_updated",
+ "conversation_resolved",
"message_created"
],
"example": "message_created",
diff --git a/swagger/tag_groups/client_swagger.json b/swagger/tag_groups/client_swagger.json
index c6a3ba408..16c1d5bc7 100644
--- a/swagger/tag_groups/client_swagger.json
+++ b/swagger/tag_groups/client_swagger.json
@@ -3132,28 +3132,96 @@
},
"template_params": {
"type": "object",
- "description": "The template params for the message in case of whatsapp Channel",
+ "description": "WhatsApp template parameters for sending structured messages",
+ "required": [
+ "name",
+ "category",
+ "language",
+ "processed_params"
+ ],
"properties": {
"name": {
"type": "string",
- "description": "Name of the template",
- "example": "sample_issue_resolution"
+ "description": "Name of the WhatsApp template (must be approved in WhatsApp Business Manager)",
+ "example": "purchase_receipt"
},
"category": {
"type": "string",
+ "enum": [
+ "UTILITY",
+ "MARKETING",
+ "SHIPPING_UPDATE",
+ "TICKET_UPDATE",
+ "ISSUE_RESOLUTION"
+ ],
"description": "Category of the template",
"example": "UTILITY"
},
"language": {
"type": "string",
- "description": "Language of the template",
+ "description": "Language code of the template (BCP 47 format)",
"example": "en_US"
},
"processed_params": {
"type": "object",
- "description": "The processed param values for template variables in template",
- "example": {
- "1": "Chatwoot"
+ "description": "Processed template parameters organized by component type",
+ "properties": {
+ "body": {
+ "type": "object",
+ "description": "Body component parameters with variable placeholders",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "example": {
+ "1": "Visa",
+ "2": "Nike",
+ "3": "Bill"
+ }
+ },
+ "header": {
+ "type": "object",
+ "description": "Header component parameters for media templates",
+ "properties": {
+ "media_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Publicly accessible URL for IMAGE, VIDEO, or DOCUMENT headers",
+ "example": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
+ },
+ "media_type": {
+ "type": "string",
+ "enum": [
+ "image",
+ "video",
+ "document"
+ ],
+ "description": "Type of media for the header",
+ "example": "document"
+ }
+ }
+ },
+ "buttons": {
+ "type": "array",
+ "description": "Button component parameters for interactive templates",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "url",
+ "copy_code"
+ ],
+ "description": "Type of button parameter"
+ },
+ "parameter": {
+ "type": "string",
+ "description": "Dynamic parameter value for the button",
+ "example": "SSFSDFSD"
+ }
+ }
+ }
+ }
}
}
}
@@ -3526,6 +3594,7 @@
"enum": [
"conversation_created",
"conversation_updated",
+ "conversation_resolved",
"message_created"
],
"example": "message_created",
diff --git a/swagger/tag_groups/other_swagger.json b/swagger/tag_groups/other_swagger.json
index e2a16245b..c8a5294d3 100644
--- a/swagger/tag_groups/other_swagger.json
+++ b/swagger/tag_groups/other_swagger.json
@@ -2547,28 +2547,96 @@
},
"template_params": {
"type": "object",
- "description": "The template params for the message in case of whatsapp Channel",
+ "description": "WhatsApp template parameters for sending structured messages",
+ "required": [
+ "name",
+ "category",
+ "language",
+ "processed_params"
+ ],
"properties": {
"name": {
"type": "string",
- "description": "Name of the template",
- "example": "sample_issue_resolution"
+ "description": "Name of the WhatsApp template (must be approved in WhatsApp Business Manager)",
+ "example": "purchase_receipt"
},
"category": {
"type": "string",
+ "enum": [
+ "UTILITY",
+ "MARKETING",
+ "SHIPPING_UPDATE",
+ "TICKET_UPDATE",
+ "ISSUE_RESOLUTION"
+ ],
"description": "Category of the template",
"example": "UTILITY"
},
"language": {
"type": "string",
- "description": "Language of the template",
+ "description": "Language code of the template (BCP 47 format)",
"example": "en_US"
},
"processed_params": {
"type": "object",
- "description": "The processed param values for template variables in template",
- "example": {
- "1": "Chatwoot"
+ "description": "Processed template parameters organized by component type",
+ "properties": {
+ "body": {
+ "type": "object",
+ "description": "Body component parameters with variable placeholders",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "example": {
+ "1": "Visa",
+ "2": "Nike",
+ "3": "Bill"
+ }
+ },
+ "header": {
+ "type": "object",
+ "description": "Header component parameters for media templates",
+ "properties": {
+ "media_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Publicly accessible URL for IMAGE, VIDEO, or DOCUMENT headers",
+ "example": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
+ },
+ "media_type": {
+ "type": "string",
+ "enum": [
+ "image",
+ "video",
+ "document"
+ ],
+ "description": "Type of media for the header",
+ "example": "document"
+ }
+ }
+ },
+ "buttons": {
+ "type": "array",
+ "description": "Button component parameters for interactive templates",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "url",
+ "copy_code"
+ ],
+ "description": "Type of button parameter"
+ },
+ "parameter": {
+ "type": "string",
+ "description": "Dynamic parameter value for the button",
+ "example": "SSFSDFSD"
+ }
+ }
+ }
+ }
}
}
}
@@ -2941,6 +3009,7 @@
"enum": [
"conversation_created",
"conversation_updated",
+ "conversation_resolved",
"message_created"
],
"example": "message_created",
diff --git a/swagger/tag_groups/platform_swagger.json b/swagger/tag_groups/platform_swagger.json
index b4feb0024..f816b8c94 100644
--- a/swagger/tag_groups/platform_swagger.json
+++ b/swagger/tag_groups/platform_swagger.json
@@ -3308,28 +3308,96 @@
},
"template_params": {
"type": "object",
- "description": "The template params for the message in case of whatsapp Channel",
+ "description": "WhatsApp template parameters for sending structured messages",
+ "required": [
+ "name",
+ "category",
+ "language",
+ "processed_params"
+ ],
"properties": {
"name": {
"type": "string",
- "description": "Name of the template",
- "example": "sample_issue_resolution"
+ "description": "Name of the WhatsApp template (must be approved in WhatsApp Business Manager)",
+ "example": "purchase_receipt"
},
"category": {
"type": "string",
+ "enum": [
+ "UTILITY",
+ "MARKETING",
+ "SHIPPING_UPDATE",
+ "TICKET_UPDATE",
+ "ISSUE_RESOLUTION"
+ ],
"description": "Category of the template",
"example": "UTILITY"
},
"language": {
"type": "string",
- "description": "Language of the template",
+ "description": "Language code of the template (BCP 47 format)",
"example": "en_US"
},
"processed_params": {
"type": "object",
- "description": "The processed param values for template variables in template",
- "example": {
- "1": "Chatwoot"
+ "description": "Processed template parameters organized by component type",
+ "properties": {
+ "body": {
+ "type": "object",
+ "description": "Body component parameters with variable placeholders",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "example": {
+ "1": "Visa",
+ "2": "Nike",
+ "3": "Bill"
+ }
+ },
+ "header": {
+ "type": "object",
+ "description": "Header component parameters for media templates",
+ "properties": {
+ "media_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Publicly accessible URL for IMAGE, VIDEO, or DOCUMENT headers",
+ "example": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
+ },
+ "media_type": {
+ "type": "string",
+ "enum": [
+ "image",
+ "video",
+ "document"
+ ],
+ "description": "Type of media for the header",
+ "example": "document"
+ }
+ }
+ },
+ "buttons": {
+ "type": "array",
+ "description": "Button component parameters for interactive templates",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "url",
+ "copy_code"
+ ],
+ "description": "Type of button parameter"
+ },
+ "parameter": {
+ "type": "string",
+ "description": "Dynamic parameter value for the button",
+ "example": "SSFSDFSD"
+ }
+ }
+ }
+ }
}
}
}
@@ -3702,6 +3770,7 @@
"enum": [
"conversation_created",
"conversation_updated",
+ "conversation_resolved",
"message_created"
],
"example": "message_created",