mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 19:17:48 +00:00 
			
		
		
		
	Merge branch 'develop' into feat/voice-channel
This commit is contained in:
		| @@ -1,62 +0,0 @@ | ||||
| version: '2' | ||||
| plugins: | ||||
|   rubocop: | ||||
|     enabled: false | ||||
|     channel: rubocop-0-73 | ||||
|   eslint: | ||||
|     enabled: false | ||||
|   csslint: | ||||
|     enabled: true | ||||
|   scss-lint: | ||||
|     enabled: true | ||||
|   brakeman: | ||||
|     enabled: false | ||||
| checks: | ||||
|   similar-code: | ||||
|     enabled: false | ||||
|   method-count: | ||||
|     enabled: true | ||||
|     config: | ||||
|       threshold: 32 | ||||
|   file-lines: | ||||
|     enabled: true | ||||
|     config: | ||||
|       threshold: 300 | ||||
|   method-lines: | ||||
|     config: | ||||
|       threshold: 50 | ||||
| exclude_patterns: | ||||
|   - 'spec/' | ||||
|   - '**/specs/**/**' | ||||
|   - '**/spec/**/**' | ||||
|   - 'db/*' | ||||
|   - 'bin/**/*' | ||||
|   - 'db/**/*' | ||||
|   - 'config/**/*' | ||||
|   - 'public/**/*' | ||||
|   - 'vendor/**/*' | ||||
|   - 'node_modules/**/*' | ||||
|   - 'lib/tasks/auto_annotate_models.rake' | ||||
|   - 'app/test-matchers.js' | ||||
|   - 'docs/*' | ||||
|   - '**/*.md' | ||||
|   - '**/*.yml' | ||||
|   - 'app/javascript/dashboard/i18n/locale' | ||||
|   - '**/*.stories.js' | ||||
|   - 'stories/' | ||||
|   - 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js' | ||||
|   - 'app/javascript/shared/constants/countries.js' | ||||
|   - 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js' | ||||
|   - 'app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js' | ||||
|   - 'app/javascript/dashboard/routes/dashboard/settings/automation/constants.js' | ||||
|   - 'app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js' | ||||
|   - 'app/javascript/dashboard/routes/dashboard/settings/reports/constants.js' | ||||
|   - 'app/javascript/dashboard/store/captain/storeFactory.js' | ||||
|   - 'app/javascript/dashboard/i18n/index.js' | ||||
|   - 'app/javascript/widget/i18n/index.js' | ||||
|   - 'app/javascript/survey/i18n/index.js' | ||||
|   - 'app/javascript/shared/constants/locales.js' | ||||
|   - 'app/javascript/dashboard/helper/specs/macrosFixtures.js' | ||||
|   - 'app/javascript/dashboard/routes/dashboard/settings/macros/constants.js' | ||||
|   - '**/fixtures/**' | ||||
|   - '**/*/fixtures.js' | ||||
							
								
								
									
										7
									
								
								.qlty/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.qlty/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| * | ||||
| !configs | ||||
| !configs/** | ||||
| !hooks | ||||
| !hooks/** | ||||
| !qlty.toml | ||||
| !.gitignore | ||||
							
								
								
									
										2
									
								
								.qlty/configs/.hadolint.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.qlty/configs/.hadolint.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| ignored: | ||||
|   - DL3008 | ||||
							
								
								
									
										1
									
								
								.qlty/configs/.shellcheckrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.qlty/configs/.shellcheckrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| source-path=SCRIPTDIR | ||||
							
								
								
									
										8
									
								
								.qlty/configs/.yamllint.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.qlty/configs/.yamllint.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| rules: | ||||
|   document-start: disable | ||||
|   quoted-strings: | ||||
|     required: only-when-needed | ||||
|     extra-allowed: ["{|}"] | ||||
|   key-duplicates: {} | ||||
|   octal-values: | ||||
|     forbid-implicit-octal: true | ||||
							
								
								
									
										84
									
								
								.qlty/qlty.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								.qlty/qlty.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| # This file was automatically generated by `qlty init`. | ||||
| # You can modify it to suit your needs. | ||||
| # We recommend you to commit this file to your repository. | ||||
| # | ||||
| # This configuration is used by both Qlty CLI and Qlty Cloud. | ||||
| # | ||||
| #     Qlty CLI -- Code quality toolkit for developers | ||||
| #     Qlty Cloud -- Fully automated Code Health Platform | ||||
| # | ||||
| # Try Qlty Cloud: https://qlty.sh | ||||
| # | ||||
| # For a guide to configuration, visit https://qlty.sh/d/config | ||||
| # Or for a full reference, visit https://qlty.sh/d/qlty-toml | ||||
| config_version = "0" | ||||
|  | ||||
| exclude_patterns = [ | ||||
|   "*_min.*", | ||||
|   "*-min.*", | ||||
|   "*.min.*", | ||||
|   "**/.yarn/**", | ||||
|   "**/*.d.ts", | ||||
|   "**/assets/**", | ||||
|   "**/bower_components/**", | ||||
|   "**/build/**", | ||||
|   "**/cache/**", | ||||
|   "**/config/**", | ||||
|   "**/db/**", | ||||
|   "**/deps/**", | ||||
|   "**/dist/**", | ||||
|   "**/extern/**", | ||||
|   "**/external/**", | ||||
|   "**/generated/**", | ||||
|   "**/Godeps/**", | ||||
|   "**/gradlew/**", | ||||
|   "**/mvnw/**", | ||||
|   "**/node_modules/**", | ||||
|   "**/protos/**", | ||||
|   "**/seed/**", | ||||
|   "**/target/**", | ||||
|   "**/templates/**", | ||||
|   "**/testdata/**", | ||||
|   "**/vendor/**", "spec/", "**/specs/**/**", "**/spec/**/**", "db/*", "bin/**/*", "db/**/*", "config/**/*", "public/**/*", "vendor/**/*", "node_modules/**/*", "lib/tasks/auto_annotate_models.rake", "app/test-matchers.js", "docs/*", "**/*.md", "**/*.yml", "app/javascript/dashboard/i18n/locale", "**/*.stories.js", "stories/", "app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js", "app/javascript/shared/constants/countries.js", "app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js", "app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js", "app/javascript/dashboard/routes/dashboard/settings/automation/constants.js", "app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js", "app/javascript/dashboard/routes/dashboard/settings/reports/constants.js", "app/javascript/dashboard/store/captain/storeFactory.js", "app/javascript/dashboard/i18n/index.js", "app/javascript/widget/i18n/index.js", "app/javascript/survey/i18n/index.js", "app/javascript/shared/constants/locales.js", "app/javascript/dashboard/helper/specs/macrosFixtures.js", "app/javascript/dashboard/routes/dashboard/settings/macros/constants.js", "**/fixtures/**", "**/*/fixtures.js", | ||||
| ] | ||||
|  | ||||
| test_patterns = [ | ||||
|   "**/test/**", | ||||
|   "**/spec/**", | ||||
|   "**/*.test.*", | ||||
|   "**/*.spec.*", | ||||
|   "**/*_test.*", | ||||
|   "**/*_spec.*", | ||||
|   "**/test_*.*", | ||||
|   "**/spec_*.*", | ||||
| ] | ||||
|  | ||||
| [smells] | ||||
| mode = "comment" | ||||
|  | ||||
| [smells.boolean_logic] | ||||
| threshold = 4 | ||||
|  | ||||
| [smells.file_complexity] | ||||
| threshold = 66 | ||||
| enabled = true | ||||
|  | ||||
| [smells.return_statements] | ||||
| threshold = 4 | ||||
|  | ||||
| [smells.nested_control_flow] | ||||
| threshold = 4 | ||||
|  | ||||
| [smells.function_parameters] | ||||
| threshold = 4 | ||||
|  | ||||
| [smells.function_complexity] | ||||
| threshold = 5 | ||||
|  | ||||
| [smells.duplication] | ||||
| enabled = true | ||||
| threshold = 20 | ||||
|  | ||||
| [[source]] | ||||
| name = "default" | ||||
| default = true | ||||
| @@ -283,7 +283,7 @@ Rails/RedundantActiveRecordAllMethod: | ||||
|   Enabled: false | ||||
|  | ||||
| Layout/TrailingEmptyLines: | ||||
|   Enabled: false | ||||
|   Enabled: true | ||||
|  | ||||
| Style/SafeNavigationChainLength: | ||||
|   Enabled: false | ||||
|   | ||||
| @@ -29,6 +29,6 @@ class Api::V1::Accounts::CampaignsController < Api::V1::Accounts::BaseController | ||||
|  | ||||
|   def campaign_params | ||||
|     params.require(:campaign).permit(:title, :description, :message, :enabled, :trigger_only_during_business_hours, :inbox_id, :sender_id, | ||||
|                                      :scheduled_at, audience: [:type, :id], trigger_rules: {}) | ||||
|                                      :scheduled_at, audience: [:type, :id], trigger_rules: {}, template_params: {}) | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -11,4 +11,4 @@ class Api::V1::Accounts::Integrations::NotionController < Api::V1::Accounts::Bas | ||||
|   def fetch_hook | ||||
|     @hook = Integrations::Hook.where(account: Current.account).find_by(app_id: 'notion') | ||||
|   end | ||||
| end | ||||
| end | ||||
|   | ||||
| @@ -18,4 +18,4 @@ class Api::V1::Accounts::Notion::AuthorizationsController < Api::V1::Accounts::O | ||||
|       render json: { success: false }, status: :unprocessable_entity | ||||
|     end | ||||
|   end | ||||
| end | ||||
| end | ||||
|   | ||||
| @@ -0,0 +1,64 @@ | ||||
| class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts::BaseController | ||||
|   before_action :validate_feature_enabled! | ||||
|  | ||||
|   # POST /api/v1/accounts/:account_id/whatsapp/authorization | ||||
|   # Handles the embedded signup callback data from the Facebook SDK | ||||
|   def create | ||||
|     validate_embedded_signup_params! | ||||
|     channel = process_embedded_signup | ||||
|     render_success_response(channel.inbox) | ||||
|   rescue StandardError => e | ||||
|     render_error_response(e) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def process_embedded_signup | ||||
|     service = Whatsapp::EmbeddedSignupService.new( | ||||
|       account: Current.account, | ||||
|       code: params[:code], | ||||
|       business_id: params[:business_id], | ||||
|       waba_id: params[:waba_id], | ||||
|       phone_number_id: params[:phone_number_id] | ||||
|     ) | ||||
|     service.perform | ||||
|   end | ||||
|  | ||||
|   def render_success_response(inbox) | ||||
|     render json: { | ||||
|       success: true, | ||||
|       id: inbox.id, | ||||
|       name: inbox.name, | ||||
|       channel_type: 'whatsapp' | ||||
|     } | ||||
|   end | ||||
|  | ||||
|   def render_error_response(error) | ||||
|     Rails.logger.error "[WHATSAPP AUTHORIZATION] Embedded signup error: #{error.message}" | ||||
|     Rails.logger.error error.backtrace.join("\n") | ||||
|     render json: { | ||||
|       success: false, | ||||
|       error: error.message | ||||
|     }, status: :unprocessable_entity | ||||
|   end | ||||
|  | ||||
|   def validate_feature_enabled! | ||||
|     return if Current.account.feature_whatsapp_embedded_signup? | ||||
|  | ||||
|     render json: { | ||||
|       success: false, | ||||
|       error: 'WhatsApp embedded signup is not enabled for this account' | ||||
|     }, status: :forbidden | ||||
|   end | ||||
|  | ||||
|   def validate_embedded_signup_params! | ||||
|     missing_params = [] | ||||
|     missing_params << 'code' if params[:code].blank? | ||||
|     missing_params << 'business_id' if params[:business_id].blank? | ||||
|     missing_params << 'waba_id' if params[:waba_id].blank? | ||||
|  | ||||
|     return if missing_params.empty? | ||||
|  | ||||
|     raise ArgumentError, "Required parameters are missing: #{missing_params.join(', ')}" | ||||
|   end | ||||
| end | ||||
| @@ -67,6 +67,8 @@ class DashboardController < ActionController::Base | ||||
|       FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''), | ||||
|       INSTAGRAM_APP_ID: GlobalConfigService.load('INSTAGRAM_APP_ID', ''), | ||||
|       FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v17.0'), | ||||
|       WHATSAPP_APP_ID: GlobalConfigService.load('WHATSAPP_APP_ID', ''), | ||||
|       WHATSAPP_CONFIGURATION_ID: GlobalConfigService.load('WHATSAPP_CONFIGURATION_ID', ''), | ||||
|       IS_ENTERPRISE: ChatwootApp.enterprise?, | ||||
|       AZURE_APP_ID: GlobalConfigService.load('AZURE_APP_ID', ''), | ||||
|       GIT_SHA: GIT_HASH | ||||
|   | ||||
| @@ -33,4 +33,4 @@ class Notion::CallbacksController < OauthCallbackController | ||||
|   def notion_redirect_uri | ||||
|     "#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/#{account.id}/settings/integrations/notion" | ||||
|   end | ||||
| end | ||||
| end | ||||
|   | ||||
| @@ -39,8 +39,10 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController | ||||
|       'email' => ['MAILER_INBOUND_EMAIL_DOMAIN'], | ||||
|       'linear' => %w[LINEAR_CLIENT_ID LINEAR_CLIENT_SECRET], | ||||
|       'slack' => %w[SLACK_CLIENT_ID SLACK_CLIENT_SECRET], | ||||
|       'instagram' => %w[INSTAGRAM_APP_ID INSTAGRAM_APP_SECRET INSTAGRAM_VERIFY_TOKEN INSTAGRAM_API_VERSION ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT], | ||||
|       'whatsapp_embedded' => %w[WHATSAPP_APP_ID WHATSAPP_APP_SECRET WHATSAPP_CONFIGURATION_ID WHATSAPP_API_VERSION], | ||||
|       'notion' => %w[NOTION_CLIENT_ID NOTION_CLIENT_SECRET], | ||||
|       'instagram' => %w[INSTAGRAM_APP_ID INSTAGRAM_APP_SECRET INSTAGRAM_VERIFY_TOKEN INSTAGRAM_API_VERSION ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT] | ||||
|       'google' => %w[GOOGLE_OAUTH_CLIENT_ID GOOGLE_OAUTH_CLIENT_SECRET GOOGLE_OAUTH_REDIRECT_URI] | ||||
|     } | ||||
|  | ||||
|     @allowed_configs = mapping.fetch(@config, %w[ENABLE_ACCOUNT_SIGNUP FIREBASE_PROJECT_ID FIREBASE_CREDENTIALS]) | ||||
|   | ||||
| @@ -7,8 +7,9 @@ | ||||
| class SuperAdmin::ApplicationController < Administrate::ApplicationController | ||||
|   include ActionView::Helpers::TagHelper | ||||
|   include ActionView::Context | ||||
|   include SuperAdmin::NavigationHelper | ||||
|  | ||||
|   helper_method :render_vue_component | ||||
|   helper_method :render_vue_component, :settings_open?, :settings_pages | ||||
|   # authenticiation done via devise : SuperAdmin Model | ||||
|   before_action :authenticate_super_admin! | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| # TODO: Move this values to features.yml itself | ||||
| # No need to replicate the same values in two places | ||||
| 
 | ||||
| # ------- Premium Features ------- # | ||||
| captain: | ||||
|   name: 'Captain' | ||||
|   description: 'Enable AI-powered conversations with your customers.' | ||||
| @@ -32,6 +34,15 @@ disable_branding: | ||||
|   enabled: <%= (ChatwootHub.pricing_plan != 'community') %> | ||||
|   icon: 'icon-sailbot-fill' | ||||
|   enterprise: true | ||||
| 
 | ||||
| # ------- Product Features ------- # | ||||
| help_center: | ||||
|   name: 'Help Center' | ||||
|   description: 'Allow agents to create help center articles and publish them in a portal.' | ||||
|   enabled: true | ||||
|   icon: 'icon-book-2-line' | ||||
| 
 | ||||
| # ------- Communication Channels ------- # | ||||
| live_chat: | ||||
|   name: 'Live Chat' | ||||
|   description: 'Improve your customer experience using a live chat on your website.' | ||||
| @@ -42,6 +53,12 @@ email: | ||||
|   description: 'Manage your email customer interactions from Chatwoot.' | ||||
|   enabled: true | ||||
|   icon: 'icon-mail-send-fill' | ||||
|   config_key: 'email' | ||||
| sms: | ||||
|   name: 'SMS' | ||||
|   description: 'Manage your SMS customer interactions from Chatwoot.' | ||||
|   enabled: true | ||||
|   icon: 'icon-message-line' | ||||
| messenger: | ||||
|   name: 'Messenger' | ||||
|   description: 'Stay connected with your customers on Facebook & Instagram.' | ||||
| @@ -69,22 +86,22 @@ line: | ||||
|   description: 'Manage your Line customer interactions from Chatwoot.' | ||||
|   enabled: true | ||||
|   icon: 'icon-line-line' | ||||
| sms: | ||||
|   name: 'SMS' | ||||
|   description: 'Manage your SMS customer interactions from Chatwoot.' | ||||
| 
 | ||||
| # ------- OAuth & Authentication ------- # | ||||
| google: | ||||
|   name: 'Google' | ||||
|   description: 'Configuration for setting up Google OAuth Integration' | ||||
|   enabled: true | ||||
|   icon: 'icon-message-line' | ||||
| help_center: | ||||
|   name: 'Help Center' | ||||
|   description: 'Allow agents to create help center articles and publish them in a portal.' | ||||
|   enabled: true | ||||
|   icon: 'icon-book-2-line' | ||||
|   icon: 'icon-google' | ||||
|   config_key: 'google' | ||||
| microsoft: | ||||
|   name: 'Microsoft' | ||||
|   description: 'Configuration for setting up Microsoft Email' | ||||
|   enabled: true | ||||
|   icon: 'icon-microsoft' | ||||
|   config_key: 'microsoft' | ||||
| 
 | ||||
| # ------- Third-party Integrations ------- # | ||||
| linear: | ||||
|   name: 'Linear' | ||||
|   description: 'Configuration for setting up Linear Integration' | ||||
| @@ -103,6 +120,12 @@ slack: | ||||
|   enabled: true | ||||
|   icon: 'icon-slack' | ||||
|   config_key: 'slack' | ||||
| whatsapp_embedded: | ||||
|   name: 'WhatsApp Embedded' | ||||
|   description: 'Configuration for setting up WhatsApp Embedded Integration' | ||||
|   enabled: true | ||||
|   icon: 'icon-whatsapp-line' | ||||
|   config_key: 'whatsapp_embedded' | ||||
| shopify: | ||||
|   name: 'Shopify' | ||||
|   description: 'Configuration for setting up Shopify Integration' | ||||
| @@ -1,6 +1,6 @@ | ||||
| module SuperAdmin::FeaturesHelper | ||||
|   def self.available_features | ||||
|     YAML.load(ERB.new(Rails.root.join('enterprise/app/helpers/super_admin/features.yml').read).result).with_indifferent_access | ||||
|     YAML.load(ERB.new(Rails.root.join('app/helpers/super_admin/features.yml').read).result).with_indifferent_access | ||||
|   end | ||||
| 
 | ||||
|   def self.plan_details | ||||
							
								
								
									
										16
									
								
								app/helpers/super_admin/navigation_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/helpers/super_admin/navigation_helper.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| module SuperAdmin::NavigationHelper | ||||
|   def settings_open? | ||||
|     params[:controller].in? %w[super_admin/settings super_admin/app_configs] | ||||
|   end | ||||
|  | ||||
|   def settings_pages | ||||
|     features = SuperAdmin::FeaturesHelper.available_features.select do |_feature, attrs| | ||||
|       attrs['config_key'].present? && attrs['enabled'] | ||||
|     end | ||||
|  | ||||
|     # Add general at the beginning | ||||
|     general_feature = [['general', { 'config_key' => 'general', 'name' => 'General' }]] | ||||
|  | ||||
|     general_feature + features.to_a | ||||
|   end | ||||
| end | ||||
							
								
								
									
										14
									
								
								app/javascript/dashboard/api/channel/whatsappChannel.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/javascript/dashboard/api/channel/whatsappChannel.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| /* global axios */ | ||||
| import ApiClient from '../ApiClient'; | ||||
|  | ||||
| class WhatsappChannel extends ApiClient { | ||||
|   constructor() { | ||||
|     super('whatsapp', { accountScoped: true }); | ||||
|   } | ||||
|  | ||||
|   createEmbeddedSignup(params) { | ||||
|     return axios.post(`${this.baseUrl()}/whatsapp/authorization`, params); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default new WhatsappChannel(); | ||||
| @@ -0,0 +1,37 @@ | ||||
| <script setup> | ||||
| import { ONE_OFF_CAMPAIGN_EMPTY_STATE_CONTENT } from './CampaignEmptyStateContent'; | ||||
|  | ||||
| import EmptyStateLayout from 'dashboard/components-next/EmptyStateLayout.vue'; | ||||
| import CampaignCard from 'dashboard/components-next/Campaigns/CampaignCard/CampaignCard.vue'; | ||||
|  | ||||
| defineProps({ | ||||
|   title: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   subtitle: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <EmptyStateLayout :title="title" :subtitle="subtitle"> | ||||
|     <template #empty-state-item> | ||||
|       <div class="flex flex-col gap-4 p-px"> | ||||
|         <CampaignCard | ||||
|           v-for="campaign in ONE_OFF_CAMPAIGN_EMPTY_STATE_CONTENT" | ||||
|           :key="campaign.id" | ||||
|           :title="campaign.title" | ||||
|           :message="campaign.message" | ||||
|           :is-enabled="campaign.enabled" | ||||
|           :status="campaign.campaign_status" | ||||
|           :sender="campaign.sender" | ||||
|           :inbox="campaign.inbox" | ||||
|           :scheduled-at="campaign.scheduled_at" | ||||
|         /> | ||||
|       </div> | ||||
|     </template> | ||||
|   </EmptyStateLayout> | ||||
| </template> | ||||
| @@ -0,0 +1,48 @@ | ||||
| <script setup> | ||||
| import { useI18n } from 'vue-i18n'; | ||||
| import { useStore } from 'dashboard/composables/store'; | ||||
| import { useAlert, useTrack } from 'dashboard/composables'; | ||||
| import { CAMPAIGN_TYPES } from 'shared/constants/campaign.js'; | ||||
| import { CAMPAIGNS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events.js'; | ||||
|  | ||||
| import WhatsAppCampaignForm from 'dashboard/components-next/Campaigns/Pages/CampaignPage/WhatsAppCampaign/WhatsAppCampaignForm.vue'; | ||||
|  | ||||
| const emit = defineEmits(['close']); | ||||
|  | ||||
| const store = useStore(); | ||||
| const { t } = useI18n(); | ||||
|  | ||||
| const addCampaign = async campaignDetails => { | ||||
|   try { | ||||
|     await store.dispatch('campaigns/create', campaignDetails); | ||||
|  | ||||
|     useTrack(CAMPAIGNS_EVENTS.CREATE_CAMPAIGN, { | ||||
|       type: CAMPAIGN_TYPES.ONE_OFF, | ||||
|     }); | ||||
|  | ||||
|     useAlert(t('CAMPAIGN.WHATSAPP.CREATE.FORM.API.SUCCESS_MESSAGE')); | ||||
|   } catch (error) { | ||||
|     const errorMessage = | ||||
|       error?.response?.message || | ||||
|       t('CAMPAIGN.WHATSAPP.CREATE.FORM.API.ERROR_MESSAGE'); | ||||
|     useAlert(errorMessage); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| const handleSubmit = campaignDetails => { | ||||
|   addCampaign(campaignDetails); | ||||
| }; | ||||
|  | ||||
| const handleClose = () => emit('close'); | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <div | ||||
|     class="w-[25rem] z-50 min-w-0 absolute top-10 ltr:right-0 rtl:left-0 bg-n-alpha-3 backdrop-blur-[100px] p-6 rounded-xl border border-n-weak shadow-md flex flex-col gap-6" | ||||
|   > | ||||
|     <h3 class="text-base font-medium text-n-slate-12"> | ||||
|       {{ t(`CAMPAIGN.WHATSAPP.CREATE.TITLE`) }} | ||||
|     </h3> | ||||
|     <WhatsAppCampaignForm @submit="handleSubmit" @cancel="handleClose" /> | ||||
|   </div> | ||||
| </template> | ||||
| @@ -0,0 +1,357 @@ | ||||
| <script setup> | ||||
| import { reactive, computed, watch, ref } from 'vue'; | ||||
| import { useI18n } from 'vue-i18n'; | ||||
| import { useVuelidate } from '@vuelidate/core'; | ||||
| import { required, minLength } from '@vuelidate/validators'; | ||||
| import { useMapGetter } from 'dashboard/composables/store'; | ||||
|  | ||||
| import Input from 'dashboard/components-next/input/Input.vue'; | ||||
| import Button from 'dashboard/components-next/button/Button.vue'; | ||||
| import ComboBox from 'dashboard/components-next/combobox/ComboBox.vue'; | ||||
| import TagMultiSelectComboBox from 'dashboard/components-next/combobox/TagMultiSelectComboBox.vue'; | ||||
|  | ||||
| const emit = defineEmits(['submit', 'cancel']); | ||||
|  | ||||
| const { t } = useI18n(); | ||||
|  | ||||
| const formState = { | ||||
|   uiFlags: useMapGetter('campaigns/getUIFlags'), | ||||
|   labels: useMapGetter('labels/getLabels'), | ||||
|   inboxes: useMapGetter('inboxes/getWhatsAppInboxes'), | ||||
|   getWhatsAppTemplates: useMapGetter('inboxes/getWhatsAppTemplates'), | ||||
| }; | ||||
|  | ||||
| const initialState = { | ||||
|   title: '', | ||||
|   inboxId: null, | ||||
|   templateId: null, | ||||
|   scheduledAt: null, | ||||
|   selectedAudience: [], | ||||
| }; | ||||
|  | ||||
| const state = reactive({ ...initialState }); | ||||
| const processedParams = ref({}); | ||||
|  | ||||
| const rules = { | ||||
|   title: { required, minLength: minLength(1) }, | ||||
|   inboxId: { required }, | ||||
|   templateId: { required }, | ||||
|   scheduledAt: { required }, | ||||
|   selectedAudience: { required }, | ||||
| }; | ||||
|  | ||||
| const v$ = useVuelidate(rules, state); | ||||
|  | ||||
| const isCreating = computed(() => formState.uiFlags.value.isCreating); | ||||
|  | ||||
| const currentDateTime = computed(() => { | ||||
|   // Added to disable the scheduled at field from being set to the current time | ||||
|   const now = new Date(); | ||||
|   const localTime = new Date(now.getTime() - now.getTimezoneOffset() * 60000); | ||||
|   return localTime.toISOString().slice(0, 16); | ||||
| }); | ||||
|  | ||||
| const mapToOptions = (items, valueKey, labelKey) => | ||||
|   items?.map(item => ({ | ||||
|     value: item[valueKey], | ||||
|     label: item[labelKey], | ||||
|   })) ?? []; | ||||
|  | ||||
| const audienceList = computed(() => | ||||
|   mapToOptions(formState.labels.value, 'id', 'title') | ||||
| ); | ||||
|  | ||||
| const inboxOptions = computed(() => | ||||
|   mapToOptions(formState.inboxes.value, 'id', 'name') | ||||
| ); | ||||
|  | ||||
| const templateOptions = computed(() => { | ||||
|   if (!state.inboxId) return []; | ||||
|   const templates = formState.getWhatsAppTemplates.value(state.inboxId); | ||||
|   return templates.map(template => { | ||||
|     // Create a more user-friendly label from template name | ||||
|     const friendlyName = template.name | ||||
|       .replace(/_/g, ' ') | ||||
|       .replace(/\b\w/g, l => l.toUpperCase()); | ||||
|  | ||||
|     return { | ||||
|       value: template.id, | ||||
|       label: `${friendlyName} (${template.language || 'en'})`, | ||||
|       template: template, | ||||
|     }; | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| const selectedTemplate = computed(() => { | ||||
|   if (!state.templateId) return null; | ||||
|   return templateOptions.value.find(option => option.value === state.templateId) | ||||
|     ?.template; | ||||
| }); | ||||
|  | ||||
| const templateString = computed(() => { | ||||
|   if (!selectedTemplate.value) return ''; | ||||
|   try { | ||||
|     return ( | ||||
|       selectedTemplate.value.components?.find( | ||||
|         component => component.type === 'BODY' | ||||
|       )?.text || '' | ||||
|     ); | ||||
|   } catch (error) { | ||||
|     return ''; | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const processedString = computed(() => { | ||||
|   if (!templateString.value) return ''; | ||||
|   return templateString.value.replace(/{{([^}]+)}}/g, (match, variable) => { | ||||
|     return processedParams.value[variable] || `{{${variable}}}`; | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| const getErrorMessage = (field, errorKey) => { | ||||
|   const baseKey = 'CAMPAIGN.WHATSAPP.CREATE.FORM'; | ||||
|   return v$.value[field].$error ? t(`${baseKey}.${errorKey}.ERROR`) : ''; | ||||
| }; | ||||
|  | ||||
| const formErrors = computed(() => ({ | ||||
|   title: getErrorMessage('title', 'TITLE'), | ||||
|   inbox: getErrorMessage('inboxId', 'INBOX'), | ||||
|   template: getErrorMessage('templateId', 'TEMPLATE'), | ||||
|   scheduledAt: getErrorMessage('scheduledAt', 'SCHEDULED_AT'), | ||||
|   audience: getErrorMessage('selectedAudience', 'AUDIENCE'), | ||||
| })); | ||||
|  | ||||
| const hasRequiredTemplateParams = computed(() => { | ||||
|   const params = Object.values(processedParams.value); | ||||
|   return params.length === 0 || params.every(param => param.trim() !== ''); | ||||
| }); | ||||
|  | ||||
| const isSubmitDisabled = computed( | ||||
|   () => v$.value.$invalid || !hasRequiredTemplateParams.value | ||||
| ); | ||||
|  | ||||
| const formatToUTCString = localDateTime => | ||||
|   localDateTime ? new Date(localDateTime).toISOString() : null; | ||||
|  | ||||
| const resetState = () => { | ||||
|   Object.assign(state, initialState); | ||||
|   processedParams.value = {}; | ||||
|   v$.value.$reset(); | ||||
| }; | ||||
|  | ||||
| const handleCancel = () => emit('cancel'); | ||||
|  | ||||
| const generateVariables = () => { | ||||
|   const matchedVariables = templateString.value.match(/{{([^}]+)}}/g); | ||||
|   if (!matchedVariables) { | ||||
|     processedParams.value = {}; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const finalVars = matchedVariables.map(match => match.replace(/{{|}}/g, '')); | ||||
|   processedParams.value = finalVars.reduce((acc, variable) => { | ||||
|     acc[variable] = processedParams.value[variable] || ''; | ||||
|     return acc; | ||||
|   }, {}); | ||||
| }; | ||||
|  | ||||
| const prepareCampaignDetails = () => { | ||||
|   // Find the selected template to get its content | ||||
|   const currentTemplate = selectedTemplate.value; | ||||
|  | ||||
|   // Extract template content - this should be the template message body | ||||
|   const templateContent = templateString.value; | ||||
|  | ||||
|   // Prepare template_params object with the same structure as used in contacts | ||||
|   const templateParams = { | ||||
|     name: currentTemplate?.name || '', | ||||
|     namespace: currentTemplate?.namespace || '', | ||||
|     category: currentTemplate?.category || 'UTILITY', | ||||
|     language: currentTemplate?.language || 'en_US', | ||||
|     processed_params: processedParams.value, | ||||
|   }; | ||||
|  | ||||
|   return { | ||||
|     title: state.title, | ||||
|     message: templateContent, | ||||
|     template_params: templateParams, | ||||
|     inbox_id: state.inboxId, | ||||
|     scheduled_at: formatToUTCString(state.scheduledAt), | ||||
|     audience: state.selectedAudience?.map(id => ({ | ||||
|       id, | ||||
|       type: 'Label', | ||||
|     })), | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| const handleSubmit = async () => { | ||||
|   const isFormValid = await v$.value.$validate(); | ||||
|   if (!isFormValid) return; | ||||
|  | ||||
|   emit('submit', prepareCampaignDetails()); | ||||
|   resetState(); | ||||
|   handleCancel(); | ||||
| }; | ||||
|  | ||||
| // Reset template selection when inbox changes | ||||
| watch( | ||||
|   () => state.inboxId, | ||||
|   () => { | ||||
|     state.templateId = null; | ||||
|     processedParams.value = {}; | ||||
|   } | ||||
| ); | ||||
|  | ||||
| // Generate variables when template changes | ||||
| watch( | ||||
|   () => state.templateId, | ||||
|   () => { | ||||
|     generateVariables(); | ||||
|   } | ||||
| ); | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <form class="flex flex-col gap-4" @submit.prevent="handleSubmit"> | ||||
|     <Input | ||||
|       v-model="state.title" | ||||
|       :label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.TITLE.LABEL')" | ||||
|       :placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.TITLE.PLACEHOLDER')" | ||||
|       :message="formErrors.title" | ||||
|       :message-type="formErrors.title ? 'error' : 'info'" | ||||
|     /> | ||||
|  | ||||
|     <div class="flex flex-col gap-1"> | ||||
|       <label for="inbox" class="mb-0.5 text-sm font-medium text-n-slate-12"> | ||||
|         {{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.INBOX.LABEL') }} | ||||
|       </label> | ||||
|       <ComboBox | ||||
|         id="inbox" | ||||
|         v-model="state.inboxId" | ||||
|         :options="inboxOptions" | ||||
|         :has-error="!!formErrors.inbox" | ||||
|         :placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.INBOX.PLACEHOLDER')" | ||||
|         :message="formErrors.inbox" | ||||
|         class="[&>div>button]:bg-n-alpha-black2 [&>div>button:not(.focused)]:dark:outline-n-weak [&>div>button:not(.focused)]:hover:!outline-n-slate-6" | ||||
|       /> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flex flex-col gap-1"> | ||||
|       <label for="template" class="mb-0.5 text-sm font-medium text-n-slate-12"> | ||||
|         {{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.LABEL') }} | ||||
|       </label> | ||||
|       <ComboBox | ||||
|         id="template" | ||||
|         v-model="state.templateId" | ||||
|         :options="templateOptions" | ||||
|         :has-error="!!formErrors.template" | ||||
|         :placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.PLACEHOLDER')" | ||||
|         :message="formErrors.template" | ||||
|         class="[&>div>button]:bg-n-alpha-black2 [&>div>button:not(.focused)]:dark:outline-n-weak [&>div>button:not(.focused)]:hover:!outline-n-slate-6" | ||||
|       /> | ||||
|       <p class="mt-1 text-xs text-n-slate-11"> | ||||
|         {{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.INFO') }} | ||||
|       </p> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Template Preview --> | ||||
|     <div | ||||
|       v-if="selectedTemplate" | ||||
|       class="flex flex-col gap-4 p-4 rounded-lg bg-n-alpha-black2" | ||||
|     > | ||||
|       <div class="flex justify-between items-center"> | ||||
|         <h3 class="text-sm font-medium text-n-slate-12"> | ||||
|           {{ selectedTemplate.name }} | ||||
|         </h3> | ||||
|         <span class="text-xs text-n-slate-11"> | ||||
|           {{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.LANGUAGE') }}: | ||||
|           {{ selectedTemplate.language || 'en' }} | ||||
|         </span> | ||||
|       </div> | ||||
|  | ||||
|       <div class="flex flex-col gap-2"> | ||||
|         <div class="rounded-md bg-n-alpha-black3"> | ||||
|           <div class="text-sm whitespace-pre-wrap text-n-slate-12"> | ||||
|             {{ processedString }} | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="text-xs text-n-slate-11"> | ||||
|         {{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.CATEGORY') }}: | ||||
|         {{ selectedTemplate.category || 'UTILITY' }} | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Template Variables --> | ||||
|     <div | ||||
|       v-if="Object.keys(processedParams).length > 0" | ||||
|       class="flex flex-col gap-3" | ||||
|     > | ||||
|       <label class="text-sm font-medium text-n-slate-12"> | ||||
|         {{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.VARIABLES_LABEL') }} | ||||
|       </label> | ||||
|       <div class="flex flex-col gap-2"> | ||||
|         <div | ||||
|           v-for="(value, key) in processedParams" | ||||
|           :key="key" | ||||
|           class="flex gap-2 items-center" | ||||
|         > | ||||
|           <Input | ||||
|             v-model="processedParams[key]" | ||||
|             type="text" | ||||
|             class="flex-1" | ||||
|             :placeholder=" | ||||
|               t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.VARIABLE_PLACEHOLDER', { | ||||
|                 variable: key, | ||||
|               }) | ||||
|             " | ||||
|           /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="flex flex-col gap-1"> | ||||
|       <label for="audience" class="mb-0.5 text-sm font-medium text-n-slate-12"> | ||||
|         {{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.AUDIENCE.LABEL') }} | ||||
|       </label> | ||||
|       <TagMultiSelectComboBox | ||||
|         v-model="state.selectedAudience" | ||||
|         :options="audienceList" | ||||
|         :label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.AUDIENCE.LABEL')" | ||||
|         :placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.AUDIENCE.PLACEHOLDER')" | ||||
|         :has-error="!!formErrors.audience" | ||||
|         :message="formErrors.audience" | ||||
|         class="[&>div>button]:bg-n-alpha-black2" | ||||
|       /> | ||||
|     </div> | ||||
|  | ||||
|     <Input | ||||
|       v-model="state.scheduledAt" | ||||
|       :label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.SCHEDULED_AT.LABEL')" | ||||
|       type="datetime-local" | ||||
|       :min="currentDateTime" | ||||
|       :placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.SCHEDULED_AT.PLACEHOLDER')" | ||||
|       :message="formErrors.scheduledAt" | ||||
|       :message-type="formErrors.scheduledAt ? 'error' : 'info'" | ||||
|     /> | ||||
|  | ||||
|     <div class="flex gap-3 justify-between items-center w-full"> | ||||
|       <Button | ||||
|         variant="faded" | ||||
|         color="slate" | ||||
|         type="button" | ||||
|         :label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.BUTTONS.CANCEL')" | ||||
|         class="w-full bg-n-alpha-2 text-n-blue-text hover:bg-n-alpha-3" | ||||
|         @click="handleCancel" | ||||
|       /> | ||||
|       <Button | ||||
|         :label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.BUTTONS.CREATE')" | ||||
|         class="w-full" | ||||
|         type="submit" | ||||
|         :is-loading="isCreating" | ||||
|         :disabled="isCreating || isSubmitDisabled" | ||||
|       /> | ||||
|     </div> | ||||
|   </form> | ||||
| </template> | ||||
| @@ -52,9 +52,9 @@ const handleBreadcrumbClick = item => { | ||||
|  | ||||
| <template> | ||||
|   <section | ||||
|     class="my-4 px-10 flex flex-col w-full h-screen overflow-y-auto bg-n-background" | ||||
|     class="mt-4 px-10 flex flex-col w-full h-screen overflow-y-auto bg-n-background" | ||||
|   > | ||||
|     <div class="max-w-[60rem] mx-auto flex flex-col w-full h-full"> | ||||
|     <div class="max-w-[60rem] mx-auto flex flex-col w-full h-full mb-4"> | ||||
|       <header class="mb-7 sticky top-0 z-10 bg-n-background"> | ||||
|         <Breadcrumb :items="breadcrumbItems" @click="handleBreadcrumbClick" /> | ||||
|       </header> | ||||
|   | ||||
| @@ -0,0 +1,21 @@ | ||||
| <script setup> | ||||
| import AddNewRulesDialog from './AddNewRulesDialog.vue'; | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <Story | ||||
|     title="Captain/Assistant/AddNewRulesDialog" | ||||
|     :layout="{ type: 'grid', width: '800px' }" | ||||
|   > | ||||
|     <Variant title="Default"> | ||||
|       <div class="px-4 py-4 bg-n-background h-[200px]"> | ||||
|         <AddNewRulesDialog | ||||
|           button-label="Add a guardrail" | ||||
|           placeholder="Type in another guardrail..." | ||||
|           confirm-label="Create" | ||||
|           cancel-label="Cancel" | ||||
|         /> | ||||
|       </div> | ||||
|     </Variant> | ||||
|   </Story> | ||||
| </template> | ||||
| @@ -0,0 +1,77 @@ | ||||
| <script setup> | ||||
| import { useToggle } from '@vueuse/core'; | ||||
|  | ||||
| import Button from 'dashboard/components-next/button/Button.vue'; | ||||
| import InlineInput from 'dashboard/components-next/inline-input/InlineInput.vue'; | ||||
|  | ||||
| defineProps({ | ||||
|   placeholder: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   buttonLabel: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   confirmLabel: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   cancelLabel: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits(['add']); | ||||
|  | ||||
| const modelValue = defineModel({ | ||||
|   type: String, | ||||
|   default: '', | ||||
| }); | ||||
|  | ||||
| const [showPopover, togglePopover] = useToggle(); | ||||
| const onClickAdd = () => { | ||||
|   if (!modelValue.value?.trim()) return; | ||||
|   emit('add', modelValue.value.trim()); | ||||
|   modelValue.value = ''; | ||||
|   togglePopover(false); | ||||
| }; | ||||
|  | ||||
| const onClickCancel = () => { | ||||
|   togglePopover(false); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <div class="inline-flex relative"> | ||||
|     <Button | ||||
|       :label="buttonLabel" | ||||
|       sm | ||||
|       slate | ||||
|       class="flex-shrink-0" | ||||
|       @click="togglePopover(!showPopover)" | ||||
|     /> | ||||
|     <div | ||||
|       v-if="showPopover" | ||||
|       class="absolute w-[26.5rem] top-9 z-50 ltr:left-0 rtl:right-0 flex flex-col gap-5 bg-n-alpha-3 backdrop-blur-[100px] p-4 rounded-xl border border-n-weak shadow-md" | ||||
|     > | ||||
|       <InlineInput | ||||
|         v-model="modelValue" | ||||
|         :placeholder="placeholder" | ||||
|         @keyup.enter="onClickAdd" | ||||
|       /> | ||||
|       <div class="flex gap-2 justify-between"> | ||||
|         <Button | ||||
|           :label="cancelLabel" | ||||
|           sm | ||||
|           link | ||||
|           slate | ||||
|           class="h-10 hover:!no-underline" | ||||
|           @click="onClickCancel" | ||||
|         /> | ||||
|         <Button :label="confirmLabel" sm @click="onClickAdd" /> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @@ -0,0 +1,19 @@ | ||||
| <script setup> | ||||
| import AddNewRulesInput from './AddNewRulesInput.vue'; | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <Story | ||||
|     title="Captain/Assistant/AddNewRulesInput" | ||||
|     :layout="{ type: 'grid', width: '800px' }" | ||||
|   > | ||||
|     <Variant title="Default"> | ||||
|       <div class="px-6 py-4 bg-n-background"> | ||||
|         <AddNewRulesInput | ||||
|           placeholder="Type in another response guideline..." | ||||
|           label="Add and save (↵)" | ||||
|         /> | ||||
|       </div> | ||||
|     </Variant> | ||||
|   </Story> | ||||
| </template> | ||||
| @@ -0,0 +1,51 @@ | ||||
| <script setup> | ||||
| import Icon from 'dashboard/components-next/icon/Icon.vue'; | ||||
| import Button from 'dashboard/components-next/button/Button.vue'; | ||||
| import InlineInput from 'dashboard/components-next/inline-input/InlineInput.vue'; | ||||
|  | ||||
| defineProps({ | ||||
|   placeholder: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   label: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits(['add']); | ||||
|  | ||||
| const modelValue = defineModel({ | ||||
|   type: String, | ||||
|   default: '', | ||||
| }); | ||||
|  | ||||
| const onClickAdd = () => { | ||||
|   if (!modelValue.value?.trim()) return; | ||||
|   emit('add', modelValue.value.trim()); | ||||
|   modelValue.value = ''; | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <div | ||||
|     class="flex py-3 ltr:pl-3 h-16 rtl:pr-3 ltr:pr-4 rtl:pl-4 items-center gap-3 rounded-xl bg-n-solid-2 outline-1 outline outline-n-container" | ||||
|   > | ||||
|     <Icon icon="i-lucide-plus" class="text-n-slate-10 size-5 flex-shrink-0" /> | ||||
|  | ||||
|     <InlineInput | ||||
|       v-model="modelValue" | ||||
|       :placeholder="placeholder" | ||||
|       @keyup.enter="onClickAdd" | ||||
|     /> | ||||
|     <Button | ||||
|       :label="label" | ||||
|       ghost | ||||
|       xs | ||||
|       slate | ||||
|       class="!text-sm !text-n-slate-11 flex-shrink-0" | ||||
|       @click="onClickAdd" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
| @@ -0,0 +1,99 @@ | ||||
| <script setup> | ||||
| import { computed } from 'vue'; | ||||
| import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue'; | ||||
| import Button from 'dashboard/components-next/button/Button.vue'; | ||||
|  | ||||
| const props = defineProps({ | ||||
|   allItems: { | ||||
|     type: Array, | ||||
|     required: true, | ||||
|   }, | ||||
|   selectAllLabel: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   selectedCountLabel: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   deleteLabel: { | ||||
|     type: String, | ||||
|     default: 'Delete', | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits(['bulkDelete']); | ||||
|  | ||||
| const modelValue = defineModel({ | ||||
|   type: Set, | ||||
|   default: () => new Set(), | ||||
| }); | ||||
|  | ||||
| const selectedCount = computed(() => modelValue.value.size); | ||||
| const totalCount = computed(() => props.allItems.length); | ||||
|  | ||||
| const hasSelected = computed(() => selectedCount.value > 0); | ||||
| const isIndeterminate = computed( | ||||
|   () => hasSelected.value && selectedCount.value < totalCount.value | ||||
| ); | ||||
| const allSelected = computed( | ||||
|   () => totalCount.value > 0 && selectedCount.value === totalCount.value | ||||
| ); | ||||
|  | ||||
| const bulkCheckboxState = computed({ | ||||
|   get: () => allSelected.value, | ||||
|   set: shouldSelectAll => { | ||||
|     const newSelectedIds = shouldSelectAll | ||||
|       ? new Set(props.allItems.map(item => item.id)) | ||||
|       : new Set(); | ||||
|     modelValue.value = newSelectedIds; | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <transition | ||||
|     name="slide-fade" | ||||
|     enter-active-class="transition-all duration-300 ease-out" | ||||
|     enter-from-class="opacity-0 transform ltr:-translate-x-4 rtl:translate-x-4" | ||||
|     enter-to-class="opacity-100 transform translate-x-0" | ||||
|     leave-active-class="hidden opacity-0" | ||||
|   > | ||||
|     <div | ||||
|       v-if="hasSelected" | ||||
|       class="flex items-center gap-3 py-1 ltr:pl-3 rtl:pr-3 ltr:pr-4 rtl:pl-4 rounded-lg bg-n-solid-2 outline outline-1 outline-n-container shadow" | ||||
|     > | ||||
|       <div class="flex items-center gap-3"> | ||||
|         <div class="flex items-center gap-1.5"> | ||||
|           <Checkbox | ||||
|             v-model="bulkCheckboxState" | ||||
|             :indeterminate="isIndeterminate" | ||||
|           /> | ||||
|           <span class="text-sm font-medium text-n-slate-12 tabular-nums"> | ||||
|             {{ selectAllLabel }} | ||||
|           </span> | ||||
|         </div> | ||||
|         <span class="text-sm text-n-slate-10 tabular-nums"> | ||||
|           {{ selectedCountLabel }} | ||||
|         </span> | ||||
|       </div> | ||||
|       <div class="h-4 w-px bg-n-strong" /> | ||||
|       <div class="flex items-center gap-3"> | ||||
|         <slot name="actions" :selected-count="selectedCount"> | ||||
|           <Button | ||||
|             :label="deleteLabel" | ||||
|             sm | ||||
|             ruby | ||||
|             ghost | ||||
|             class="!px-1.5" | ||||
|             icon="i-lucide-trash" | ||||
|             @click="emit('bulkDelete')" | ||||
|           /> | ||||
|         </slot> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div v-else class="flex items-center gap-3"> | ||||
|       <slot name="default-actions" /> | ||||
|     </div> | ||||
|   </transition> | ||||
| </template> | ||||
| @@ -0,0 +1,37 @@ | ||||
| <script setup> | ||||
| import RuleCard from './RuleCard.vue'; | ||||
|  | ||||
| const sampleRules = [ | ||||
|   { id: 1, content: 'Block sensitive personal information', selectable: true }, | ||||
|   { id: 2, content: 'Reject offensive language', selectable: true }, | ||||
|   { id: 3, content: 'Deflect legal or medical advice', selectable: true }, | ||||
| ]; | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <Story | ||||
|     title="Captain/Assistant/RuleCard" | ||||
|     :layout="{ type: 'grid', width: '800px' }" | ||||
|   > | ||||
|     <Variant title="Selectable List"> | ||||
|       <div class="flex flex-col gap-4 px-20 py-4 bg-n-background"> | ||||
|         <RuleCard | ||||
|           v-for="rule in sampleRules" | ||||
|           :id="rule.id" | ||||
|           :key="rule.id" | ||||
|           :content="rule.content" | ||||
|           :selectable="rule.selectable" | ||||
|           @select="id => console.log('Selected rule', id)" | ||||
|           @edit="id => console.log('Edit', id)" | ||||
|           @delete="id => console.log('Delete', id)" | ||||
|         /> | ||||
|       </div> | ||||
|     </Variant> | ||||
|  | ||||
|     <Variant title="Non-Selectable"> | ||||
|       <div class="flex flex-col gap-4 px-20 py-4 bg-n-background"> | ||||
|         <RuleCard id="4" content="Replies should be friendly and clear." /> | ||||
|       </div> | ||||
|     </Variant> | ||||
|   </Story> | ||||
| </template> | ||||
| @@ -0,0 +1,94 @@ | ||||
| <script setup> | ||||
| import { computed, ref, watch } from 'vue'; | ||||
| import Button from 'dashboard/components-next/button/Button.vue'; | ||||
| import CardLayout from 'dashboard/components-next/CardLayout.vue'; | ||||
| import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue'; | ||||
| import InlineInput from 'dashboard/components-next/inline-input/InlineInput.vue'; | ||||
|  | ||||
| const props = defineProps({ | ||||
|   id: { | ||||
|     type: Number, | ||||
|     required: true, | ||||
|   }, | ||||
|   content: { | ||||
|     type: String, | ||||
|     required: true, | ||||
|   }, | ||||
|   selectable: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
|   isSelected: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits(['select', 'hover', 'edit', 'delete']); | ||||
|  | ||||
| const modelValue = computed({ | ||||
|   get: () => props.isSelected, | ||||
|   set: () => emit('select', props.id), | ||||
| }); | ||||
|  | ||||
| const isEditing = ref(false); | ||||
| const editedContent = ref(props.content); | ||||
|  | ||||
| // Local content to display to avoid flicker until parent prop updates on inline edit | ||||
| const localContent = ref(props.content); | ||||
|  | ||||
| // Keeps localContent in sync when parent updates content prop | ||||
| watch( | ||||
|   () => props.content, | ||||
|   newVal => { | ||||
|     localContent.value = newVal; | ||||
|   } | ||||
| ); | ||||
|  | ||||
| const startEdit = () => { | ||||
|   isEditing.value = true; | ||||
|   editedContent.value = props.content; | ||||
| }; | ||||
|  | ||||
| const saveEdit = () => { | ||||
|   isEditing.value = false; | ||||
|   // Update local content | ||||
|   localContent.value = editedContent.value; | ||||
|   emit('edit', { id: props.id, content: editedContent.value }); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <CardLayout | ||||
|     selectable | ||||
|     class="relative [&>div]:!py-5 [&>div]:ltr:!pr-4 [&>div]:rtl:!pl-4" | ||||
|     layout="row" | ||||
|     @mouseenter="emit('hover', true)" | ||||
|     @mouseleave="emit('hover', false)" | ||||
|   > | ||||
|     <div v-show="selectable" class="absolute top-6 ltr:left-3 rtl:right-3"> | ||||
|       <Checkbox v-model="modelValue" /> | ||||
|     </div> | ||||
|     <InlineInput | ||||
|       v-if="isEditing" | ||||
|       v-model="editedContent" | ||||
|       focus-on-mount | ||||
|       custom-input-class="flex items-center gap-2 text-sm text-n-slate-12" | ||||
|       @keyup.enter="saveEdit" | ||||
|     /> | ||||
|     <span v-else class="flex items-center gap-2 text-sm text-n-slate-12"> | ||||
|       {{ localContent }} | ||||
|     </span> | ||||
|     <div class="flex items-center gap-2"> | ||||
|       <Button icon="i-lucide-pen" slate xs ghost @click="startEdit" /> | ||||
|       <span class="w-px h-4 bg-n-weak" /> | ||||
|       <Button | ||||
|         icon="i-lucide-trash" | ||||
|         slate | ||||
|         xs | ||||
|         ghost | ||||
|         @click="emit('delete', id)" | ||||
|       /> | ||||
|     </div> | ||||
|   </CardLayout> | ||||
| </template> | ||||
| @@ -0,0 +1,46 @@ | ||||
| <script setup> | ||||
| import SuggestedRules from './SuggestedRules.vue'; | ||||
| import Button from 'dashboard/components-next/button/Button.vue'; | ||||
|  | ||||
| const guidelinesExample = [ | ||||
|   { | ||||
|     content: | ||||
|       'Block queries that share or request sensitive personal information (e.g. phone numbers, passwords).', | ||||
|   }, | ||||
|   { | ||||
|     content: | ||||
|       'Reject queries that include offensive, discriminatory, or threatening language.', | ||||
|   }, | ||||
|   { | ||||
|     content: | ||||
|       'Deflect when the assistant is asked for legal or medical diagnosis or treatment.', | ||||
|   }, | ||||
| ]; | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <Story | ||||
|     title="Captain/Assistant/SuggestedRules" | ||||
|     :layout="{ type: 'grid', width: '800px' }" | ||||
|   > | ||||
|     <Variant title="Suggested Rules List"> | ||||
|       <div class="px-20 py-4 bg-n-background"> | ||||
|         <SuggestedRules | ||||
|           title="Example response guidelines" | ||||
|           :items="guidelinesExample" | ||||
|         > | ||||
|           <template #default="{ item }"> | ||||
|             <span class="text-sm text-n-slate-12">{{ item.content }}</span> | ||||
|             <Button | ||||
|               label="Add this" | ||||
|               ghost | ||||
|               xs | ||||
|               slate | ||||
|               class="!text-sm !text-n-slate-11 flex-shrink-0" | ||||
|             /> | ||||
|           </template> | ||||
|         </SuggestedRules> | ||||
|       </div> | ||||
|     </Variant> | ||||
|   </Story> | ||||
| </template> | ||||
| @@ -0,0 +1,63 @@ | ||||
| <script setup> | ||||
| import { useI18n } from 'vue-i18n'; | ||||
| import Button from 'dashboard/components-next/button/Button.vue'; | ||||
|  | ||||
| defineProps({ | ||||
|   title: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   items: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits(['add', 'close']); | ||||
|  | ||||
| const { t } = useI18n(); | ||||
|  | ||||
| const onAddClick = () => { | ||||
|   emit('add'); | ||||
| }; | ||||
|  | ||||
| const onClickClose = () => { | ||||
|   emit('close'); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <div | ||||
|     class="flex flex-col items-start self-stretch rounded-xl w-full overflow-hidden border border-dashed border-n-strong" | ||||
|   > | ||||
|     <div class="flex items-center justify-between w-full gap-3 px-4 pb-1 pt-4"> | ||||
|       <div class="flex items-center gap-3"> | ||||
|         <h5 class="text-sm font-medium text-n-slate-11">{{ title }}</h5> | ||||
|         <span class="h-3 w-px bg-n-weak" /> | ||||
|         <Button | ||||
|           :label="t('CAPTAIN.ASSISTANTS.GUARDRAILS.ADD.SUGGESTED.ADD')" | ||||
|           ghost | ||||
|           xs | ||||
|           slate | ||||
|           class="!text-sm !text-n-slate-11 flex-shrink-0" | ||||
|           @click="onAddClick" | ||||
|         /> | ||||
|       </div> | ||||
|       <Button | ||||
|         ghost | ||||
|         xs | ||||
|         slate | ||||
|         icon="i-lucide-x" | ||||
|         class="!text-sm !text-n-slate-11 flex-shrink-0" | ||||
|         @click="onClickClose" | ||||
|       /> | ||||
|     </div> | ||||
|     <div | ||||
|       class="flex flex-col items-start divide-y divide-n-strong divide-dashed w-full" | ||||
|     > | ||||
|       <div v-for="item in items" :key="item.content" class="w-full px-4 py-4"> | ||||
|         <slot :item="item" /> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @@ -331,6 +331,11 @@ const menuItems = computed(() => { | ||||
|           label: t('SIDEBAR.SMS'), | ||||
|           to: accountScopedRoute('campaigns_sms_index'), | ||||
|         }, | ||||
|         { | ||||
|           name: 'WhatsApp', | ||||
|           label: t('SIDEBAR.WHATSAPP'), | ||||
|           to: accountScopedRoute('campaigns_whatsapp_index'), | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ export const FEATURE_FLAGS = { | ||||
|   AUTO_RESOLVE_CONVERSATIONS: 'auto_resolve_conversations', | ||||
|   AUTOMATIONS: 'automations', | ||||
|   CAMPAIGNS: 'campaigns', | ||||
|   WHATSAPP_CAMPAIGNS: 'whatsapp_campaign', | ||||
|   CANNED_RESPONSES: 'canned_responses', | ||||
|   CRM: 'crm', | ||||
|   CUSTOM_ATTRIBUTES: 'custom_attributes', | ||||
| @@ -36,6 +37,7 @@ export const FEATURE_FLAGS = { | ||||
|   REPORT_V4: 'report_v4', | ||||
|   CHANNEL_INSTAGRAM: 'channel_instagram', | ||||
|   CONTACT_CHATWOOT_SUPPORT_TEAM: 'contact_chatwoot_support_team', | ||||
|   WHATSAPP_EMBEDDED_SIGNUP: 'whatsapp_embedded_signup', | ||||
|   CAPTAIN_V2: 'captain_integration_v2', | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Fetching custom attributes", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Conversation", | ||||
|       "CONTACT": "Contact" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Text", | ||||
|       "NUMBER": "Number", | ||||
|       "LINK": "Link", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "List", | ||||
|       "CHECKBOX": "Checkbox" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Add Custom Attribute", | ||||
|       "SUBMIT": "Create", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Open conversation" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "None", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Press enter to select", | ||||
|       "ENTER_TO_REMOVE": "Press enter to remove", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Select one", | ||||
|       "SELECT": "Select" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Change Priority", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "None", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "جلب الصفات المخصصة", | ||||
|     "DESCRIPTION": "سمة مخصصة تتتبع تفاصيل إضافية حول جهات الاتصال أو المحادثات الخاصة بك - مثل خطة الاشتراك أو تاريخ الشراء الأول. يمكنك إضافة أنواع مختلفة من السمات المخصصة، مثل النص أو القوائم أو الأرقام، لالتقاط المعلومات المحددة التي تحتاجها.", | ||||
|     "LEARN_MORE": "تعرف على المزيد حول السمات المخصصة", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "المحادثات", | ||||
|       "CONTACT": "جهات الاتصال" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "النص", | ||||
|       "NUMBER": "العدد", | ||||
|       "LINK": "الرابط", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "القائمة", | ||||
|       "CHECKBOX": "خانة الاختيار" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "إضافة صفة خاصة", | ||||
|       "SUBMIT": "إنشاء", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "فتح المحادثة" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "لا شيء", | ||||
|       "LOW": "منخفضة", | ||||
|       "MEDIUM": "متوسطة", | ||||
|       "HIGH": "عالية", | ||||
|       "URGENT": "عاجل" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "اضغط على زر الإدخال للاختيار", | ||||
|       "ENTER_TO_REMOVE": "اضغط على زر الإدخال للحذف", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "اختر واحدا", | ||||
|       "SELECT": "اختر" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "تغيير الأولوية", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "لا شيء", | ||||
|       "LOW": "منخفضة", | ||||
|       "MEDIUM": "متوسطة", | ||||
|       "HIGH": "عالية", | ||||
|       "URGENT": "عاجل" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Fetching custom attributes", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Conversation", | ||||
|       "CONTACT": "Contact" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Text", | ||||
|       "NUMBER": "Number", | ||||
|       "LINK": "Link", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "List", | ||||
|       "CHECKBOX": "Checkbox" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Add Custom Attribute", | ||||
|       "SUBMIT": "Create", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Open conversation" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "None", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Press enter to select", | ||||
|       "ENTER_TO_REMOVE": "Press enter to remove", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Select one", | ||||
|       "SELECT": "Select" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Change Priority", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "None", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Fetching custom attributes", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Разговор", | ||||
|       "CONTACT": "Contact" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Text", | ||||
|       "NUMBER": "Number", | ||||
|       "LINK": "Link", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "List", | ||||
|       "CHECKBOX": "Checkbox" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Add Custom Attribute", | ||||
|       "SUBMIT": "Създаване", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Open conversation" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "None", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Press enter to select", | ||||
|       "ENTER_TO_REMOVE": "Press enter to remove", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Select one", | ||||
|       "SELECT": "Select" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Change Priority", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "None", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "S'estan recollint atributs personalitzats", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Conversa", | ||||
|       "CONTACT": "Contacte" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Llista", | ||||
|       "NUMBER": "Número", | ||||
|       "LINK": "Enllaç", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "Llista", | ||||
|       "CHECKBOX": "Casella de selecció" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Afegir atribut personalitzat", | ||||
|       "SUBMIT": "Crear", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Afegeix SLA", | ||||
|       "OPEN_CONVERSATION": "Obrir conversa" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Ningú", | ||||
|       "LOW": "Baixa", | ||||
|       "MEDIUM": "Mitjana", | ||||
|       "HIGH": "Alta", | ||||
|       "URGENT": "Urgent" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Presiona retorn (tecla enter) per seleccionar", | ||||
|       "ENTER_TO_REMOVE": "Presiona retorn (tecla enter) per eliminar", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Selecciona un", | ||||
|       "SELECT": "Selecciona" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Canvia la prioritat", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Ningú", | ||||
|       "LOW": "Baixa", | ||||
|       "MEDIUM": "Mitjana", | ||||
|       "HIGH": "Alta", | ||||
|       "URGENT": "Urgent" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Fetching custom attributes", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Conversation", | ||||
|       "CONTACT": "Contact" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Text", | ||||
|       "NUMBER": "Number", | ||||
|       "LINK": "Link", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "List", | ||||
|       "CHECKBOX": "Checkbox" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Add Custom Attribute", | ||||
|       "SUBMIT": "Create", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Open conversation" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Nic", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Typ zprávy", | ||||
|       "MESSAGE_CONTAINS": "Zpráva obsahuje", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Stiskněte Enter pro vybrání", | ||||
|       "ENTER_TO_REMOVE": "Stiskněte Enter pro odebrání", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Vyberte jeden", | ||||
|       "SELECT": "Select" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Change Priority", | ||||
|       "ADD_PRIVATE_NOTE": "Přidat soukromou poznámku", | ||||
|       "SEND_WEBHOOK_EVENT": "Poslat událost webhook" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Nic", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Henter brugerdefinerede attributter", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Samtale", | ||||
|       "CONTACT": "Kontakt" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Tekst", | ||||
|       "NUMBER": "Nummer", | ||||
|       "LINK": "Link", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "Liste", | ||||
|       "CHECKBOX": "Afkrydsningsfelt" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Tilføj Tilpasset Attribut", | ||||
|       "SUBMIT": "Opret", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Åbn samtale" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Ingen", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Tryk enter for at vælge", | ||||
|       "ENTER_TO_REMOVE": "Tryk enter for at fjerne", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Vælg en", | ||||
|       "SELECT": "Vælg" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Change Priority", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Ingen", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Benutzerdefinierte Attribute abrufen", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Konversation", | ||||
|       "CONTACT": "Kontakt" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Text", | ||||
|       "NUMBER": "Nummer", | ||||
|       "LINK": "Link", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "Liste", | ||||
|       "CHECKBOX": "Checkbox" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Benutzerdefiniertes Attribut hinzufügen", | ||||
|       "SUBMIT": "Erstellen", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Unterhaltung öffnen" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Keine", | ||||
|       "LOW": "Niedrig", | ||||
|       "MEDIUM": "Mittel", | ||||
|       "HIGH": "Hoch", | ||||
|       "URGENT": "Dringend" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Drücken Sie zur Auswahl die Eingabetaste", | ||||
|       "ENTER_TO_REMOVE": "Drücken Sie zum Entfernen die Eingabetaste", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Eines wählen", | ||||
|       "SELECT": "Auswählen" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Priorität ändern", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Keine", | ||||
|       "LOW": "Niedrig", | ||||
|       "MEDIUM": "Mittel", | ||||
|       "HIGH": "Hoch", | ||||
|       "URGENT": "Dringend" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Λήψη προσαρμοσμένων ιδιοτήτων", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Συνομιλία", | ||||
|       "CONTACT": "Επαφές" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Κείμενο", | ||||
|       "NUMBER": "Αριθμός", | ||||
|       "LINK": "Σύνδεσμος", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "Λίστα", | ||||
|       "CHECKBOX": "Checkbox" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Προσθήκη προσαρμοσμένης ιδιότητας", | ||||
|       "SUBMIT": "Δημιουργία", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Άνοιγμα συνομιλίας" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Κανένα", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Πάτησε enter για επιλογή", | ||||
|       "ENTER_TO_REMOVE": "Πάτησε enter για αφαίρεση", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Επιλέξτε ένα", | ||||
|       "SELECT": "Επιλογή" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Change Priority", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Κανένα", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -146,6 +146,7 @@ | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event", | ||||
|       "SEND_ATTACHMENT": "Send Attachment", | ||||
|       "SEND_MESSAGE": "Send a Message", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "CHANGE_PRIORITY": "Change Priority", | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Open conversation" | ||||
|   | ||||
| @@ -137,6 +137,70 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "WHATSAPP": { | ||||
|       "HEADER_TITLE": "WhatsApp campaigns", | ||||
|       "NEW_CAMPAIGN": "Create campaign", | ||||
|       "EMPTY_STATE": { | ||||
|         "TITLE": "No WhatsApp campaigns are available", | ||||
|         "SUBTITLE": "Launch a WhatsApp campaign to reach your customers directly. Send offers or make announcements with ease. Click 'Create campaign' to get started." | ||||
|       }, | ||||
|       "CARD": { | ||||
|         "STATUS": { | ||||
|           "COMPLETED": "Completed", | ||||
|           "SCHEDULED": "Scheduled" | ||||
|         }, | ||||
|         "CAMPAIGN_DETAILS": { | ||||
|           "SENT_FROM": "Sent from", | ||||
|           "ON": "on" | ||||
|         } | ||||
|       }, | ||||
|       "CREATE": { | ||||
|         "TITLE": "Create WhatsApp campaign", | ||||
|         "CANCEL_BUTTON_TEXT": "Cancel", | ||||
|         "CREATE_BUTTON_TEXT": "Create", | ||||
|         "FORM": { | ||||
|           "TITLE": { | ||||
|             "LABEL": "Title", | ||||
|             "PLACEHOLDER": "Please enter the title of campaign", | ||||
|             "ERROR": "Title is required" | ||||
|           }, | ||||
|           "INBOX": { | ||||
|             "LABEL": "Select Inbox", | ||||
|             "PLACEHOLDER": "Select Inbox", | ||||
|             "ERROR": "Inbox is required" | ||||
|           }, | ||||
|           "TEMPLATE": { | ||||
|             "LABEL": "WhatsApp Template", | ||||
|             "PLACEHOLDER": "Select a template", | ||||
|             "INFO": "Select a template to use for this campaign.", | ||||
|             "ERROR": "Template is required", | ||||
|             "PREVIEW_TITLE": "Process {templateName}", | ||||
|             "LANGUAGE": "Language", | ||||
|             "CATEGORY": "Category", | ||||
|             "VARIABLES_LABEL": "Variables", | ||||
|             "VARIABLE_PLACEHOLDER": "Enter value for {variable}" | ||||
|           }, | ||||
|           "AUDIENCE": { | ||||
|             "LABEL": "Audience", | ||||
|             "PLACEHOLDER": "Select the customer labels", | ||||
|             "ERROR": "Audience is required" | ||||
|           }, | ||||
|           "SCHEDULED_AT": { | ||||
|             "LABEL": "Scheduled time", | ||||
|             "PLACEHOLDER": "Please select the time", | ||||
|             "ERROR": "Scheduled time is required" | ||||
|           }, | ||||
|           "BUTTONS": { | ||||
|             "CREATE": "Create", | ||||
|             "CANCEL": "Cancel" | ||||
|           }, | ||||
|           "API": { | ||||
|             "SUCCESS_MESSAGE": "WhatsApp campaign created successfully", | ||||
|             "ERROR_MESSAGE": "There was an error. Please try again." | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "CONFIRM_DELETE": { | ||||
|       "TITLE": "Are you sure to delete?", | ||||
|       "DESCRIPTION": "The delete action is permanent and cannot be reversed.", | ||||
|   | ||||
| @@ -222,10 +222,17 @@ | ||||
|         "DESC": "Start supporting your customers via WhatsApp.", | ||||
|         "PROVIDERS": { | ||||
|           "LABEL": "API Provider", | ||||
|           "WHATSAPP_EMBEDDED": "WhatsApp Business", | ||||
|           "TWILIO": "Twilio", | ||||
|           "WHATSAPP_CLOUD": "WhatsApp Cloud", | ||||
|           "WHATSAPP_CLOUD_DESC": "Quick setup through Meta", | ||||
|           "TWILIO_DESC": "Connect via Twilio credentials", | ||||
|           "360_DIALOG": "360Dialog" | ||||
|         }, | ||||
|         "SELECT_PROVIDER": { | ||||
|           "TITLE": "Select your API provider", | ||||
|           "DESCRIPTION": "Choose your WhatsApp provider. You can connect directly through Meta which requires no setup, or connect through Twilio using your account credentials." | ||||
|         }, | ||||
|         "INBOX_NAME": { | ||||
|           "LABEL": "Inbox Name", | ||||
|           "PLACEHOLDER": "Please enter an inbox name", | ||||
| @@ -264,6 +271,28 @@ | ||||
|           "WEBHOOK_VERIFICATION_TOKEN": "Webhook Verification Token" | ||||
|         }, | ||||
|         "SUBMIT_BUTTON": "Create WhatsApp Channel", | ||||
|         "EMBEDDED_SIGNUP": { | ||||
|           "TITLE": "Quick Setup with Meta", | ||||
|           "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.", | ||||
|           "BENEFITS": { | ||||
|             "TITLE": "Benefits of Embedded Signup:", | ||||
|             "EASY_SETUP": "No manual configuration required", | ||||
|             "SECURE_AUTH": "Secure OAuth based authentication", | ||||
|             "AUTO_CONFIG": "Automatic webhook and phone number configuration" | ||||
|           }, | ||||
|           "SUBMIT_BUTTON": "Connect with WhatsApp Business", | ||||
|           "AUTH_PROCESSING": "Authenticating with Meta", | ||||
|           "WAITING_FOR_BUSINESS_INFO": "Please complete business setup in the Meta window...", | ||||
|           "PROCESSING": "Setting up your WhatsApp Business Account", | ||||
|           "LOADING_SDK": "Loading Facebook SDK...", | ||||
|           "CANCELLED": "WhatsApp Signup was cancelled", | ||||
|           "SUCCESS_TITLE": "WhatsApp Business Account Connected!", | ||||
|           "WAITING_FOR_AUTH": "Waiting for authentication...", | ||||
|           "INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.", | ||||
|           "SIGNUP_ERROR": "Signup error occurred", | ||||
|           "AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.", | ||||
|           "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured" | ||||
|         }, | ||||
|         "API": { | ||||
|           "ERROR_MESSAGE": "We were not able to save the WhatsApp channel" | ||||
|         } | ||||
|   | ||||
| @@ -521,6 +521,100 @@ | ||||
|           "TITLE": "Captain Assistant", | ||||
|           "NOTE": "Captain Assistant engages directly with customers, learns from your help docs and past conversations, and delivers instant, accurate responses. It handles the initial queries, providing quick resolutions before  transferring to an agent when needed." | ||||
|         } | ||||
|       }, | ||||
|       "GUARDRAILS": { | ||||
|         "TITLE": "Guardrails", | ||||
|         "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic.", | ||||
|         "BREADCRUMB": { | ||||
|           "TITLE": "Guardrails" | ||||
|         }, | ||||
|         "BULK_ACTION": { | ||||
|           "SELECTED": "{count} item selected | {count} items selected", | ||||
|           "SELECT_ALL": "Select all ({count})", | ||||
|           "UNSELECT_ALL": "Unselect all ({count})", | ||||
|           "BULK_DELETE_BUTTON": "Delete" | ||||
|         }, | ||||
|         "ADD": { | ||||
|           "SUGGESTED": { | ||||
|             "TITLE": "Example guardrails", | ||||
|             "ADD": "Add all", | ||||
|             "ADD_SINGLE": "Add this", | ||||
|             "SAVE": "Add and save (↵)", | ||||
|             "PLACEHOLDER": "Type in another guardrail..." | ||||
|           }, | ||||
|           "NEW": { | ||||
|             "TITLE": "Add a guardrail", | ||||
|             "CREATE": "Create", | ||||
|             "CANCEL": "Cancel", | ||||
|             "PLACEHOLDER": "Type in another guardrail...", | ||||
|             "TEST_ALL": "Test all" | ||||
|           } | ||||
|         }, | ||||
|         "LIST": { | ||||
|           "SEARCH_PLACEHOLDER": "Search..." | ||||
|         }, | ||||
|         "EMPTY_MESSAGE": "No guardrails found. Create or add examples to begin.", | ||||
|         "API": { | ||||
|           "ADD": { | ||||
|             "SUCCESS": "Guardrails added successfully", | ||||
|             "ERROR": "There was an error adding guardrails, please try again." | ||||
|           }, | ||||
|           "UPDATE": { | ||||
|             "SUCCESS": "Guardrails updated successfully", | ||||
|             "ERROR": "There was an error updating guardrails, please try again." | ||||
|           }, | ||||
|           "DELETE": { | ||||
|             "SUCCESS": "Guardrails deleted successfully", | ||||
|             "ERROR": "There was an error deleting guardrails, please try again." | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "RESPONSE_GUIDELINES": { | ||||
|         "TITLE": "Response Guidelines", | ||||
|         "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?", | ||||
|         "BREADCRUMB": { | ||||
|           "TITLE": "Response Guidelines" | ||||
|         }, | ||||
|         "BULK_ACTION": { | ||||
|           "SELECTED": "{count} item selected | {count} items selected", | ||||
|           "SELECT_ALL": "Select all ({count})", | ||||
|           "UNSELECT_ALL": "Unselect all ({count})", | ||||
|           "BULK_DELETE_BUTTON": "Delete" | ||||
|         }, | ||||
|         "ADD": { | ||||
|           "SUGGESTED": { | ||||
|             "TITLE": "Example response guidelines", | ||||
|             "ADD": "Add all", | ||||
|             "ADD_SINGLE": "Add this", | ||||
|             "SAVE": "Add and save (↵)", | ||||
|             "PLACEHOLDER": "Type in another response guideline..." | ||||
|           }, | ||||
|           "NEW": { | ||||
|             "TITLE": "Add a response guideline", | ||||
|             "CREATE": "Create", | ||||
|             "CANCEL": "Cancel", | ||||
|             "PLACEHOLDER": "Type in another response guideline...", | ||||
|             "TEST_ALL": "Test all" | ||||
|           } | ||||
|         }, | ||||
|         "LIST": { | ||||
|           "SEARCH_PLACEHOLDER": "Search..." | ||||
|         }, | ||||
|         "EMPTY_MESSAGE": "No response guidelines found. Create or add examples to begin.", | ||||
|         "API": { | ||||
|           "ADD": { | ||||
|             "SUCCESS": "Response Guidelines added successfully", | ||||
|             "ERROR": "There was an error adding response guidelines, please try again." | ||||
|           }, | ||||
|           "UPDATE": { | ||||
|             "SUCCESS": "Response Guidelines updated successfully", | ||||
|             "ERROR": "There was an error updating response guidelines, please try again." | ||||
|           }, | ||||
|           "DELETE": { | ||||
|             "SUCCESS": "Response Guidelines deleted successfully", | ||||
|             "ERROR": "There was an error deleting response guidelines, please try again." | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "DOCUMENTS": { | ||||
|   | ||||
| @@ -319,6 +319,7 @@ | ||||
|     "CSAT": "CSAT", | ||||
|     "LIVE_CHAT": "Live Chat", | ||||
|     "SMS": "SMS", | ||||
|     "WHATSAPP": "WhatsApp", | ||||
|     "CAMPAIGNS": "Campaigns", | ||||
|     "ONGOING": "Ongoing", | ||||
|     "ONE_OFF": "One off", | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Obtener atributos personalizados", | ||||
|     "DESCRIPTION": "Un atributo personalizado rastrea detalles adicionales sobre tus contactos o conversaciones, como el plan de suscripción o la fecha de su primera compra. Puede agregar diferentes tipos de atributos personalizados, como textos, listas o números, para capturar la información específica que necesita.", | ||||
|     "LEARN_MORE": "Aprende más sobre los atributos personalizados", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Conversación", | ||||
|       "CONTACT": "Contacto" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Texto", | ||||
|       "NUMBER": "Número", | ||||
|       "LINK": "Enlace", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "Lista", | ||||
|       "CHECKBOX": "Casilla" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Añadir atributo personalizado", | ||||
|       "SUBMIT": "Crear", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Añadir SLA", | ||||
|       "OPEN_CONVERSATION": "Abrir conversación" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Ninguna", | ||||
|       "LOW": "Baja", | ||||
|       "MEDIUM": "Media", | ||||
|       "HIGH": "Alta", | ||||
|       "URGENT": "Urgente" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Tipo de mensaje", | ||||
|       "MESSAGE_CONTAINS": "El mensaje contiene", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Pulse Enter para seleccionar", | ||||
|       "ENTER_TO_REMOVE": "Presione Enter para eliminar", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Seleccione uno", | ||||
|       "SELECT": "Seleccionar" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Cambiar prioridad", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Enviar Evento de Webhook" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "Ninguna", | ||||
|       "LOW": "Baja", | ||||
|       "MEDIUM": "Media", | ||||
|       "HIGH": "Alta", | ||||
|       "URGENT": "Urgente" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "واکشی ویژگیهای سفارشی", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "گفتگو", | ||||
|       "CONTACT": "مخاطب" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "متن", | ||||
|       "NUMBER": "شماره", | ||||
|       "LINK": "پیوند", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "فهرست", | ||||
|       "CHECKBOX": "چک باکس" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "اضافه کردن ویژگی سفارشی", | ||||
|       "SUBMIT": "ايجاد كردن", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "اضافه کردن SLA", | ||||
|       "OPEN_CONVERSATION": "باز کردن گفتگو" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "هیچکدام", | ||||
|       "LOW": "پایین", | ||||
|       "MEDIUM": "متوسط", | ||||
|       "HIGH": "بالا", | ||||
|       "URGENT": "فوری" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "برای انتخاب Enter را فشار دهید", | ||||
|       "ENTER_TO_REMOVE": "برای حذف دکمه enter را فشار دهید", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "یکی را انتخاب کن", | ||||
|       "SELECT": "انتخاب کنید" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "تغییر اولویت", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "هیچکدام", | ||||
|       "LOW": "پایین", | ||||
|       "MEDIUM": "متوسط", | ||||
|       "HIGH": "بالا", | ||||
|       "URGENT": "فوری" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Fetching custom attributes", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Conversation", | ||||
|       "CONTACT": "Contact" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Text", | ||||
|       "NUMBER": "Number", | ||||
|       "LINK": "Link", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "List", | ||||
|       "CHECKBOX": "Checkbox" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Add Custom Attribute", | ||||
|       "SUBMIT": "Luo", | ||||
|   | ||||
| @@ -150,6 +150,17 @@ | ||||
|       "ADD_SLA": "Add SLA", | ||||
|       "OPEN_CONVERSATION": "Open conversation" | ||||
|     }, | ||||
|     "MESSAGE_TYPES": { | ||||
|       "INCOMING": "Incoming Message", | ||||
|       "OUTGOING": "Outgoing Message" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "None", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     }, | ||||
|     "ATTRIBUTES": { | ||||
|       "MESSAGE_TYPE": "Message Type", | ||||
|       "MESSAGE_CONTAINS": "Message Contains", | ||||
|   | ||||
| @@ -134,6 +134,7 @@ | ||||
|     "MULTISELECT": { | ||||
|       "ENTER_TO_SELECT": "Paina enter valitaksesi", | ||||
|       "ENTER_TO_REMOVE": "Paina enter poistaaksesi", | ||||
|       "NO_OPTIONS": "List is empty", | ||||
|       "SELECT_ONE": "Valitse yksi", | ||||
|       "SELECT": "Select" | ||||
|     } | ||||
|   | ||||
| @@ -481,6 +481,37 @@ | ||||
|         "ERROR_MESSAGE": "There was an error updating the assistant, please try again.", | ||||
|         "NOT_FOUND": "Could not find the assistant. Please try again." | ||||
|       }, | ||||
|       "SETTINGS": { | ||||
|         "BREADCRUMB": { | ||||
|           "ASSISTANT": "Assistant" | ||||
|         }, | ||||
|         "BASIC_SETTINGS": { | ||||
|           "TITLE": "Basic settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "SYSTEM_SETTINGS": { | ||||
|           "TITLE": "System settings", | ||||
|           "DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human." | ||||
|         }, | ||||
|         "CONTROL_ITEMS": { | ||||
|           "TITLE": "The Fun Stuff", | ||||
|           "DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.", | ||||
|           "OPTIONS": { | ||||
|             "GUARDRAILS": { | ||||
|               "TITLE": "Guardrails", | ||||
|               "DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic." | ||||
|             }, | ||||
|             "SCENARIOS": { | ||||
|               "TITLE": "Scenarios", | ||||
|               "DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”" | ||||
|             }, | ||||
|             "RESPONSE_GUIDELINES": { | ||||
|               "TITLE": "Response guidelines", | ||||
|               "DESCRIPTION": "The vibe and structure of your assistant’s replies—clear and friendly? Short and snappy? Detailed and formal?" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "OPTIONS": { | ||||
|         "EDIT_ASSISTANT": "Edit Assistant", | ||||
|         "DELETE_ASSISTANT": "Delete Assistant", | ||||
|   | ||||
| @@ -99,6 +99,13 @@ | ||||
|       "CHANGE_PRIORITY": "Change Priority", | ||||
|       "ADD_PRIVATE_NOTE": "Add a Private Note", | ||||
|       "SEND_WEBHOOK_EVENT": "Send Webhook Event" | ||||
|     }, | ||||
|     "PRIORITY_TYPES": { | ||||
|       "NONE": "None", | ||||
|       "LOW": "Low", | ||||
|       "MEDIUM": "Medium", | ||||
|       "HIGH": "High", | ||||
|       "URGENT": "Urgent" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,18 @@ | ||||
|     "LOADING": "Récupération des attributs personnalisés", | ||||
|     "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", | ||||
|     "LEARN_MORE": "Learn more about custom attributes", | ||||
|     "ATTRIBUTE_MODELS": { | ||||
|       "CONVERSATION": "Conversation", | ||||
|       "CONTACT": "Contact" | ||||
|     }, | ||||
|     "ATTRIBUTE_TYPES": { | ||||
|       "TEXT": "Texte", | ||||
|       "NUMBER": "Nombre", | ||||
|       "LINK": "Lien", | ||||
|       "DATE": "Date", | ||||
|       "LIST": "Liste", | ||||
|       "CHECKBOX": "Case à cocher" | ||||
|     }, | ||||
|     "ADD": { | ||||
|       "TITLE": "Ajouter un attribut personnalisé", | ||||
|       "SUBMIT": "Créer", | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Sojan Jose
					Sojan Jose