mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-29 10:12:34 +00:00 
			
		
		
		
	Merge branch 'release/2.13.0'
This commit is contained in:
		
							
								
								
									
										13
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								.env.example
									
									
									
									
									
								
							| @@ -35,7 +35,7 @@ REDIS_SENTINELS= | ||||
| REDIS_SENTINEL_MASTER_NAME= | ||||
|  | ||||
| # By default Chatwoot will pass REDIS_PASSWORD as the password value for sentinels | ||||
| # Use the following environment variable to customize passwords for sentinels.  | ||||
| # Use the following environment variable to customize passwords for sentinels. | ||||
| # Use empty string if sentinels are configured with out passwords | ||||
| # REDIS_SENTINEL_PASSWORD= | ||||
|  | ||||
| @@ -45,7 +45,7 @@ REDIS_SENTINEL_MASTER_NAME= | ||||
| # REDIS_OPENSSL_VERIFY_MODE=none | ||||
|  | ||||
| # Postgres Database config variables | ||||
| # You can leave POSTGRES_DATABASE blank. The default name of  | ||||
| # You can leave POSTGRES_DATABASE blank. The default name of | ||||
| # the database in the production environment is chatwoot_production | ||||
| # POSTGRES_DATABASE= | ||||
| POSTGRES_HOST=postgres | ||||
| @@ -213,3 +213,12 @@ STRIPE_WEBHOOK_SECRET= | ||||
| # Set to true if you want to upload files to cloud storage using the signed url | ||||
| # Make sure to follow https://edgeguides.rubyonrails.org/active_storage_overview.html#cross-origin-resource-sharing-cors-configuration on the cloud storage after setting this to true. | ||||
| DIRECT_UPLOADS_ENABLED= | ||||
|  | ||||
| #MS OAUTH creds | ||||
| AZURE_APP_ID= | ||||
| AZURE_APP_SECRET= | ||||
|  | ||||
| ## Advanced configurations | ||||
| ## Change these values to fine tune performance | ||||
| # control the concurrency setting of sidekiq | ||||
| # SIDEKIQ_CONCURRENCY=10 | ||||
|   | ||||
							
								
								
									
										6
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -37,6 +37,8 @@ gem 'json_schemer' | ||||
| gem 'rack-attack' | ||||
| # a utility tool for streaming, flexible and safe downloading of remote files | ||||
| gem 'down', '~> 5.0' | ||||
| # authentication type to fetch and send mail over oauth2.0 | ||||
| gem 'gmail_xoauth' | ||||
|  | ||||
| ##-- for active storage --## | ||||
| gem 'aws-sdk-s3', require: false | ||||
| @@ -160,6 +162,8 @@ group :test do | ||||
|   gem 'database_cleaner' | ||||
|   # mock http calls | ||||
|   gem 'webmock' | ||||
|   # test profiling | ||||
|   gem 'test-prof' | ||||
| end | ||||
|  | ||||
| group :development, :test do | ||||
| @@ -186,3 +190,5 @@ group :development, :test do | ||||
|   gem 'spring' | ||||
|   gem 'spring-watcher-listen' | ||||
| end | ||||
| # worked with microsoft refresh token | ||||
| gem 'omniauth-oauth2' | ||||
|   | ||||
							
								
								
									
										30
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								Gemfile.lock
									
									
									
									
									
								
							| @@ -249,6 +249,8 @@ GEM | ||||
|     gli (2.21.0) | ||||
|     globalid (1.0.0) | ||||
|       activesupport (>= 5.0) | ||||
|     gmail_xoauth (0.4.2) | ||||
|       oauth (>= 0.3.6) | ||||
|     google-apis-core (0.7.0) | ||||
|       addressable (~> 2.5, >= 2.5.1) | ||||
|       googleauth (>= 0.16.2, < 2.a) | ||||
| @@ -333,8 +335,8 @@ GEM | ||||
|     http-cookie (1.0.5) | ||||
|       domain_name (~> 0.5) | ||||
|     http-form_data (2.3.0) | ||||
|     httparty (0.20.0) | ||||
|       mime-types (~> 3.0) | ||||
|     httparty (0.21.0) | ||||
|       mini_mime (>= 1.0.0) | ||||
|       multi_xml (>= 0.5.2) | ||||
|     httpclient (2.8.3) | ||||
|     i18n (1.11.0) | ||||
| @@ -437,6 +439,20 @@ GEM | ||||
|     nokogiri (1.13.10-x86_64-linux) | ||||
|       racc (~> 1.4) | ||||
|     oauth (0.5.10) | ||||
|     oauth2 (2.0.9) | ||||
|       faraday (>= 0.17.3, < 3.0) | ||||
|       jwt (>= 1.0, < 3.0) | ||||
|       multi_xml (~> 0.5) | ||||
|       rack (>= 1.2, < 4) | ||||
|       snaky_hash (~> 2.0) | ||||
|       version_gem (~> 1.1) | ||||
|     omniauth (2.1.0) | ||||
|       hashie (>= 3.4.6) | ||||
|       rack (>= 2.2.3) | ||||
|       rack-protection | ||||
|     omniauth-oauth2 (1.8.0) | ||||
|       oauth2 (>= 1.4, < 3) | ||||
|       omniauth (~> 2.0) | ||||
|     orm_adapter (0.5.0) | ||||
|     os (1.1.4) | ||||
|     parallel (1.22.1) | ||||
| @@ -465,6 +481,8 @@ GEM | ||||
|       rack (>= 1.0, < 3) | ||||
|     rack-cors (1.1.1) | ||||
|       rack (>= 2.0.0) | ||||
|     rack-protection (3.0.5) | ||||
|       rack | ||||
|     rack-proxy (0.7.2) | ||||
|       rack | ||||
|     rack-test (2.0.2) | ||||
| @@ -620,6 +638,9 @@ GEM | ||||
|       gli | ||||
|       hashie | ||||
|       websocket-driver | ||||
|     snaky_hash (2.0.1) | ||||
|       hashie | ||||
|       version_gem (~> 1.1, >= 1.1.1) | ||||
|     spring (2.1.1) | ||||
|     spring-watcher-listen (2.0.1) | ||||
|       listen (>= 2.7, < 4.0) | ||||
| @@ -635,6 +656,7 @@ GEM | ||||
|     statsd-ruby (1.5.0) | ||||
|     stripe (6.5.0) | ||||
|     telephone_number (1.4.16) | ||||
|     test-prof (1.0.11) | ||||
|     thor (1.2.1) | ||||
|     tilt (2.0.10) | ||||
|     time_diff (0.3.0) | ||||
| @@ -663,6 +685,7 @@ GEM | ||||
|     valid_email2 (4.0.3) | ||||
|       activemodel (>= 3.2) | ||||
|       mail (~> 2.5) | ||||
|     version_gem (1.1.1) | ||||
|     warden (1.2.9) | ||||
|       rack (>= 2.0.9) | ||||
|     web-console (4.2.0) | ||||
| @@ -735,6 +758,7 @@ DEPENDENCIES | ||||
|   flag_shih_tzu | ||||
|   foreman | ||||
|   geocoder | ||||
|   gmail_xoauth | ||||
|   google-cloud-dialogflow | ||||
|   google-cloud-storage | ||||
|   groupdate | ||||
| @@ -756,6 +780,7 @@ DEPENDENCIES | ||||
|   maxminddb | ||||
|   mock_redis | ||||
|   newrelic_rpm | ||||
|   omniauth-oauth2 | ||||
|   pg | ||||
|   pg_search | ||||
|   procore-sift | ||||
| @@ -791,6 +816,7 @@ DEPENDENCIES | ||||
|   squasher | ||||
|   stripe | ||||
|   telephone_number | ||||
|   test-prof | ||||
|   time_diff | ||||
|   twilio-ruby (~> 5.66) | ||||
|   twitty | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| backend: bin/rails s -p 3000 | ||||
| frontend: bin/webpack-dev-server | ||||
| worker: bundle exec sidekiq -C config/sidekiq.yml | ||||
| # https://github.com/mperham/sidekiq/issues/3090#issuecomment-389748695 | ||||
| worker: dotenv bundle exec sidekiq -C config/sidekiq.yml | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| backend: RAILS_ENV=test bin/rails s -p 5050 | ||||
| frontend: bin/webpack-dev-server | ||||
| worker: RAILS_ENV=test bundle exec sidekiq -C config/sidekiq.yml | ||||
| worker: dotenv RAILS_ENV=test bundle exec sidekiq -C config/sidekiq.yml | ||||
|   | ||||
| @@ -119,4 +119,4 @@ Thanks goes to all these [wonderful people](https://www.chatwoot.com/docs/contri | ||||
| <a href="https://github.com/chatwoot/chatwoot/graphs/contributors"><img src="https://opencollective.com/chatwoot/contributors.svg?width=890&button=false" /></a> | ||||
|  | ||||
|  | ||||
| *Chatwoot* © 2017-2022, Chatwoot Inc - Released under the MIT License. | ||||
| *Chatwoot* © 2017-2023, Chatwoot Inc - Released under the MIT License. | ||||
|   | ||||
| @@ -0,0 +1,48 @@ | ||||
| class Api::V1::Accounts::Integrations::DyteController < Api::V1::Accounts::BaseController | ||||
|   before_action :fetch_conversation, only: [:create_a_meeting] | ||||
|   before_action :fetch_message, only: [:add_participant_to_meeting] | ||||
|   before_action :authorize_request | ||||
|  | ||||
|   def create_a_meeting | ||||
|     render_response(dyte_processor_service.create_a_meeting(Current.user)) | ||||
|   end | ||||
|  | ||||
|   def add_participant_to_meeting | ||||
|     if @message.content_type != 'integrations' | ||||
|       return render json: { | ||||
|         error: I18n.t('errors.dyte.invalid_message_type') | ||||
|       }, status: :unprocessable_entity | ||||
|     end | ||||
|  | ||||
|     render_response( | ||||
|       dyte_processor_service.add_participant_to_meeting(@message.content_attributes['data']['meeting_id'], Current.user) | ||||
|     ) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def authorize_request | ||||
|     authorize @conversation.inbox, :show? | ||||
|   end | ||||
|  | ||||
|   def render_response(response) | ||||
|     render json: response, status: response[:error].blank? ? :ok : :unprocessable_entity | ||||
|   end | ||||
|  | ||||
|   def dyte_processor_service | ||||
|     Integrations::Dyte::ProcessorService.new(account: Current.account, conversation: @conversation) | ||||
|   end | ||||
|  | ||||
|   def permitted_params | ||||
|     params.permit(:conversation_id, :message_id) | ||||
|   end | ||||
|  | ||||
|   def fetch_conversation | ||||
|     @conversation = Current.account.conversations.find_by!(display_id: permitted_params[:conversation_id]) | ||||
|   end | ||||
|  | ||||
|   def fetch_message | ||||
|     @message = Current.account.messages.find(permitted_params[:message_id]) | ||||
|     @conversation = @message.conversation | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,27 @@ | ||||
| class Api::V1::Accounts::Microsoft::AuthorizationsController < Api::V1::Accounts::BaseController | ||||
|   include MicrosoftConcern | ||||
|   before_action :check_authorization | ||||
|  | ||||
|   def create | ||||
|     email = params[:authorization][:email] | ||||
|     redirect_url = microsoft_client.auth_code.authorize_url( | ||||
|       { | ||||
|         redirect_uri: "#{base_url}/microsoft/callback", | ||||
|         scope: 'offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send openid', | ||||
|         prompt: 'consent' | ||||
|       } | ||||
|     ) | ||||
|     if redirect_url | ||||
|       ::Redis::Alfred.setex(email, Current.account.id, 5.minutes) | ||||
|       render json: { success: true, url: redirect_url } | ||||
|     else | ||||
|       render json: { success: false }, status: :unprocessable_entity | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def check_authorization | ||||
|     raise Pundit::NotAuthorizedError unless Current.account_user.administrator? | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,36 @@ | ||||
| class Api::V1::Widget::Integrations::DyteController < Api::V1::Widget::BaseController | ||||
|   before_action :set_message | ||||
|  | ||||
|   def add_participant_to_meeting | ||||
|     if @message.content_type != 'integrations' | ||||
|       return render json: { | ||||
|         error: I18n.t('errors.dyte.invalid_message_type') | ||||
|       }, status: :unprocessable_entity | ||||
|     end | ||||
|  | ||||
|     response = dyte_processor_service.add_participant_to_meeting( | ||||
|       @message.content_attributes['data']['meeting_id'], | ||||
|       @conversation.contact | ||||
|     ) | ||||
|     render_response(response) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def render_response(response) | ||||
|     render json: response, status: response[:error].blank? ? :ok : :unprocessable_entity | ||||
|   end | ||||
|  | ||||
|   def dyte_processor_service | ||||
|     Integrations::Dyte::ProcessorService.new(account: @web_widget.inbox.account, conversation: @conversation) | ||||
|   end | ||||
|  | ||||
|   def set_message | ||||
|     @message = @web_widget.inbox.messages.find(permitted_params[:message_id]) | ||||
|     @conversation = @message.conversation | ||||
|   end | ||||
|  | ||||
|   def permitted_params | ||||
|     params.permit(:website_token, :message_id) | ||||
|   end | ||||
| end | ||||
							
								
								
									
										22
									
								
								app/controllers/concerns/microsoft_concern.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/controllers/concerns/microsoft_concern.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| module MicrosoftConcern | ||||
|   extend ActiveSupport::Concern | ||||
|  | ||||
|   def microsoft_client | ||||
|     ::OAuth2::Client.new(ENV.fetch('AZURE_APP_ID', nil), ENV.fetch('AZURE_APP_SECRET', nil), | ||||
|                          { | ||||
|                            site: 'https://login.microsoftonline.com', | ||||
|                            authorize_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', | ||||
|                            token_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token' | ||||
|                          }) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def parsed_body | ||||
|     @parsed_body ||= Rack::Utils.parse_nested_query(@response.raw_response.body) | ||||
|   end | ||||
|  | ||||
|   def base_url | ||||
|     ENV.fetch('FRONTEND_URL', 'http://localhost:3000') | ||||
|   end | ||||
| end | ||||
| @@ -55,7 +55,8 @@ class DashboardController < ActionController::Base | ||||
|       ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false'), | ||||
|       FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''), | ||||
|       FACEBOOK_API_VERSION: 'v14.0', | ||||
|       IS_ENTERPRISE: ChatwootApp.enterprise? | ||||
|       IS_ENTERPRISE: ChatwootApp.enterprise?, | ||||
|       AZURE_APP_ID: ENV.fetch('AZURE_APP_ID', '') | ||||
|     } | ||||
|   end | ||||
| end | ||||
|   | ||||
							
								
								
									
										72
									
								
								app/controllers/microsoft/callbacks_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								app/controllers/microsoft/callbacks_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| class Microsoft::CallbacksController < ApplicationController | ||||
|   include MicrosoftConcern | ||||
|  | ||||
|   def show | ||||
|     @response = microsoft_client.auth_code.get_token( | ||||
|       oauth_code, | ||||
|       redirect_uri: "#{base_url}/microsoft/callback" | ||||
|     ) | ||||
|  | ||||
|     inbox = find_or_create_inbox | ||||
|     ::Redis::Alfred.delete(users_data['email']) | ||||
|     redirect_to app_microsoft_inbox_agents_url(account_id: account.id, inbox_id: inbox.id) | ||||
|   rescue StandardError => e | ||||
|     ChatwootExceptionTracker.new(e).capture_exception | ||||
|     redirect_to '/' | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def oauth_code | ||||
|     params[:code] | ||||
|   end | ||||
|  | ||||
|   def users_data | ||||
|     decoded_token = JWT.decode parsed_body[:id_token], nil, false | ||||
|     decoded_token[0] | ||||
|   end | ||||
|  | ||||
|   def parsed_body | ||||
|     @parsed_body ||= @response.response.parsed | ||||
|   end | ||||
|  | ||||
|   def account_id | ||||
|     ::Redis::Alfred.get(users_data['email']) | ||||
|   end | ||||
|  | ||||
|   def account | ||||
|     @account ||= Account.find(account_id) | ||||
|   end | ||||
|  | ||||
|   def find_or_create_inbox | ||||
|     channel_email = Channel::Email.find_by(email: users_data['email'], account: account) | ||||
|     channel_email ||= create_microsoft_channel_with_inbox | ||||
|     update_microsoft_channel(channel_email) | ||||
|     channel_email.inbox | ||||
|   end | ||||
|  | ||||
|   def create_microsoft_channel_with_inbox | ||||
|     ActiveRecord::Base.transaction do | ||||
|       channel_email = Channel::Email.create!(email: users_data['email'], account: account) | ||||
|       account.inboxes.create!( | ||||
|         account: account, | ||||
|         channel: channel_email, | ||||
|         name: users_data['name'] | ||||
|       ) | ||||
|       channel_email | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def update_microsoft_channel(channel_email) | ||||
|     channel_email.update!({ | ||||
|                             imap_login: users_data['email'], imap_address: 'outlook.office365.com', | ||||
|                             imap_port: '993', imap_enabled: true, | ||||
|                             provider: 'microsoft', | ||||
|                             provider_config: { | ||||
|                               access_token: parsed_body['access_token'], | ||||
|                               refresh_token: parsed_body['refresh_token'], | ||||
|                               expires_on: (Time.current.utc + 1.hour).to_s | ||||
|                             } | ||||
|                           }) | ||||
|   end | ||||
| end | ||||
| @@ -11,8 +11,8 @@ class SwaggerController < ApplicationController | ||||
|  | ||||
|   def derived_path | ||||
|     params[:path] ||= 'index.html' | ||||
|     path = params[:path] | ||||
|     path << ".#{params[:format]}" unless path.ends_with?(params[:format].to_s) | ||||
|     path = Rack::Utils.clean_path_info(params[:path]) | ||||
|     path << ".#{Rack::Utils.clean_path_info(params[:format])}" unless path.ends_with?(params[:format].to_s) | ||||
|     path | ||||
|   end | ||||
| end | ||||
|   | ||||
							
								
								
									
										17
									
								
								app/drops/contact_drop.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/drops/contact_drop.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| class ContactDrop < BaseDrop | ||||
|   def email | ||||
|     @obj.try(:email) | ||||
|   end | ||||
|  | ||||
|   def phone_number | ||||
|     @obj.try(:phone_number) | ||||
|   end | ||||
|  | ||||
|   def first_name | ||||
|     @obj.try(:name).try(:split).try(:first) | ||||
|   end | ||||
|  | ||||
|   def last_name | ||||
|     @obj.try(:name).try(:split).try(:last) if @obj.try(:name).try(:split).try(:size) > 1 | ||||
|   end | ||||
| end | ||||
| @@ -2,4 +2,12 @@ class UserDrop < BaseDrop | ||||
|   def available_name | ||||
|     @obj.try(:available_name) | ||||
|   end | ||||
|  | ||||
|   def first_name | ||||
|     @obj.try(:name).try(:split).try(:first) | ||||
|   end | ||||
|  | ||||
|   def last_name | ||||
|     @obj.try(:name).try(:split).try(:last) if @obj.try(:name).try(:split).try(:size) > 1 | ||||
|   end | ||||
| end | ||||
|   | ||||
							
								
								
									
										14
									
								
								app/javascript/dashboard/api/channel/microsoftClient.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/javascript/dashboard/api/channel/microsoftClient.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| /* global axios */ | ||||
| import ApiClient from '../ApiClient'; | ||||
|  | ||||
| class MicrosoftClient extends ApiClient { | ||||
|   constructor() { | ||||
|     super('microsoft', { accountScoped: true }); | ||||
|   } | ||||
|  | ||||
|   generateAuthorization(payload) { | ||||
|     return axios.post(`${this.url}/authorization`, payload); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default new MicrosoftClient(); | ||||
							
								
								
									
										23
									
								
								app/javascript/dashboard/api/integrations/dyte.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/javascript/dashboard/api/integrations/dyte.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| /* global axios */ | ||||
|  | ||||
| import ApiClient from '../ApiClient'; | ||||
|  | ||||
| class DyteAPI extends ApiClient { | ||||
|   constructor() { | ||||
|     super('integrations/dyte', { accountScoped: true }); | ||||
|   } | ||||
|  | ||||
|   createAMeeting(conversationId) { | ||||
|     return axios.post(`${this.url}/create_a_meeting`, { | ||||
|       conversation_id: conversationId, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   addParticipantToMeeting(messageId) { | ||||
|     return axios.post(`${this.url}/add_participant_to_meeting`, { | ||||
|       message_id: messageId, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default new DyteAPI(); | ||||
							
								
								
									
										35
									
								
								app/javascript/dashboard/api/specs/integrations/dyte.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/javascript/dashboard/api/specs/integrations/dyte.spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import DyteAPIClient from '../../integrations/dyte'; | ||||
| import ApiClient from '../../ApiClient'; | ||||
| import describeWithAPIMock from '../apiSpecHelper'; | ||||
|  | ||||
| describe('#accountAPI', () => { | ||||
|   it('creates correct instance', () => { | ||||
|     expect(DyteAPIClient).toBeInstanceOf(ApiClient); | ||||
|     expect(DyteAPIClient).toHaveProperty('createAMeeting'); | ||||
|     expect(DyteAPIClient).toHaveProperty('addParticipantToMeeting'); | ||||
|   }); | ||||
|  | ||||
|   describeWithAPIMock('createAMeeting', context => { | ||||
|     it('creates a valid request', () => { | ||||
|       DyteAPIClient.createAMeeting(1); | ||||
|       expect(context.axiosMock.post).toHaveBeenCalledWith( | ||||
|         '/api/v1/integrations/dyte/create_a_meeting', | ||||
|         { | ||||
|           conversation_id: 1, | ||||
|         } | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describeWithAPIMock('addParticipantToMeeting', context => { | ||||
|     it('creates a valid request', () => { | ||||
|       DyteAPIClient.addParticipantToMeeting(1); | ||||
|       expect(context.axiosMock.post).toHaveBeenCalledWith( | ||||
|         '/api/v1/integrations/dyte/add_participant_to_meeting', | ||||
|         { | ||||
|           message_id: 1, | ||||
|         } | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 12 KiB | 
| @@ -75,7 +75,7 @@ Arial, | ||||
| sans-serif; | ||||
| $body-antialiased: true; | ||||
| $global-margin: $space-small; | ||||
| $global-padding: $space-micro; | ||||
| $global-padding: $space-small; | ||||
| $global-weight-normal: normal; | ||||
| $global-weight-bold: bold; | ||||
| $global-radius: 0; | ||||
|   | ||||
| @@ -37,7 +37,6 @@ | ||||
| @include foundation-prototype-sizing; | ||||
| @include foundation-prototype-spacing; | ||||
|  | ||||
|  | ||||
| @import 'typography'; | ||||
| @import 'layout'; | ||||
| @import 'animations'; | ||||
| @@ -61,7 +60,6 @@ | ||||
| @import 'widgets/woot-tables'; | ||||
|  | ||||
| @import 'views/settings/inbox'; | ||||
| @import 'views/settings/channel'; | ||||
| @import 'views/settings/integrations'; | ||||
|  | ||||
| @import 'plugins/multiselect'; | ||||
|   | ||||
| @@ -1,52 +0,0 @@ | ||||
| $channel-hover-color: rgba(0, 0, 0, 0.1); | ||||
|  | ||||
| .channels { | ||||
|   margin-top: $space-medium; | ||||
|  | ||||
|   .inactive { | ||||
|     filter: grayscale(100%); | ||||
|   } | ||||
|  | ||||
|   .channel { | ||||
|     @include flex; | ||||
|     @include background-white; | ||||
|     @include border-light; | ||||
|  | ||||
|     cursor: pointer; | ||||
|     flex-direction: column; | ||||
|     margin: -1px; | ||||
|     padding: $space-normal $zero; | ||||
|     transition: all 0.2s ease-in; | ||||
|  | ||||
|     &:last-child { | ||||
|       @include border-light; | ||||
|     } | ||||
|  | ||||
|     &:hover { | ||||
|       border: 1px solid $primary-color; | ||||
|       box-shadow: 0 2px 8px $channel-hover-color; | ||||
|       z-index: 999; | ||||
|     } | ||||
|  | ||||
|     &.disabled { | ||||
|       opacity: 0.6; | ||||
|     } | ||||
|  | ||||
|     img { | ||||
|       margin: $space-normal auto; | ||||
|       width: 50%; | ||||
|     } | ||||
|  | ||||
|     .channel__title { | ||||
|       color: $color-body; | ||||
|       font-size: var(--font-size-default); | ||||
|       text-align: center; | ||||
|       text-transform: capitalize; | ||||
|     } | ||||
|  | ||||
|     p { | ||||
|       color: $medium-gray; | ||||
|       width: 100%; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -42,7 +42,6 @@ $resolve-button-width: 13.2rem; | ||||
|     margin-right: var(--space-normal); | ||||
|     min-width: 0; | ||||
|  | ||||
|  | ||||
|     .user--profile__meta { | ||||
|       align-items: flex-start; | ||||
|       display: flex; | ||||
| @@ -54,13 +53,17 @@ $resolve-button-width: 13.2rem; | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| .header-actions-wrap { | ||||
|   align-items: center; | ||||
|   display: flex; | ||||
|   flex-direction: row; | ||||
|   flex-grow: 1; | ||||
|   justify-content: flex-end; | ||||
|   margin-top: var(--space-small); | ||||
|  | ||||
|   @include breakpoint(medium up) { | ||||
|     margin-top: 0; | ||||
|   } | ||||
|  | ||||
|   &.has-open-sidebar { | ||||
|     justify-content: flex-end; | ||||
|   | ||||
| @@ -81,8 +81,12 @@ | ||||
|   } | ||||
|  | ||||
|   .conversations-list { | ||||
|     @include scroll-on-hover; | ||||
|     overflow-y: auto; | ||||
|     flex: 1 1; | ||||
|  | ||||
|     @include breakpoint(large up) { | ||||
|       @include scroll-on-hover; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .chat-list__top { | ||||
| @@ -324,6 +328,7 @@ | ||||
|       var(--space-one); | ||||
|  | ||||
|     .is-text { | ||||
|       align-items: center; | ||||
|       display: inline-flex; | ||||
|       text-align: start; | ||||
|  | ||||
|   | ||||
| @@ -82,8 +82,8 @@ | ||||
|     @include flex-align($x: flex-end, $y: middle); | ||||
|     padding: $space-small $zero; | ||||
|  | ||||
|     button { | ||||
|       font-size: $font-size-small; | ||||
|     .button { | ||||
|       margin-left: var(--space-small); | ||||
|     } | ||||
|  | ||||
|     &.justify-content-end { | ||||
|   | ||||
							
								
								
									
										67
									
								
								app/javascript/dashboard/components/ChannelSelector.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								app/javascript/dashboard/components/ChannelSelector.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| <template> | ||||
|   <button class="small-6 medium-4 large-3 channel" @click="$emit('click')"> | ||||
|     <img :src="src" :alt="title" /> | ||||
|     <h3 class="channel__title"> | ||||
|       {{ title }} | ||||
|     </h3> | ||||
|   </button> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     title: { | ||||
|       type: String, | ||||
|       required: true, | ||||
|     }, | ||||
|     src: { | ||||
|       type: String, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .inactive { | ||||
|   filter: grayscale(100%); | ||||
| } | ||||
|  | ||||
| .channel { | ||||
|   background: var(--white); | ||||
|   border: 1px solid var(--color-border-light); | ||||
|   cursor: pointer; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   margin: -1px; | ||||
|   padding: var(--space-normal) 0; | ||||
|   transition: all 0.2s ease-in; | ||||
|   align-items: center; | ||||
|   &:hover { | ||||
|     border: 1px solid var(--w-500); | ||||
|     box-shadow: var(--shadow-medium); | ||||
|     z-index: var(--z-index-high); | ||||
|   } | ||||
|  | ||||
|   &.disabled { | ||||
|     opacity: 0.6; | ||||
|   } | ||||
|  | ||||
|   img { | ||||
|     margin: var(--space-normal) auto; | ||||
|     width: 50%; | ||||
|   } | ||||
|  | ||||
|   .channel__title { | ||||
|     color: var(--color-body); | ||||
|     font-size: var(--font-size-default); | ||||
|     text-align: center; | ||||
|     text-transform: capitalize; | ||||
|   } | ||||
|  | ||||
|   p { | ||||
|     color: var(--b-500); | ||||
|     width: 100%; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -794,30 +794,15 @@ export default { | ||||
|  | ||||
| .conversations-list-wrap { | ||||
|   flex-shrink: 0; | ||||
|   width: 34rem; | ||||
|   flex-basis: clamp(32rem, 4vw + 34rem, 44rem); | ||||
|   overflow: hidden; | ||||
|   @include breakpoint(large up) { | ||||
|     width: 36rem; | ||||
|   } | ||||
|   @include breakpoint(xlarge up) { | ||||
|     width: 35rem; | ||||
|   } | ||||
|   @include breakpoint(xxlarge up) { | ||||
|     width: 38rem; | ||||
|   } | ||||
|   @include breakpoint(xxxlarge up) { | ||||
|     flex-basis: 46rem; | ||||
|   } | ||||
|  | ||||
|   &.hide { | ||||
|     display: none; | ||||
|   } | ||||
|  | ||||
|   &.list--full-width { | ||||
|     width: 100%; | ||||
|     @include breakpoint(xxxlarge up) { | ||||
|       flex-basis: 100%; | ||||
|     } | ||||
|     flex-basis: 100%; | ||||
|   } | ||||
| } | ||||
| .filter--actions { | ||||
|   | ||||
| @@ -39,6 +39,8 @@ export default { | ||||
| <style scoped lang="scss"> | ||||
| .wrap-content { | ||||
|   word-wrap: break-word; | ||||
|   margin-top: var(--space-small); | ||||
|  | ||||
|   .content-value { | ||||
|     font-weight: var(--font-weight-bold); | ||||
|   } | ||||
|   | ||||
| @@ -233,9 +233,6 @@ export default { | ||||
|     width: 40rem; | ||||
|   } | ||||
| } | ||||
| .off-canvas-content.is-open-left { | ||||
|   transform: translateX(18.8rem); | ||||
| } | ||||
|  | ||||
| .secondary-sidebar { | ||||
|   overflow-y: auto; | ||||
|   | ||||
| @@ -88,9 +88,9 @@ | ||||
|       v-model="action_params" | ||||
|       :teams="dropdownValues" | ||||
|     /> | ||||
|     <textarea | ||||
|     <woot-message-editor | ||||
|       v-if="inputType === 'textarea'" | ||||
|       v-model="action_params" | ||||
|       v-model="castMessageVmodel" | ||||
|       rows="4" | ||||
|       :placeholder="$t('AUTOMATION.ACTION.TEAM_MESSAGE_INPUT_PLACEHOLDER')" | ||||
|       class="action-message" | ||||
| @@ -107,10 +107,12 @@ | ||||
| <script> | ||||
| import AutomationActionTeamMessageInput from './AutomationActionTeamMessageInput.vue'; | ||||
| import AutomationActionFileInput from './AutomationFileInput.vue'; | ||||
| import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor'; | ||||
| export default { | ||||
|   components: { | ||||
|     AutomationActionTeamMessageInput, | ||||
|     AutomationActionFileInput, | ||||
|     WootMessageEditor, | ||||
|   }, | ||||
|   props: { | ||||
|     value: { | ||||
| @@ -173,6 +175,17 @@ export default { | ||||
|         'is-a-macro': this.isMacro, | ||||
|       }; | ||||
|     }, | ||||
|     castMessageVmodel: { | ||||
|       get() { | ||||
|         if (Array.isArray(this.action_params)) { | ||||
|           return this.action_params[0]; | ||||
|         } | ||||
|         return this.action_params; | ||||
|       }, | ||||
|       set(value) { | ||||
|         this.action_params = value; | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     removeAction() { | ||||
| @@ -281,4 +294,8 @@ export default { | ||||
| .action-message { | ||||
|   margin: var(--space-small) var(--space-zero) var(--space-zero); | ||||
| } | ||||
| // Prosemirror does not have a native way of hiding the menu bar, hence | ||||
| ::v-deep .ProseMirror-menubar { | ||||
|   display: none; | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -1,56 +1,15 @@ | ||||
| <template> | ||||
|   <div | ||||
|     class="small-6 medium-4 large-3 columns channel" | ||||
|   <channel-selector | ||||
|     :class="{ inactive: !isActive }" | ||||
|     :title="channel.name" | ||||
|     :src="getChannelThumbnail()" | ||||
|     @click="onItemClick" | ||||
|   > | ||||
|     <img | ||||
|       v-if="channel.key === 'facebook'" | ||||
|       src="~dashboard/assets/images/channels/messenger.png" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'twitter'" | ||||
|       src="~dashboard/assets/images/channels/twitter.png" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'telegram'" | ||||
|       src="~dashboard/assets/images/channels/telegram.png" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'api' && !channel.thumbnail" | ||||
|       src="~dashboard/assets/images/channels/api.png" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'api' && channel.thumbnail" | ||||
|       :src="channel.thumbnail" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'email'" | ||||
|       src="~dashboard/assets/images/channels/email.png" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'line'" | ||||
|       src="~dashboard/assets/images/channels/line.png" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'website'" | ||||
|       src="~dashboard/assets/images/channels/website.png" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'sms'" | ||||
|       src="~dashboard/assets/images/channels/sms.png" | ||||
|     /> | ||||
|     <img | ||||
|       v-if="channel.key === 'whatsapp'" | ||||
|       src="~dashboard/assets/images/channels/whatsapp.png" | ||||
|     /> | ||||
|     <h3 class="channel__title"> | ||||
|       {{ channel.name }} | ||||
|     </h3> | ||||
|   </div> | ||||
|   /> | ||||
| </template> | ||||
| <script> | ||||
| import ChannelSelector from '../ChannelSelector'; | ||||
| export default { | ||||
|   components: { ChannelSelector }, | ||||
|   props: { | ||||
|     channel: { | ||||
|       type: Object, | ||||
| @@ -92,6 +51,12 @@ export default { | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     getChannelThumbnail() { | ||||
|       if (this.channel.key === 'api' && this.channel.thumbnail) { | ||||
|         return this.channel.thumbnail; | ||||
|       } | ||||
|       return `/assets/images/dashboard/channels/${this.channel.key}.png`; | ||||
|     }, | ||||
|     onItemClick() { | ||||
|       if (this.isActive) { | ||||
|         this.$emit('channel-item-click', this.channel.key); | ||||
|   | ||||
| @@ -0,0 +1,57 @@ | ||||
| <template> | ||||
|   <woot-button | ||||
|     v-if="isVideoIntegrationEnabled" | ||||
|     v-tooltip.top-end=" | ||||
|       $t('INTEGRATION_SETTINGS.DYTE.START_VIDEO_CALL_HELP_TEXT') | ||||
|     " | ||||
|     icon="video" | ||||
|     :is-loading="isLoading" | ||||
|     color-scheme="secondary" | ||||
|     variant="smooth" | ||||
|     size="small" | ||||
|     @click="onClick" | ||||
|   /> | ||||
| </template> | ||||
| <script> | ||||
| import { mapGetters } from 'vuex'; | ||||
| import DyteAPI from 'dashboard/api/integrations/dyte'; | ||||
| import alertMixin from 'shared/mixins/alertMixin'; | ||||
|  | ||||
| export default { | ||||
|   mixins: [alertMixin], | ||||
|   props: { | ||||
|     conversationId: { | ||||
|       type: Number, | ||||
|       default: 0, | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { isLoading: false }; | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapGetters({ appIntegrations: 'integrations/getAppIntegrations' }), | ||||
|     isVideoIntegrationEnabled() { | ||||
|       return this.appIntegrations.find( | ||||
|         integration => integration.id === 'dyte' && !!integration.hooks.length | ||||
|       ); | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     if (!this.appIntegrations.length) { | ||||
|       this.$store.dispatch('integrations/get'); | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     async onClick() { | ||||
|       this.isLoading = true; | ||||
|       try { | ||||
|         await DyteAPI.createAMeeting(this.conversationId); | ||||
|       } catch (error) { | ||||
|         this.showAlert(this.$t('INTEGRATION_SETTINGS.DYTE.CREATE_ERROR')); | ||||
|       } finally { | ||||
|         this.isLoading = false; | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| @@ -15,29 +15,25 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { EditorView } from 'prosemirror-view'; | ||||
|  | ||||
| import { defaultMarkdownSerializer } from 'prosemirror-markdown'; | ||||
| import { | ||||
|   addMentionsToMarkdownSerializer, | ||||
|   addMentionsToMarkdownParser, | ||||
|   schemaWithMentions, | ||||
| } from '@chatwoot/prosemirror-schema/src/mentions/schema'; | ||||
|  | ||||
|   messageSchema, | ||||
|   wootMessageWriterSetup, | ||||
|   EditorView, | ||||
|   MessageMarkdownTransformer, | ||||
|   MessageMarkdownSerializer, | ||||
|   EditorState, | ||||
|   Selection, | ||||
| } from '@chatwoot/prosemirror-schema'; | ||||
| import { | ||||
|   suggestionsPlugin, | ||||
|   triggerCharacters, | ||||
| } from '@chatwoot/prosemirror-schema/src/mentions/plugin'; | ||||
| import { EditorState, Selection } from 'prosemirror-state'; | ||||
| import { defaultMarkdownParser } from 'prosemirror-markdown'; | ||||
| import { wootWriterSetup } from '@chatwoot/prosemirror-schema'; | ||||
|  | ||||
| import TagAgents from '../conversation/TagAgents'; | ||||
| import CannedResponse from '../conversation/CannedResponse'; | ||||
|  | ||||
| const TYPING_INDICATOR_IDLE_TIME = 4000; | ||||
|  | ||||
| import '@chatwoot/prosemirror-schema/src/woot-editor.css'; | ||||
| import { | ||||
|   hasPressedEnterAndNotCmdOrShift, | ||||
|   hasPressedCommandAndEnter, | ||||
| @@ -53,9 +49,9 @@ import AnalyticsHelper, { | ||||
|  | ||||
| const createState = (content, placeholder, plugins = []) => { | ||||
|   return EditorState.create({ | ||||
|     doc: addMentionsToMarkdownParser(defaultMarkdownParser).parse(content), | ||||
|     plugins: wootWriterSetup({ | ||||
|       schema: schemaWithMentions, | ||||
|     doc: new MessageMarkdownTransformer(messageSchema).parse(content), | ||||
|     plugins: wootMessageWriterSetup({ | ||||
|       schema: messageSchema, | ||||
|       placeholder, | ||||
|       plugins, | ||||
|     }), | ||||
| @@ -88,9 +84,7 @@ export default { | ||||
|   }, | ||||
|   computed: { | ||||
|     contentFromEditor() { | ||||
|       return addMentionsToMarkdownSerializer( | ||||
|         defaultMarkdownSerializer | ||||
|       ).serialize(this.editorView.state.doc); | ||||
|       return MessageMarkdownSerializer.serialize(this.editorView.state.doc); | ||||
|     }, | ||||
|     plugins() { | ||||
|       if (!this.enableSuggestions) { | ||||
| @@ -282,11 +276,11 @@ export default { | ||||
|       } | ||||
|  | ||||
|       let from = this.range.from - 1; | ||||
|       let node = addMentionsToMarkdownParser(defaultMarkdownParser).parse( | ||||
|       let node = new MessageMarkdownTransformer(messageSchema).parse( | ||||
|         cannedItem | ||||
|       ); | ||||
|  | ||||
|       if (node.childCount === 1) { | ||||
|       if (node.textContent === cannedItem) { | ||||
|         node = this.editorView.state.schema.text(cannedItem); | ||||
|         from = this.range.from; | ||||
|       } | ||||
| @@ -372,10 +366,18 @@ export default { | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
| @import '~@chatwoot/prosemirror-schema/src/styles/base.scss'; | ||||
|  | ||||
| .ProseMirror-menubar-wrapper { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|  | ||||
|   .ProseMirror-menubar { | ||||
|     min-height: var(--space-two) !important; | ||||
|     margin-left: var(--space-minus-one); | ||||
|     padding-bottom: 0; | ||||
|   } | ||||
|  | ||||
|   > .ProseMirror { | ||||
|     padding: 0; | ||||
|     word-break: break-word; | ||||
| @@ -384,6 +386,7 @@ export default { | ||||
|  | ||||
| .editor-root { | ||||
|   width: 100%; | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| .ProseMirror-woot-style { | ||||
| @@ -406,6 +409,9 @@ export default { | ||||
|     color: var(--s-900); | ||||
|     padding: 0 var(--space-smaller); | ||||
|   } | ||||
|   .ProseMirror-menubar { | ||||
|     background: var(--y-50); | ||||
|   } | ||||
| } | ||||
|  | ||||
| .editor-wrap { | ||||
|   | ||||
| @@ -0,0 +1,167 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <div class="editor-root editor--article"> | ||||
|       <div ref="editor" /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { | ||||
|   fullSchema, | ||||
|   wootArticleWriterSetup, | ||||
|   EditorView, | ||||
|   ArticleMarkdownSerializer, | ||||
|   ArticleMarkdownTransformer, | ||||
|   EditorState, | ||||
|   Selection, | ||||
| } from '@chatwoot/prosemirror-schema'; | ||||
|  | ||||
| import eventListenerMixins from 'shared/mixins/eventListenerMixins'; | ||||
| import uiSettingsMixin from 'dashboard/mixins/uiSettings'; | ||||
|  | ||||
| const createState = (content, placeholder, plugins = []) => { | ||||
|   return EditorState.create({ | ||||
|     doc: new ArticleMarkdownTransformer(fullSchema).parse(content), | ||||
|     plugins: wootArticleWriterSetup({ | ||||
|       schema: fullSchema, | ||||
|       placeholder, | ||||
|       plugins, | ||||
|     }), | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| export default { | ||||
|   mixins: [eventListenerMixins, uiSettingsMixin], | ||||
|   props: { | ||||
|     value: { type: String, default: '' }, | ||||
|     editorId: { type: String, default: '' }, | ||||
|     placeholder: { type: String, default: '' }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       editorView: null, | ||||
|       state: undefined, | ||||
|       plugins: [], | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     contentFromEditor() { | ||||
|       if (this.editorView) { | ||||
|         return ArticleMarkdownSerializer.serialize(this.editorView.state.doc); | ||||
|       } | ||||
|       return ''; | ||||
|     }, | ||||
|   }, | ||||
|   watch: { | ||||
|     value(newValue = '') { | ||||
|       if (newValue !== this.contentFromEditor) { | ||||
|         this.reloadState(); | ||||
|       } | ||||
|     }, | ||||
|     editorId() { | ||||
|       this.reloadState(); | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.state = createState(this.value, this.placeholder, this.plugins); | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.createEditorView(); | ||||
|  | ||||
|     this.editorView.updateState(this.state); | ||||
|     this.focusEditorInputField(); | ||||
|   }, | ||||
|   methods: { | ||||
|     reloadState() { | ||||
|       this.state = createState(this.value, this.placeholder, this.plugins); | ||||
|       this.editorView.updateState(this.state); | ||||
|       this.focusEditorInputField(); | ||||
|     }, | ||||
|     createEditorView() { | ||||
|       this.editorView = new EditorView(this.$refs.editor, { | ||||
|         state: this.state, | ||||
|         dispatchTransaction: tx => { | ||||
|           this.state = this.state.apply(tx); | ||||
|           this.emitOnChange(); | ||||
|         }, | ||||
|         handleDOMEvents: { | ||||
|           keyup: () => { | ||||
|             this.onKeyup(); | ||||
|           }, | ||||
|           keydown: (view, event) => { | ||||
|             this.onKeydown(event); | ||||
|           }, | ||||
|           focus: () => { | ||||
|             this.onFocus(); | ||||
|           }, | ||||
|           blur: () => { | ||||
|             this.onBlur(); | ||||
|           }, | ||||
|         }, | ||||
|       }); | ||||
|     }, | ||||
|  | ||||
|     handleKeyEvents() {}, | ||||
|     focusEditorInputField() { | ||||
|       const { tr } = this.editorView.state; | ||||
|       const selection = Selection.atEnd(tr.doc); | ||||
|  | ||||
|       this.editorView.dispatch(tr.setSelection(selection)); | ||||
|       this.editorView.focus(); | ||||
|     }, | ||||
|  | ||||
|     emitOnChange() { | ||||
|       this.editorView.updateState(this.state); | ||||
|  | ||||
|       this.$emit('input', this.contentFromEditor); | ||||
|     }, | ||||
|  | ||||
|     onKeyup() { | ||||
|       this.$emit('keyup'); | ||||
|     }, | ||||
|     onKeydown() { | ||||
|       this.$emit('keydown'); | ||||
|     }, | ||||
|     onBlur() { | ||||
|       this.$emit('blur'); | ||||
|     }, | ||||
|     onFocus() { | ||||
|       this.$emit('focus'); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
| @import '~@chatwoot/prosemirror-schema/src/styles/article.scss'; | ||||
|  | ||||
| .ProseMirror-menubar-wrapper { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|  | ||||
|   > .ProseMirror { | ||||
|     padding: 0; | ||||
|     word-break: break-word; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .editor-root { | ||||
|   width: 100%; | ||||
| } | ||||
|  | ||||
| .ProseMirror-woot-style { | ||||
|   min-height: 8rem; | ||||
|   max-height: 12rem; | ||||
|   overflow: auto; | ||||
| } | ||||
|  | ||||
| .ProseMirror-prompt { | ||||
|   z-index: var(--z-index-highest); | ||||
|   background: var(--white); | ||||
|   box-shadow: var(--shadow-large); | ||||
|   border-radius: var(--border-radius-normal); | ||||
|   border: 1px solid var(--color-border); | ||||
|   min-width: 40rem; | ||||
| } | ||||
| </style> | ||||
| @@ -87,6 +87,10 @@ | ||||
|         :title="'Whatsapp Templates'" | ||||
|         @click="$emit('selectWhatsappTemplate')" | ||||
|       /> | ||||
|       <video-call-button | ||||
|         v-if="(isAWebWidgetInbox || isAPIInbox) && !isOnPrivateNote" | ||||
|         :conversation-id="conversationId" | ||||
|       /> | ||||
|       <transition name="modal-fade"> | ||||
|         <div | ||||
|           v-show="$refs.upload && $refs.upload.dropActive" | ||||
| @@ -124,13 +128,13 @@ import { | ||||
|   ALLOWED_FILE_TYPES, | ||||
|   ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP, | ||||
| } from 'shared/constants/messages'; | ||||
|  | ||||
| import VideoCallButton from '../VideoCallButton'; | ||||
| import { REPLY_EDITOR_MODES } from './constants'; | ||||
| import { mapGetters } from 'vuex'; | ||||
|  | ||||
| export default { | ||||
|   name: 'ReplyBottomPanel', | ||||
|   components: { FileUpload }, | ||||
|   components: { FileUpload, VideoCallButton }, | ||||
|   mixins: [eventListenerMixins, uiSettingsMixin, inboxMixin], | ||||
|   props: { | ||||
|     mode: { | ||||
| @@ -209,6 +213,10 @@ export default { | ||||
|       type: Boolean, | ||||
|       default: false, | ||||
|     }, | ||||
|     conversationId: { | ||||
|       type: Number, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapGetters({ | ||||
| @@ -269,7 +277,7 @@ export default { | ||||
|       } | ||||
|     }, | ||||
|     showMessageSignatureButton() { | ||||
|       return !this.isPrivate && this.isAnEmailChannel; | ||||
|       return !this.isOnPrivateNote && this.isAnEmailChannel; | ||||
|     }, | ||||
|     sendWithSignature() { | ||||
|       const { send_with_signature: isEnabled } = this.uiSettings; | ||||
|   | ||||
| @@ -166,10 +166,14 @@ export default { | ||||
| .conversation-sidebar-wrap { | ||||
|   height: auto; | ||||
|   flex: 0 0; | ||||
|   overflow: hidden; | ||||
|   z-index: var(--z-index-low); | ||||
|   overflow: auto; | ||||
|   background: white; | ||||
|   flex-basis: 28rem; | ||||
|   flex-basis: 100%; | ||||
|  | ||||
|   @include breakpoint(medium up) { | ||||
|     flex-basis: 28rem; | ||||
|   } | ||||
|  | ||||
|   @include breakpoint(large up) { | ||||
|     flex-basis: 30em; | ||||
|   | ||||
| @@ -1,53 +1,54 @@ | ||||
| <template> | ||||
|   <div class="conv-header"> | ||||
|     <div class="user"> | ||||
|       <back-button v-if="showBackButton" :back-url="backButtonUrl" /> | ||||
|       <Thumbnail | ||||
|         :src="currentContact.thumbnail" | ||||
|         size="40px" | ||||
|         :badge="inboxBadge" | ||||
|         :username="currentContact.name" | ||||
|         :status="currentContact.availability_status" | ||||
|       /> | ||||
|       <div class="user--profile__meta"> | ||||
|         <h3 class="user--name text-truncate"> | ||||
|           <span class="margin-right-smaller">{{ currentContact.name }}</span> | ||||
|           <fluent-icon | ||||
|             v-if="!isHMACVerified" | ||||
|             v-tooltip="$t('CONVERSATION.UNVERIFIED_SESSION')" | ||||
|             size="14" | ||||
|             class="hmac-warning__icon" | ||||
|             icon="warning" | ||||
|           /> | ||||
|         </h3> | ||||
|         <div class="conversation--header--actions"> | ||||
|           <inbox-name | ||||
|             v-if="hasMultipleInboxes" | ||||
|             :inbox="inbox" | ||||
|             class="margin-right-small" | ||||
|           /> | ||||
|           <span | ||||
|             v-if="isSnoozed" | ||||
|             class="snoozed--display-text margin-right-small" | ||||
|           > | ||||
|             {{ snoozedDisplayText }} | ||||
|           </span> | ||||
|           <woot-button | ||||
|             class="user--profile__button margin-right-small" | ||||
|             size="small" | ||||
|             variant="link" | ||||
|             @click="$emit('contact-panel-toggle')" | ||||
|           > | ||||
|             {{ contactPanelToggleText }} | ||||
|           </woot-button> | ||||
|     <div class="conversation-header--details"> | ||||
|       <div class="user"> | ||||
|         <back-button v-if="showBackButton" :back-url="backButtonUrl" /> | ||||
|         <Thumbnail | ||||
|           :src="currentContact.thumbnail" | ||||
|           :badge="inboxBadge" | ||||
|           :username="currentContact.name" | ||||
|           :status="currentContact.availability_status" | ||||
|         /> | ||||
|         <div class="user--profile__meta"> | ||||
|           <h3 class="user--name text-truncate"> | ||||
|             <span class="margin-right-smaller">{{ currentContact.name }}</span> | ||||
|             <fluent-icon | ||||
|               v-if="!isHMACVerified" | ||||
|               v-tooltip="$t('CONVERSATION.UNVERIFIED_SESSION')" | ||||
|               size="14" | ||||
|               class="hmac-warning__icon" | ||||
|               icon="warning" | ||||
|             /> | ||||
|           </h3> | ||||
|           <div class="conversation--header--actions text-truncate"> | ||||
|             <inbox-name | ||||
|               v-if="hasMultipleInboxes" | ||||
|               :inbox="inbox" | ||||
|               class="margin-right-small" | ||||
|             /> | ||||
|             <span | ||||
|               v-if="isSnoozed" | ||||
|               class="snoozed--display-text margin-right-small" | ||||
|             > | ||||
|               {{ snoozedDisplayText }} | ||||
|             </span> | ||||
|             <woot-button | ||||
|               class="user--profile__button margin-right-small" | ||||
|               size="small" | ||||
|               variant="link" | ||||
|               @click="$emit('contact-panel-toggle')" | ||||
|             > | ||||
|               {{ contactPanelToggleText }} | ||||
|             </woot-button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div | ||||
|       class="header-actions-wrap" | ||||
|       :class="{ 'has-open-sidebar': isContactPanelOpen }" | ||||
|     > | ||||
|       <more-actions :conversation-id="currentChat.id" /> | ||||
|       <div | ||||
|         class="header-actions-wrap" | ||||
|         :class="{ 'has-open-sidebar': isContactPanelOpen }" | ||||
|       > | ||||
|         <more-actions :conversation-id="currentChat.id" /> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @@ -165,14 +166,27 @@ export default { | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .text-truncate { | ||||
|   white-space: nowrap; | ||||
|   overflow: hidden; | ||||
|   text-overflow: ellipsis; | ||||
| } | ||||
| @import '~dashboard/assets/scss/woot'; | ||||
|  | ||||
| .conv-header { | ||||
|   flex: 0 0 var(--space-jumbo); | ||||
|   flex-direction: row; | ||||
|  | ||||
|   @include breakpoint(medium up) { | ||||
|     flex-direction: column; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .conversation-header--details { | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   width: 100%; | ||||
|  | ||||
|   @include breakpoint(medium up) { | ||||
|     flex-direction: row; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .option__desc { | ||||
|   | ||||
| @@ -1,8 +1,5 @@ | ||||
| <template> | ||||
|   <li | ||||
|     v-if="hasAttachments || data.content || isEmailContentType" | ||||
|     :class="alignBubble" | ||||
|   > | ||||
|   <li v-if="shouldRenderMessage" :class="alignBubble"> | ||||
|     <div :class="wrapClass"> | ||||
|       <div v-tooltip.top-start="messageToolTip" :class="bubbleClass"> | ||||
|         <bubble-mail-head | ||||
| @@ -17,6 +14,11 @@ | ||||
|           :is-email="isEmailContentType" | ||||
|           :display-quoted-button="displayQuotedButton" | ||||
|         /> | ||||
|         <bubble-integration | ||||
|           :message-id="data.id" | ||||
|           :content-attributes="contentAttributes" | ||||
|           :inbox-id="data.inbox_id" | ||||
|         /> | ||||
|         <span | ||||
|           v-if="isPending && hasAttachments" | ||||
|           class="chat-bubble has-attachment agent" | ||||
| @@ -111,14 +113,14 @@ | ||||
| </template> | ||||
| <script> | ||||
| import messageFormatterMixin from 'shared/mixins/messageFormatterMixin'; | ||||
| import BubbleActions from './bubble/Actions'; | ||||
| import BubbleFile from './bubble/File'; | ||||
| import BubbleImage from './bubble/Image'; | ||||
| import BubbleIntegration from './bubble/Integration.vue'; | ||||
| import BubbleLocation from './bubble/Location'; | ||||
| import BubbleMailHead from './bubble/MailHead'; | ||||
| import BubbleText from './bubble/Text'; | ||||
| import BubbleImage from './bubble/Image'; | ||||
| import BubbleFile from './bubble/File'; | ||||
| import BubbleVideo from './bubble/Video.vue'; | ||||
| import BubbleActions from './bubble/Actions'; | ||||
| import BubbleLocation from './bubble/Location'; | ||||
|  | ||||
| import Spinner from 'shared/components/Spinner'; | ||||
| import ContextMenu from 'dashboard/modules/conversations/components/MessageContextMenu'; | ||||
|  | ||||
| @@ -130,12 +132,13 @@ import { generateBotMessageContent } from './helpers/botMessageContentHelper'; | ||||
| export default { | ||||
|   components: { | ||||
|     BubbleActions, | ||||
|     BubbleText, | ||||
|     BubbleImage, | ||||
|     BubbleFile, | ||||
|     BubbleVideo, | ||||
|     BubbleMailHead, | ||||
|     BubbleImage, | ||||
|     BubbleIntegration, | ||||
|     BubbleLocation, | ||||
|     BubbleMailHead, | ||||
|     BubbleText, | ||||
|     BubbleVideo, | ||||
|     ContextMenu, | ||||
|     Spinner, | ||||
|   }, | ||||
| @@ -169,6 +172,14 @@ export default { | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     shouldRenderMessage() { | ||||
|       return ( | ||||
|         this.hasAttachments || | ||||
|         this.data.content || | ||||
|         this.isEmailContentType || | ||||
|         this.isAnIntegrationMessage | ||||
|       ); | ||||
|     }, | ||||
|     emailMessageContent() { | ||||
|       const { | ||||
|         html_content: { full: fullHTMLContent } = {}, | ||||
| @@ -274,6 +285,9 @@ export default { | ||||
|     isTemplate() { | ||||
|       return this.data.message_type === MESSAGE_TYPE.TEMPLATE; | ||||
|     }, | ||||
|     isAnIntegrationMessage() { | ||||
|       return this.contentType === 'integrations'; | ||||
|     }, | ||||
|     emailHeadAttributes() { | ||||
|       return { | ||||
|         email: this.contentAttributes.email, | ||||
|   | ||||
| @@ -96,25 +96,26 @@ | ||||
|       </p> | ||||
|     </div> | ||||
|     <reply-bottom-panel | ||||
|       :mode="replyType" | ||||
|       :inbox="inbox" | ||||
|       :send-button-text="replyButtonLabel" | ||||
|       :on-file-upload="onFileUpload" | ||||
|       :show-file-upload="showFileUpload" | ||||
|       :show-audio-recorder="showAudioRecorder" | ||||
|       :toggle-emoji-picker="toggleEmojiPicker" | ||||
|       :toggle-audio-recorder="toggleAudioRecorder" | ||||
|       :toggle-audio-recorder-play-pause="toggleAudioRecorderPlayPause" | ||||
|       :show-emoji-picker="showEmojiPicker" | ||||
|       :on-send="onSendReply" | ||||
|       :is-send-disabled="isReplyButtonDisabled" | ||||
|       :recording-audio-duration-text="recordingAudioDurationText" | ||||
|       :recording-audio-state="recordingAudioState" | ||||
|       :is-recording-audio="isRecordingAudio" | ||||
|       :is-on-private-note="isOnPrivateNote" | ||||
|       :show-editor-toggle="isAPIInbox && !isOnPrivateNote" | ||||
|       :conversation-id="conversationId" | ||||
|       :enable-multiple-file-upload="enableMultipleFileUpload" | ||||
|       :has-whatsapp-templates="hasWhatsappTemplates" | ||||
|       :inbox="inbox" | ||||
|       :is-on-private-note="isOnPrivateNote" | ||||
|       :is-recording-audio="isRecordingAudio" | ||||
|       :is-send-disabled="isReplyButtonDisabled" | ||||
|       :mode="replyType" | ||||
|       :on-file-upload="onFileUpload" | ||||
|       :on-send="onSendReply" | ||||
|       :recording-audio-duration-text="recordingAudioDurationText" | ||||
|       :recording-audio-state="recordingAudioState" | ||||
|       :send-button-text="replyButtonLabel" | ||||
|       :show-audio-recorder="showAudioRecorder" | ||||
|       :show-editor-toggle="isAPIInbox && !isOnPrivateNote" | ||||
|       :show-emoji-picker="showEmojiPicker" | ||||
|       :show-file-upload="showFileUpload" | ||||
|       :toggle-audio-recorder-play-pause="toggleAudioRecorderPlayPause" | ||||
|       :toggle-audio-recorder="toggleAudioRecorder" | ||||
|       :toggle-emoji-picker="toggleEmojiPicker" | ||||
|       @selectWhatsappTemplate="openWhatsappTemplateModal" | ||||
|       @toggle-editor="toggleRichContentEditor" | ||||
|     /> | ||||
|   | ||||
| @@ -96,7 +96,9 @@ export default { | ||||
| <style scoped lang="scss"> | ||||
| .mention--box { | ||||
|   background: var(--white); | ||||
|   border-radius: var(--border-radius-normal); | ||||
|   border-top: 1px solid var(--color-border); | ||||
|   box-shadow: var(--shadow-medium); | ||||
|   font-size: var(--font-size-small); | ||||
|   left: 0; | ||||
|   line-height: 1.2; | ||||
|   | ||||
| @@ -0,0 +1,39 @@ | ||||
| <template> | ||||
|   <dyte-video-call | ||||
|     v-if="showDyteIntegration" | ||||
|     :message-id="messageId" | ||||
|     :meeting-data="contentAttributes.data" | ||||
|   /> | ||||
| </template> | ||||
| <script> | ||||
| import DyteVideoCall from './integrations/Dyte.vue'; | ||||
| import inboxMixin from 'shared/mixins/inboxMixin'; | ||||
|  | ||||
| export default { | ||||
|   components: { DyteVideoCall }, | ||||
|   mixins: [inboxMixin], | ||||
|   props: { | ||||
|     messageId: { | ||||
|       type: [String, Number], | ||||
|       default: 0, | ||||
|     }, | ||||
|     contentAttributes: { | ||||
|       type: Object, | ||||
|       default: () => ({}), | ||||
|     }, | ||||
|     inboxId: { | ||||
|       type: [String, Number], | ||||
|       default: 0, | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     showDyteIntegration() { | ||||
|       const isEnabledOnTheInbox = this.isAPIInbox || this.isAWebWidgetInbox; | ||||
|       return isEnabledOnTheInbox && this.contentAttributes.type === 'dyte'; | ||||
|     }, | ||||
|     inbox() { | ||||
|       return this.$store.getters['inboxes/getInbox'](this.inboxId); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| @@ -0,0 +1,103 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <woot-button | ||||
|       size="small" | ||||
|       variant="smooth" | ||||
|       color-scheme="secondary" | ||||
|       icon="video-add" | ||||
|       class="join-call-button" | ||||
|       :is-loading="isLoading" | ||||
|       @click="joinTheCall" | ||||
|     > | ||||
|       {{ $t('INTEGRATION_SETTINGS.DYTE.CLICK_HERE_TO_JOIN') }} | ||||
|     </woot-button> | ||||
|     <div v-if="dyteAuthToken" class="video-call--container"> | ||||
|       <iframe | ||||
|         :src="meetingLink" | ||||
|         allow="camera;microphone;fullscreen;display-capture;picture-in-picture;clipboard-write;" | ||||
|       /> | ||||
|       <woot-button | ||||
|         size="small" | ||||
|         variant="smooth" | ||||
|         color-scheme="secondary" | ||||
|         class="join-call-button" | ||||
|         @click="leaveTheRoom" | ||||
|       > | ||||
|         {{ $t('INTEGRATION_SETTINGS.DYTE.LEAVE_THE_ROOM') }} | ||||
|       </woot-button> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script> | ||||
| import DyteAPI from 'dashboard/api/integrations/dyte'; | ||||
| import { buildDyteURL } from 'shared/helpers/IntegrationHelper'; | ||||
| import alertMixin from 'shared/mixins/alertMixin'; | ||||
|  | ||||
| export default { | ||||
|   mixins: [alertMixin], | ||||
|   props: { | ||||
|     messageId: { | ||||
|       type: Number, | ||||
|       required: true, | ||||
|     }, | ||||
|     meetingData: { | ||||
|       type: Object, | ||||
|       default: () => ({}), | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { isLoading: false, dyteAuthToken: '', isSDKMounted: false }; | ||||
|   }, | ||||
|   computed: { | ||||
|     meetingLink() { | ||||
|       return buildDyteURL(this.meetingData.room_name, this.dyteAuthToken); | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     async joinTheCall() { | ||||
|       this.isLoading = true; | ||||
|       try { | ||||
|         const { | ||||
|           data: { authResponse: { authToken } = {} } = {}, | ||||
|         } = await DyteAPI.addParticipantToMeeting(this.messageId); | ||||
|         this.dyteAuthToken = authToken; | ||||
|       } catch (err) { | ||||
|         this.showAlert(this.$t('INTEGRATION_SETTINGS.DYTE.JOIN_ERROR')); | ||||
|       } finally { | ||||
|         this.isLoading = false; | ||||
|       } | ||||
|     }, | ||||
|     leaveTheRoom() { | ||||
|       this.dyteAuthToken = ''; | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <style lang="scss"> | ||||
| .join-call-button { | ||||
|   margin: var(--space-small) 0; | ||||
| } | ||||
|  | ||||
| .video-call--container { | ||||
|   position: fixed; | ||||
|   bottom: 0; | ||||
|   right: 0; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   z-index: var(--z-index-high); | ||||
|   padding: var(--space-smaller); | ||||
|   background: var(--b-800); | ||||
|  | ||||
|   iframe { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     border: 0; | ||||
|   } | ||||
|  | ||||
|   button { | ||||
|     position: absolute; | ||||
|     top: var(--space-smaller); | ||||
|     right: 16rem; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -70,7 +70,9 @@ export default { | ||||
| .mention--box { | ||||
|   background: var(--white); | ||||
|   border-bottom: var(--space-small) solid var(--white); | ||||
|   border-radius: var(--border-radius-normal); | ||||
|   border-top: 1px solid var(--color-border); | ||||
|   box-shadow: var(--shadow-medium); | ||||
|   left: 0; | ||||
|   max-height: 14rem; | ||||
|   overflow: auto; | ||||
|   | ||||
| @@ -1,23 +1,14 @@ | ||||
| <template> | ||||
|   <modal :show.sync="show" :on-close="cancel"> | ||||
|     <div class="column content-box"> | ||||
|       <woot-modal-header :header-title="title" /> | ||||
|       <div class="row modal-content"> | ||||
|         <div class="medium-12 columns"> | ||||
|           <p> | ||||
|             {{ description }} | ||||
|           </p> | ||||
|         </div> | ||||
|         <div class="modal-footer"> | ||||
|           <div class="medium-12 columns"> | ||||
|             <woot-button @click="confirm"> | ||||
|               {{ confirmLabel }} | ||||
|             </woot-button> | ||||
|             <button class="button clear" @click="cancel"> | ||||
|               {{ cancelLabel }} | ||||
|             </button> | ||||
|           </div> | ||||
|         </div> | ||||
|       <woot-modal-header :header-title="title" :header-content="description" /> | ||||
|       <div class="modal-footer"> | ||||
|         <woot-button variant="clear" @click="cancel"> | ||||
|           {{ cancelLabel }} | ||||
|         </woot-button> | ||||
|         <woot-button @click="confirm"> | ||||
|           {{ confirmLabel }} | ||||
|         </woot-button> | ||||
|       </div> | ||||
|     </div> | ||||
|   </modal> | ||||
| @@ -73,3 +64,8 @@ export default { | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .modal-container .modal-footer { | ||||
|   padding: var(--space-normal) var(--space-medium); | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -23,5 +23,6 @@ export default { | ||||
|   }, | ||||
|   DOCS_URL: '//www.chatwoot.com/docs/product/', | ||||
|   TESTIMONIAL_URL: 'https://testimonials.cdn.chatwoot.com/content.json', | ||||
|   SMALL_SCREEN_BREAKPOINT: 1024, | ||||
| }; | ||||
| export const DEFAULT_REDIRECT_URL = '/app/'; | ||||
|   | ||||
| @@ -97,8 +97,9 @@ export const generateConditionOptions = (options, key = 'id') => { | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| export const getActionOptions = ({ teams, labels, type }) => { | ||||
| export const getActionOptions = ({ agents, teams, labels, type }) => { | ||||
|   const actionsMap = { | ||||
|     assign_agent: agents, | ||||
|     assign_team: teams, | ||||
|     send_email_to_team: teams, | ||||
|     add_label: generateConditionOptions(labels, 'title'), | ||||
| @@ -178,7 +179,7 @@ export const getDefaultConditions = eventName => { | ||||
| export const getDefaultActions = () => { | ||||
|   return [ | ||||
|     { | ||||
|       action_name: 'assign_team', | ||||
|       action_name: 'assign_agent', | ||||
|       action_params: [], | ||||
|     }, | ||||
|   ]; | ||||
| @@ -213,7 +214,7 @@ export const isCustomAttribute = (attrs, key) => { | ||||
|  | ||||
| export const generateCustomAttributes = ( | ||||
|   conversationAttributes = [], | ||||
|   contactAttribtues = [], | ||||
|   contactAttributes = [], | ||||
|   conversationlabel, | ||||
|   contactlabel | ||||
| ) => { | ||||
| @@ -228,14 +229,14 @@ export const generateCustomAttributes = ( | ||||
|       ...conversationAttributes | ||||
|     ); | ||||
|   } | ||||
|   if (contactAttribtues.length) { | ||||
|   if (contactAttributes.length) { | ||||
|     customAttributes.push( | ||||
|       { | ||||
|         key: `contact_custom_attribute`, | ||||
|         name: contactlabel, | ||||
|         disabled: true, | ||||
|       }, | ||||
|       ...contactAttribtues | ||||
|       ...contactAttributes | ||||
|     ); | ||||
|   } | ||||
|   return customAttributes; | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Select an agent bot", | ||||
|       "DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.", | ||||
|       "SUBMIT": "تحديث", | ||||
|       "DISCONNECT": "Disconnect Bot", | ||||
|       "SUCCESS_MESSAGE": "Successfully updated the agent bot", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot", | ||||
|       "ERROR_MESSAGE": "Could not update the agent bot, please try again later", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later", | ||||
|       "SELECT_PLACEHOLDER": "Select Bot" | ||||
|     }, | ||||
|     "ADD": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "تم إضافة سمة مخصصة بنجاح", | ||||
|         "ERROR_MESSAGE": "تعذر إنشاء سمة مخصصة، الرجاء المحاولة مرة أخرى لاحقاً" | ||||
|         "SUCCESS_MESSAGE": "تم إضافة سمة مخصصة بنجاح!", | ||||
|         "ERROR_MESSAGE": "تعذر إنشاء سمة مخصصة، الرجاء المحاولة مرة أخرى لاحقاً." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
|   "BULK_ACTION": { | ||||
|     "CONVERSATIONS_SELECTED": "%{conversationCount} المحادثات المحددة", | ||||
|     "AGENT_SELECT_LABEL": "اختر وكيل", | ||||
|     "ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign %{conversationCount} %{conversationLabel} to", | ||||
|     "UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign %{conversationCount} %{conversationLabel}?", | ||||
|     "ASSIGN_CONFIRMATION_LABEL": "هل أنت متأكد من أنك تريد تعيين %{conversationCount} %{conversationLabel} إلى", | ||||
|     "UNASSIGN_CONFIRMATION_LABEL": "هل أنت متأكد من إلغاء تعيين %{conversationCount} %{conversationLabel}؟", | ||||
|     "GO_BACK_LABEL": "العودة للخلف", | ||||
|     "ASSIGN_LABEL": "تكليف", | ||||
|     "YES": "نعم", | ||||
| @@ -31,10 +31,10 @@ | ||||
|     "TEAMS": { | ||||
|       "TEAM_SELECT_LABEL": "اختيار فريق", | ||||
|       "NONE": "لا شيء", | ||||
|       "NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.", | ||||
|       "ASSIGN_SELECTED_TEAMS": "Assign selected team", | ||||
|       "ASSIGN_SUCCESFUL": "Teams assiged successfully", | ||||
|       "ASSIGN_FAILED": "Failed to assign team, please try again" | ||||
|       "NO_TEAMS_AVAILABLE": "لا توجد فرق مضافة إلى هذا الحساب حتى الآن.", | ||||
|       "ASSIGN_SELECTED_TEAMS": "تعيين فريق محدد", | ||||
|       "ASSIGN_SUCCESFUL": "تم تعيين الفرق بنجاح", | ||||
|       "ASSIGN_FAILED": "فشل تعيين الفريق، الرجاء المحاولة مرة أخرى" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -57,8 +57,8 @@ | ||||
|     "REPLY_TO_TWEET": "الرد على هذه التغريدة", | ||||
|     "LINK_TO_STORY": "الذهاب إلى قصة الإنستقرام", | ||||
|     "SENT": "Sent successfully", | ||||
|     "READ": "Read successfully", | ||||
|     "DELIVERED": "Delivered successfully", | ||||
|     "READ": "تمت القراءة بنجاح", | ||||
|     "DELIVERED": "تم الإرسال بنجاح", | ||||
|     "NO_MESSAGES": "لا توجد رسائل", | ||||
|     "NO_CONTENT": "لم يتم العثور على محتوى", | ||||
|     "HIDE_QUOTED_TEXT": "Hide Quoted Text", | ||||
|   | ||||
| @@ -214,7 +214,7 @@ | ||||
|       "CONVERSATION_INFO": "معلومات المحادثة", | ||||
|       "CONTACT_ATTRIBUTES": "سمات جهة الاتصال", | ||||
|       "PREVIOUS_CONVERSATION": "المحادثات السابقة", | ||||
|       "MACROS": "Macros" | ||||
|       "MACROS": "ماكروس" | ||||
|     } | ||||
|   }, | ||||
|   "CONVERSATION_CUSTOM_ATTRIBUTES": { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "EMOJI": { | ||||
|     "PLACEHOLDER": "Search emojis", | ||||
|     "NOT_FOUND": "No emoji match your search" | ||||
|     "PLACEHOLDER": "ابحث في الايموجي", | ||||
|     "NOT_FOUND": "لا يوجد إيموجي يطابق بحثك" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -23,7 +23,7 @@ | ||||
|         "ERROR": "الرجاء إدخال اسم حساب صحيح" | ||||
|       }, | ||||
|       "LANGUAGE": { | ||||
|         "LABEL": "Site language", | ||||
|         "LABEL": "لغة الموقع", | ||||
|         "PLACEHOLDER": "اسم الحساب الخاص بك", | ||||
|         "ERROR": "" | ||||
|       }, | ||||
| @@ -55,7 +55,7 @@ | ||||
|       "ENTER_TO_SELECT": "اضغط على زر الإدخال للاختيار", | ||||
|       "ENTER_TO_REMOVE": "اضغط على زر الإدخال للحذف", | ||||
|       "SELECT_ONE": "اختر واحدا", | ||||
|       "SELECT": "Select" | ||||
|       "SELECT": "اختر" | ||||
|     } | ||||
|   }, | ||||
|   "NOTIFICATIONS_PAGE": { | ||||
| @@ -139,6 +139,6 @@ | ||||
|     } | ||||
|   }, | ||||
|   "DASHBOARD_APPS": { | ||||
|     "LOADING_MESSAGE": "Loading Dashboard App..." | ||||
|     "LOADING_MESSAGE": "تحميل تطبيق لوحة التحكم..." | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -53,6 +53,10 @@ | ||||
|           "ENABLE": "إنشاء محادثات من التغريدات المشار إليها" | ||||
|         } | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       }, | ||||
|       "WEBSITE_CHANNEL": { | ||||
|         "TITLE": "قناة الموقع", | ||||
|         "DESC": "قم بإنشاء قناة تواصل لموقع الويب الخاص بك وابدأ في استقبال الرسائل من عملائك عبر صندوق الدردشة المباشرة.", | ||||
| @@ -134,7 +138,7 @@ | ||||
|         "PHONE_NUMBER": { | ||||
|           "LABEL": "رقم الهاتف", | ||||
|           "PLACEHOLDER": "الرجاء إدخال رقم الهاتف الذي سيتم إرسال الرسائل منه.", | ||||
|           "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." | ||||
|           "ERROR": "الرجاء تقديم رقم هاتف صالح يبدأ بإشارة '+' ولا يحتوي على أي مسافات." | ||||
|         }, | ||||
|         "API_CALLBACK": { | ||||
|           "TITLE": "عنوان Callback URL", | ||||
| @@ -185,7 +189,7 @@ | ||||
|           "PHONE_NUMBER": { | ||||
|             "LABEL": "رقم الهاتف", | ||||
|             "PLACEHOLDER": "الرجاء إدخال رقم الهاتف الذي سيتم إرسال الرسائل منه.", | ||||
|             "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." | ||||
|             "ERROR": "الرجاء تقديم رقم هاتف صالح يبدأ بإشارة '+' ولا يحتوي على أي مسافات." | ||||
|           }, | ||||
|           "SUBMIT_BUTTON": "إنشاء قناة عرض التردد", | ||||
|           "API": { | ||||
| @@ -214,7 +218,7 @@ | ||||
|         "PHONE_NUMBER": { | ||||
|           "LABEL": "رقم الهاتف", | ||||
|           "PLACEHOLDER": "الرجاء إدخال رقم الهاتف الذي سيتم إرسال الرسائل منه.", | ||||
|           "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." | ||||
|           "ERROR": "الرجاء تقديم رقم هاتف صالح يبدأ بإشارة '+' ولا يحتوي على أي مسافات." | ||||
|         }, | ||||
|         "PHONE_NUMBER_ID": { | ||||
|           "LABEL": "رقم الهاتف", | ||||
| @@ -239,9 +243,9 @@ | ||||
|         }, | ||||
|         "API_CALLBACK": { | ||||
|           "TITLE": "عنوان Callback URL", | ||||
|           "SUBTITLE": "You have to configure the webhook URL and the verification token in the Facebook Developer portal with the values shown below.", | ||||
|           "SUBTITLE": "يجب عليك تكوين رابط الويب هوك و رمز التحقق في بوابة مطور الفيسبوك مع القيم الموضحة أدناه.", | ||||
|           "WEBHOOK_URL": "رابط Webhook", | ||||
|           "WEBHOOK_VERIFICATION_TOKEN": "Webhook Verification Token" | ||||
|           "WEBHOOK_VERIFICATION_TOKEN": "رمز التحقق من Webhook" | ||||
|         }, | ||||
|         "SUBMIT_BUTTON": "إنشاء قناة واتساب", | ||||
|         "API": { | ||||
| @@ -421,7 +425,7 @@ | ||||
|       "PRE_CHAT_FORM": "نموذج ما قبل الدردشة", | ||||
|       "BUSINESS_HOURS": "ساعات العمل", | ||||
|       "WIDGET_BUILDER": "منشئ اللايف شات", | ||||
|       "BOT_CONFIGURATION": "Bot Configuration" | ||||
|       "BOT_CONFIGURATION": "اعدادات البوت" | ||||
|     }, | ||||
|     "SETTINGS": "الإعدادات", | ||||
|     "FEATURES": { | ||||
| @@ -445,8 +449,8 @@ | ||||
|       "ENABLE_CSAT_SUB_TEXT": "تمكين/تعطيل تقييم خدمة العملاء بعد إنتهاء المحادثة", | ||||
|       "ENABLE_CONTINUITY_VIA_EMAIL": "تمكين استمرارية المحادثة عبر البريد الإلكتروني", | ||||
|       "ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT": "المحادثات ستستمر عبر البريد الإلكتروني إذا كان عنوان البريد الإلكتروني لجهة الاتصال متاحاً.", | ||||
|       "LOCK_TO_SINGLE_CONVERSATION": "Lock to single conversation", | ||||
|       "LOCK_TO_SINGLE_CONVERSATION_SUB_TEXT": "Enable or disable multiple conversations for the same contact in this inbox", | ||||
|       "LOCK_TO_SINGLE_CONVERSATION": "قفل إلى محادثة واحدة", | ||||
|       "LOCK_TO_SINGLE_CONVERSATION_SUB_TEXT": "تمكين أو تعطيل محادثات متعددة لنفس جهة الاتصال في هذا البريد الوارد", | ||||
|       "INBOX_UPDATE_TITLE": "إعدادات قناة التواصل", | ||||
|       "INBOX_UPDATE_SUB_TEXT": "تحديث إعدادات قناة التواصل", | ||||
|       "AUTO_ASSIGNMENT_SUB_TEXT": "تمكين أو تعطيل الإسناد التلقائي للمحادثات الجديدة إلى الموظفين المضافين إلى قناة التواصل هذه.", | ||||
| @@ -548,6 +552,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "تمكين SSL" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "SMTP", | ||||
|       "SUBTITLE": "تعيين تفاصيل IMAP الخاصة بك", | ||||
|   | ||||
| @@ -76,6 +76,13 @@ | ||||
|         "BODY": "<br/><p>ستتم الآن مزامنة جميع المحادثات الواردة إلى <b><i>محادثة الزبائن</i></b> داخل مكان العمل الخاص بك.</p><p>الرد على موضوع محادثة في <b><i>محادثة العملاء</i></b> قناة الركود سوف تقوم بإنشاء رد على العميل من خلال المحادثة.</p><p>ابدأ الردود ب <b><i>ملاحظة:</i></b> لإنشاء ملاحظات خاصة بدلاً من الردود.</p><p>إذا كان للرد على slack ملف تعريف الوكيل في الدردشة تحت نفس البريد الإلكتروني، فسيتم ربط الردود وفقا لذلك.</p><p>عندما لا يكون للرد ملف شخصي للوكيل المرتبط، ستتم الردود من ملف بوت الشخصي.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Click here to join", | ||||
|       "LEAVE_THE_ROOM": "Leave the room", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Start a new video call with the customer", | ||||
|       "JOIN_ERROR": "There was an error joining the call, please try again", | ||||
|       "CREATE_ERROR": "There was an error creating a meeting link, please try again" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "حذف", | ||||
|       "API": { | ||||
|   | ||||
| @@ -1,78 +1,78 @@ | ||||
| { | ||||
|   "MACROS": { | ||||
|     "HEADER": "Macros", | ||||
|     "HEADER_BTN_TXT": "Add a new macro", | ||||
|     "HEADER_BTN_TXT_SAVE": "Save macro", | ||||
|     "LOADING": "Fetching macros", | ||||
|     "SIDEBAR_TXT": "<p><b>Macros</b><p>A macro is a set of saved actions that help customer service agents easily complete tasks. The agents can define a set of actions like tagging a conversation with a label, sending an email transcript, updating a custom attribute, etc., and they can run these actions in a single click. When the agents run the macro, the actions would be performed sequentially in the order they are defined. Macros improve productivity and increase consistency in actions. </p><p>A macro can be helpful in 2 ways. </p><p><b>As an agent assist:</b> If an agent performs a set of actions multiple times, they can save it as a macro and execute all the actions together using a single click.</p><p><b>As an option to onboard a team member:</b> Every agent has to perform many different checks/actions during each conversation. Onboarding a new support team member will be easy if pre-defined macros are available on the account. Instead of describing each step in detail, the manager/team lead can point to the macros used in different scenarios.</p>", | ||||
|     "ERROR": "Something went wrong. Please try again", | ||||
|     "ORDER_INFO": "Macros will run in the order you add your actions. You can rearrange them by dragging them by the handle beside each node.", | ||||
|     "HEADER": "ماكروس", | ||||
|     "HEADER_BTN_TXT": "إضافة ماكرو جديد", | ||||
|     "HEADER_BTN_TXT_SAVE": "حفظ الماكرو", | ||||
|     "LOADING": "جاري جلب الماكروس", | ||||
|     "SIDEBAR_TXT": "<p><b>الماكروس</b><p>الماكرو هو مجموعة من الإجراءات المحفوظة التي تساعد وكلاء خدمة العملاء على إكمال المهام بسهولة. يمكن للوكلاء تحديد مجموعة من الإجراءات مثل وضع علامة على محادثة مع تسمية، وإرسال نص بريد إلكتروني، وتحديث سمة مخصصة، إلخ. ويمكنهم تنفيذ هذه الإجراءات بنقرة واحدة. وعندما يدير الوكلاء الكلية، يتم تنفيذ الإجراءات بالتسلسل حسب الترتيب الذي تحدده. يحسن الماكرو الإنتاجية ويزيد الاتساق في الإجراءات. </p><p>يمكن أن يكون الماكرو مفيداً بطريقتين. </p><p><b>كوكيل يساعد:</b> إذا قام وكيل بمجموعة من الإجراءات عدة مرات، يمكنهم حفظه كماكلي وتنفيذ جميع الإجراءات معاً باستخدام نقرة واحدة.</p><p><b>كخيار للدخول إلى عضوية الفريق:</b> يجب على كل وكيل إجراء العديد من الشيكات والإجراءات المختلفة خلال كل محادثة. سيكون عضو فريق الدعم الجديد من أونبواردينغ سهلا إذا كان الماكرو المحدد مسبقا متاحا على الحساب. وبدلا من وصف كل خطوة بالتفصيل، يمكن للمدير/الفريق أن يشير إلى الكتلة الكلية المستخدمة في سيناريوهات مختلفة.</p>", | ||||
|     "ERROR": "حدث خطأ ما. الرجاء المحاولة مرة أخرى", | ||||
|     "ORDER_INFO": "سيتم تشغيل الماكرو بالترتيب الذي تضيفه إجراءاتك. يمكنك إعادة ترتيبهم بسحبهم بواسطة المعالج بجانب كل عقدة.", | ||||
|     "ADD": { | ||||
|       "FORM": { | ||||
|         "NAME": { | ||||
|           "LABEL": "Macro name", | ||||
|           "PLACEHOLDER": "Enter a name for your macro", | ||||
|           "ERROR": "Name is required for creating a macro" | ||||
|           "LABEL": "اسم الماكرو", | ||||
|           "PLACEHOLDER": "أدخل اسم الماكرو الخاص بك", | ||||
|           "ERROR": "الاسم مطلوب لإنشاء الماكرو" | ||||
|         }, | ||||
|         "ACTIONS": { | ||||
|           "LABEL": "الإجراءات" | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Macro added successfully", | ||||
|         "ERROR_MESSAGE": "Unable to create macro, Please try again later" | ||||
|         "SUCCESS_MESSAGE": "تمت إضافة الماكرو بنجاح", | ||||
|         "ERROR_MESSAGE": "غير قادر على إنشاء الماكرو ، الرجاء المحاولة مرة أخرى لاحقاً" | ||||
|       } | ||||
|     }, | ||||
|     "LIST": { | ||||
|       "TABLE_HEADER": [ | ||||
|         "الاسم", | ||||
|         "Created by", | ||||
|         "Last updated by", | ||||
|         "Visibility" | ||||
|         "تم إنشاؤها بواسطة", | ||||
|         "آخر تحديث بواسطة", | ||||
|         "الظهور" | ||||
|       ], | ||||
|       "404": "No macros found" | ||||
|       "404": "لم يتم العثور على الماكروس" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "TOOLTIP": "Delete macro", | ||||
|       "TOOLTIP": "حذف الماكرو", | ||||
|       "CONFIRM": { | ||||
|         "MESSAGE": "هل أنت متأكد من الحذف ", | ||||
|         "YES": "نعم، احذف", | ||||
|         "NO": "لا" | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Macro deleted successfully", | ||||
|         "ERROR_MESSAGE": "There was an error deleting the macro. Please try again later" | ||||
|         "SUCCESS_MESSAGE": "تم حذف الماكرو بنجاح", | ||||
|         "ERROR_MESSAGE": "حدث خطأ أثناء حذف الماكرو. الرجاء المحاولة مرة أخرى في وقت لاحق" | ||||
|       } | ||||
|     }, | ||||
|     "EDIT": { | ||||
|       "TOOLTIP": "Edit macro", | ||||
|       "TOOLTIP": "تعديل الماكرو", | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Macro updated successfully", | ||||
|         "ERROR_MESSAGE": "Could not update Macro, Please try again later" | ||||
|         "SUCCESS_MESSAGE": "تم تحديث الماكرو بنجاح", | ||||
|         "ERROR_MESSAGE": "تعذر تحديث الماكرو ، الرجاء المحاولة مرة أخرى لاحقاً" | ||||
|       } | ||||
|     }, | ||||
|     "EDITOR": { | ||||
|       "START_FLOW": "Start Flow", | ||||
|       "END_FLOW": "End Flow", | ||||
|       "LOADING": "Fetching macro", | ||||
|       "ADD_BTN_TOOLTIP": "Add new action", | ||||
|       "DELETE_BTN_TOOLTIP": "Delete Action", | ||||
|       "START_FLOW": "بداية الإجرائات", | ||||
|       "END_FLOW": "نهاية الإجرائات", | ||||
|       "LOADING": "جاري جلب الماكروس", | ||||
|       "ADD_BTN_TOOLTIP": "إضافة إجراء جديد", | ||||
|       "DELETE_BTN_TOOLTIP": "حذف الإجراء", | ||||
|       "VISIBILITY": { | ||||
|         "LABEL": "Macro Visibility", | ||||
|         "LABEL": "الرؤية الخاصة بالماكرو", | ||||
|         "GLOBAL": { | ||||
|           "LABEL": "Public", | ||||
|           "DESCRIPTION": "This macro is available publicly for all agents in this account." | ||||
|           "LABEL": "عامة", | ||||
|           "DESCRIPTION": "هذا الماكرو متاح بشكل عام لجميع الوكلاء في هذا الحساب." | ||||
|         }, | ||||
|         "PERSONAL": { | ||||
|           "LABEL": "Private", | ||||
|           "DESCRIPTION": "This macro will be private to you and not be available to others." | ||||
|           "LABEL": "خاص", | ||||
|           "DESCRIPTION": "هذا الماكرو سيكون خاصا لك ولن يكون متاحا للآخرين." | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "EXECUTE": { | ||||
|       "BUTTON_TOOLTIP": "Execute", | ||||
|       "PREVIEW": "Preview Macro", | ||||
|       "EXECUTED_SUCCESSFULLY": "Macro executed successfully" | ||||
|       "BUTTON_TOOLTIP": "تنفيذ", | ||||
|       "PREVIEW": "معاينة الماكرو", | ||||
|       "EXECUTED_SUCCESSFULLY": "تم تنفيذ الماكرو بنجاح" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -59,18 +59,18 @@ | ||||
|         "TITLE": "الإشعارات الصوتية", | ||||
|         "NOTE": "تمكين التنبيهات الصوتية في لوحة التحكم للرسائل والمحادثات الجديدة.", | ||||
|         "ALERT_TYPE": { | ||||
|           "TITLE": "Alert events:", | ||||
|           "TITLE": "أحداث التنبيه:", | ||||
|           "NONE": "لا شيء", | ||||
|           "ASSIGNED": "المحادثات المسندة", | ||||
|           "ALL_CONVERSATIONS": "كل المحادثات" | ||||
|         }, | ||||
|         "DEFAULT_TONE": { | ||||
|           "TITLE": "Alert tone:" | ||||
|           "TITLE": "نغمة التنبيه:" | ||||
|         }, | ||||
|         "CONDITIONS": { | ||||
|           "TITLE": "Alert conditions:", | ||||
|           "CONDITION_ONE": "Send audio alerts only if the browser window is not active", | ||||
|           "CONDITION_TWO": "Send alerts every 30s until all the assigned conversations are read" | ||||
|           "TITLE": "شروط التنبيه:", | ||||
|           "CONDITION_ONE": "إرسال تنبيهات صوتية فقط إذا كانت نافذة المتصفح غير نشطة", | ||||
|           "CONDITION_TWO": "إرسال تنبيهات كل 30 ثانية حتى يتم قراءة جميع المحادثات المعينة" | ||||
|         } | ||||
|       }, | ||||
|       "EMAIL_NOTIFICATIONS_SECTION": { | ||||
| @@ -115,8 +115,8 @@ | ||||
|           "مشغول", | ||||
|           "غير متصل" | ||||
|         ], | ||||
|         "SET_AVAILABILITY_SUCCESS": "Availability has been set successfully", | ||||
|         "SET_AVAILABILITY_ERROR": "Couldn't set availability, please try again" | ||||
|         "SET_AVAILABILITY_SUCCESS": "تم تعيين التوافر بنجاح", | ||||
|         "SET_AVAILABILITY_ERROR": "تعذر تعيين التوافر، الرجاء المحاولة مرة أخرى" | ||||
|       }, | ||||
|       "EMAIL": { | ||||
|         "LABEL": "عنوان البريد الإلكتروني الخاص بك", | ||||
| @@ -147,7 +147,7 @@ | ||||
|     "SELECTOR_SUBTITLE": "اختر حساباً من القائمة التالية", | ||||
|     "PROFILE_SETTINGS": "إعدادات الملف الشخصي", | ||||
|     "KEYBOARD_SHORTCUTS": "اختصارات لوحة المفاتيح", | ||||
|     "SUPER_ADMIN_CONSOLE": "Super Admin Console", | ||||
|     "SUPER_ADMIN_CONSOLE": "وحدة تحكم المدير المتميز", | ||||
|     "LOGOUT": "تسجيل الخروج" | ||||
|   }, | ||||
|   "APP_GLOBAL": { | ||||
| @@ -173,7 +173,7 @@ | ||||
|       "UPLOADING": "جاري الرفع..." | ||||
|     }, | ||||
|     "LOCATION_BUBBLE": { | ||||
|       "SEE_ON_MAP": "See on map" | ||||
|       "SEE_ON_MAP": "مشاهدة على الخريطة" | ||||
|     }, | ||||
|     "FORM_BUBBLE": { | ||||
|       "SUBMIT": "إرسال" | ||||
| @@ -197,7 +197,7 @@ | ||||
|     "CONTACTS": "جهات الاتصال", | ||||
|     "HOME": "الرئيسية", | ||||
|     "AGENTS": "موظف الدعم", | ||||
|     "AGENT_BOTS": "Bots", | ||||
|     "AGENT_BOTS": "البوتات", | ||||
|     "INBOXES": "قنوات التواصل", | ||||
|     "NOTIFICATIONS": "الإشعارات", | ||||
|     "CANNED_RESPONSES": "الردود السريعة", | ||||
| @@ -208,7 +208,7 @@ | ||||
|     "LABELS": "الوسوم", | ||||
|     "CUSTOM_ATTRIBUTES": "سمات مخصصة", | ||||
|     "AUTOMATION": "الأتمتة", | ||||
|     "MACROS": "Macros", | ||||
|     "MACROS": "ماكروس", | ||||
|     "TEAMS": "الفرق", | ||||
|     "BILLING": "الفواتير", | ||||
|     "CUSTOM_VIEWS_FOLDER": "المجلدات", | ||||
| @@ -241,8 +241,8 @@ | ||||
|       "CATEGORY_EMPTY_MESSAGE": "لم يتم العثور على فئات" | ||||
|     }, | ||||
|     "SET_AUTO_OFFLINE": { | ||||
|       "TEXT": "Mark offline automatically", | ||||
|       "INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard." | ||||
|       "TEXT": "وضع علامة غير متصل تلقائيا", | ||||
|       "INFO_TEXT": "السماح للنظام بوضع علامة  غير متصل أوتوماتيكياً  عندما لا تستخدم التطبيق أو لوحة التحكم." | ||||
|     }, | ||||
|     "DOCS": "قراءة المستندات" | ||||
|   }, | ||||
| @@ -282,6 +282,7 @@ | ||||
|     } | ||||
|   }, | ||||
|   "KEYBOARD_SHORTCUTS": { | ||||
|     "TOGGLE_MODAL": "عرض جميع الاختصارات", | ||||
|     "TITLE": { | ||||
|       "OPEN_CONVERSATION": "فتح المحادثة", | ||||
|       "RESOLVE_AND_NEXT": "حل وانتقل إلى التالي", | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "REGISTER": { | ||||
|     "TRY_WOOT": "تسجيل حساب", | ||||
|     "TITLE": "تسجيل", | ||||
|     "TESTIMONIAL_HEADER": "All it takes is one step to move forward", | ||||
|     "TESTIMONIAL_HEADER": "إن كل ما يلزم هو خطوة واحدة للمضي قدما", | ||||
|     "TESTIMONIAL_CONTENT": "You're one step away from engaging your customers, retaining them and finding new ones.", | ||||
|     "TERMS_ACCEPT": "من خلال التسجيل، فإنك توافق على <a href=\"https://www.chatwoot.com/terms\">شروط الخدمة</a> و <a href=\"https://www.chatwoot.com/privacy-policy\">سياسة الخصوصية</a>", | ||||
|     "COMPANY_NAME": { | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Select an agent bot", | ||||
|       "DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.", | ||||
|       "SUBMIT": "Обновяване", | ||||
|       "DISCONNECT": "Disconnect Bot", | ||||
|       "SUCCESS_MESSAGE": "Successfully updated the agent bot", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot", | ||||
|       "ERROR_MESSAGE": "Could not update the agent bot, please try again later", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later", | ||||
|       "SELECT_PLACEHOLDER": "Select Bot" | ||||
|     }, | ||||
|     "ADD": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Custom Attribute added successfully", | ||||
|         "ERROR_MESSAGE": "Could not able to create a custom attribute, Please try again later" | ||||
|         "SUCCESS_MESSAGE": "Custom Attribute added successfully!", | ||||
|         "ERROR_MESSAGE": "Could not create a Custom Attribute. Please try again later." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -53,6 +53,10 @@ | ||||
|           "ENABLE": "Create conversations from mentioned Tweets" | ||||
|         } | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       }, | ||||
|       "WEBSITE_CHANNEL": { | ||||
|         "TITLE": "Website channel", | ||||
|         "DESC": "Create a channel for your website and start supporting your customers via our website widget.", | ||||
| @@ -548,6 +552,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "Enable SSL" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "SMTP", | ||||
|       "SUBTITLE": "Set your SMTP details", | ||||
|   | ||||
| @@ -76,6 +76,13 @@ | ||||
|         "BODY": "<br/><p>Chatwoot will now sync all the incoming conversations into the <b><i>customer-conversations</i></b> channel inside your slack workplace.</p><p>Replying to a conversation thread in <b><i>customer-conversations</i></b> slack channel will create a response back to the customer through chatwoot.</p><p>Start the replies with <b><i>note:</i></b> to create private notes instead of replies.</p><p>If the replier on slack has an agent profile in chatwoot under the same email, the replies will be associated accordingly.</p><p>When the replier doesn't have an associated agent profile, the replies will be made from the bot profile.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Click here to join", | ||||
|       "LEAVE_THE_ROOM": "Leave the room", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Start a new video call with the customer", | ||||
|       "JOIN_ERROR": "There was an error joining the call, please try again", | ||||
|       "CREATE_ERROR": "There was an error creating a meeting link, please try again" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Изтрий", | ||||
|       "API": { | ||||
|   | ||||
| @@ -282,6 +282,7 @@ | ||||
|     } | ||||
|   }, | ||||
|   "KEYBOARD_SHORTCUTS": { | ||||
|     "TOGGLE_MODAL": "View all shortcuts", | ||||
|     "TITLE": { | ||||
|       "OPEN_CONVERSATION": "Open conversation", | ||||
|       "RESOLVE_AND_NEXT": "Resolve and move to next", | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Select an agent bot", | ||||
|       "DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.", | ||||
|       "SUBMIT": "Actualitza", | ||||
|       "DISCONNECT": "Disconnect Bot", | ||||
|       "SUCCESS_MESSAGE": "Successfully updated the agent bot", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot", | ||||
|       "ERROR_MESSAGE": "Could not update the agent bot, please try again later", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later", | ||||
|       "SELECT_PLACEHOLDER": "Select Bot" | ||||
|     }, | ||||
|     "ADD": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Custom Attribute added successfully", | ||||
|         "ERROR_MESSAGE": "Could not able to create a custom attribute, Please try again later" | ||||
|         "SUCCESS_MESSAGE": "Custom Attribute added successfully!", | ||||
|         "ERROR_MESSAGE": "Could not create a Custom Attribute. Please try again later." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -53,6 +53,10 @@ | ||||
|           "ENABLE": "Create conversations from mentioned Tweets" | ||||
|         } | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       }, | ||||
|       "WEBSITE_CHANNEL": { | ||||
|         "TITLE": "Canal Web", | ||||
|         "DESC": "Crea un canal per al vostre lloc web i comença a donar suport als vostres clients mitjançant el teu widget del lloc web.", | ||||
| @@ -548,6 +552,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "Enable SSL" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "SMTP", | ||||
|       "SUBTITLE": "Set your SMTP details", | ||||
|   | ||||
| @@ -76,6 +76,13 @@ | ||||
|         "BODY": "<br/><p>Chatwoot will now sync all the incoming conversations into the <b><i>customer-conversations</i></b> channel inside your slack workplace.</p><p>Replying to a conversation thread in <b><i>customer-conversations</i></b> slack channel will create a response back to the customer through chatwoot.</p><p>Start the replies with <b><i>note:</i></b> to create private notes instead of replies.</p><p>If the replier on slack has an agent profile in chatwoot under the same email, the replies will be associated accordingly.</p><p>When the replier doesn't have an associated agent profile, the replies will be made from the bot profile.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Click here to join", | ||||
|       "LEAVE_THE_ROOM": "Leave the room", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Start a new video call with the customer", | ||||
|       "JOIN_ERROR": "There was an error joining the call, please try again", | ||||
|       "CREATE_ERROR": "There was an error creating a meeting link, please try again" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Suprimeix", | ||||
|       "API": { | ||||
|   | ||||
| @@ -282,6 +282,7 @@ | ||||
|     } | ||||
|   }, | ||||
|   "KEYBOARD_SHORTCUTS": { | ||||
|     "TOGGLE_MODAL": "View all shortcuts", | ||||
|     "TITLE": { | ||||
|       "OPEN_CONVERSATION": "Open conversation", | ||||
|       "RESOLVE_AND_NEXT": "Resolve and move to next", | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Select an agent bot", | ||||
|       "DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.", | ||||
|       "SUBMIT": "Aktualizovat", | ||||
|       "DISCONNECT": "Disconnect Bot", | ||||
|       "SUCCESS_MESSAGE": "Successfully updated the agent bot", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot", | ||||
|       "ERROR_MESSAGE": "Could not update the agent bot, please try again later", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later", | ||||
|       "SELECT_PLACEHOLDER": "Select Bot" | ||||
|     }, | ||||
|     "ADD": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Custom Attribute added successfully", | ||||
|         "ERROR_MESSAGE": "Could not able to create a custom attribute, Please try again later" | ||||
|         "SUCCESS_MESSAGE": "Custom Attribute added successfully!", | ||||
|         "ERROR_MESSAGE": "Could not create a Custom Attribute. Please try again later." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -53,6 +53,10 @@ | ||||
|           "ENABLE": "Create conversations from mentioned Tweets" | ||||
|         } | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       }, | ||||
|       "WEBSITE_CHANNEL": { | ||||
|         "TITLE": "Kanál webové stránky", | ||||
|         "DESC": "Vytvořte si kanál pro vaše webové stránky a začněte podporovat své zákazníky prostřednictvím našeho widgetu.", | ||||
| @@ -548,6 +552,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "Enable SSL" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "SMTP", | ||||
|       "SUBTITLE": "Set your SMTP details", | ||||
|   | ||||
| @@ -76,6 +76,13 @@ | ||||
|         "BODY": "<br/><p>Chatwoot will now sync all the incoming conversations into the <b><i>customer-conversations</i></b> channel inside your slack workplace.</p><p>Replying to a conversation thread in <b><i>customer-conversations</i></b> slack channel will create a response back to the customer through chatwoot.</p><p>Start the replies with <b><i>note:</i></b> to create private notes instead of replies.</p><p>If the replier on slack has an agent profile in chatwoot under the same email, the replies will be associated accordingly.</p><p>When the replier doesn't have an associated agent profile, the replies will be made from the bot profile.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Click here to join", | ||||
|       "LEAVE_THE_ROOM": "Leave the room", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Start a new video call with the customer", | ||||
|       "JOIN_ERROR": "There was an error joining the call, please try again", | ||||
|       "CREATE_ERROR": "There was an error creating a meeting link, please try again" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Vymazat", | ||||
|       "API": { | ||||
|   | ||||
| @@ -282,6 +282,7 @@ | ||||
|     } | ||||
|   }, | ||||
|   "KEYBOARD_SHORTCUTS": { | ||||
|     "TOGGLE_MODAL": "View all shortcuts", | ||||
|     "TITLE": { | ||||
|       "OPEN_CONVERSATION": "Open conversation", | ||||
|       "RESOLVE_AND_NEXT": "Resolve and move to next", | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Select an agent bot", | ||||
|       "DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.", | ||||
|       "SUBMIT": "Opdater", | ||||
|       "DISCONNECT": "Disconnect Bot", | ||||
|       "SUCCESS_MESSAGE": "Successfully updated the agent bot", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot", | ||||
|       "ERROR_MESSAGE": "Could not update the agent bot, please try again later", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later", | ||||
|       "SELECT_PLACEHOLDER": "Vælg bot" | ||||
|     }, | ||||
|     "ADD": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Brugerdefineret attribut blev tilføjet", | ||||
|         "ERROR_MESSAGE": "Kunne ikke oprette en brugerdefineret attribut. Prøv igen senere" | ||||
|         "SUCCESS_MESSAGE": "Brugerdefineret attribut blev tilføjet!", | ||||
|         "ERROR_MESSAGE": "Could not create a Custom Attribute. Please try again later." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -217,14 +217,14 @@ | ||||
|         "DOMAIN": { | ||||
|           "LABEL": "Tilpasset Domæne", | ||||
|           "PLACEHOLDER": "Portal brugerdefineret domæne", | ||||
|           "HELP_TEXT": "Add only If you want to use a custom domain for your portals. Eg: https://example.com", | ||||
|           "HELP_TEXT": "Tilføj kun hvis du vil bruge et brugerdefineret domæne, til dine portaler. Eksempelvis https://example.com", | ||||
|           "ERROR": "Indtast et gyldigt domæne URL" | ||||
|         }, | ||||
|         "HOME_PAGE_LINK": { | ||||
|           "LABEL": "Link Til Hjemmeside", | ||||
|           "PLACEHOLDER": "Link til portalens hjemmeside", | ||||
|           "HELP_TEXT": "The link used to return from the portal to the home page. Eg: https://example.com", | ||||
|           "ERROR": "Enter a valid home page URL" | ||||
|           "ERROR": "Indtast en gyldig URL til startsiden" | ||||
|         }, | ||||
|         "THEME_COLOR": { | ||||
|           "LABEL": "Portal tema farve", | ||||
|   | ||||
| @@ -53,6 +53,10 @@ | ||||
|           "ENABLE": "Opret samtaler fra nævnte Tweets" | ||||
|         } | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       }, | ||||
|       "WEBSITE_CHANNEL": { | ||||
|         "TITLE": "Hjemmesidekanal", | ||||
|         "DESC": "Opret en kanal til din hjemmeside og begynde at supporte dine kunder via vores hjemmeside widget.", | ||||
| @@ -548,6 +552,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "Aktiver SSL" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "Smtp", | ||||
|       "SUBTITLE": "Indstil dine IMAP-oplysninger", | ||||
|   | ||||
| @@ -76,6 +76,13 @@ | ||||
|         "BODY": "<br/><p>Chatwoot vil nu synkronisere alle indgående samtaler ind i <b><i>kundesamtaler</i></b> kanalen i din slack arbejdsplads.</p><p>Svar på en samtaletråd i <b><i>kunde-samtaler</i></b> slack kanal vil skabe et svar tilbage til kunden gennem chatwoot.</p><p>Start svarene med <b><i>note:</i></b> for at oprette private noter i stedet for svar.</p><p>Hvis replikatoren på slack har en agentprofil i chatwoot under samme e-mail, vil svarene blive tilknyttet i overensstemmelse hermed.</p><p>Når replikatoren ikke har en tilknyttet agentprofil, vil svarene blive fremsat fra bot-profilen.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Click here to join", | ||||
|       "LEAVE_THE_ROOM": "Leave the room", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Start a new video call with the customer", | ||||
|       "JOIN_ERROR": "There was an error joining the call, please try again", | ||||
|       "CREATE_ERROR": "There was an error creating a meeting link, please try again" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Slet", | ||||
|       "API": { | ||||
|   | ||||
| @@ -282,6 +282,7 @@ | ||||
|     } | ||||
|   }, | ||||
|   "KEYBOARD_SHORTCUTS": { | ||||
|     "TOGGLE_MODAL": "View all shortcuts", | ||||
|     "TITLE": { | ||||
|       "OPEN_CONVERSATION": "Åbn samtale", | ||||
|       "RESOLVE_AND_NEXT": "Løs og flyt til næste", | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Agenten-Bot auswählen", | ||||
|       "DESC": "Sie können einen Agenten-Bot aus der Liste in diesen Posteingang setzen. Der Bot kann die Unterhaltung anfangs bearbeiten und bei Bedarf an einen Agenten übertragen.", | ||||
|       "SUBMIT": "Aktualisieren", | ||||
|       "DISCONNECT": "Disconnect Bot", | ||||
|       "SUCCESS_MESSAGE": "Agenten-Bot erfolgreich aktualisiert", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot", | ||||
|       "ERROR_MESSAGE": "Konnte den Agenten-Bot nicht aktualisieren, bitte versuchen Sie es später erneut", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later", | ||||
|       "SELECT_PLACEHOLDER": "Bot auswählen" | ||||
|     }, | ||||
|     "ADD": { | ||||
| @@ -37,29 +40,29 @@ | ||||
|       } | ||||
|     }, | ||||
|     "LIST": { | ||||
|       "404": "No Bots found, you can create a bot by clicking the 'Configure new bot' Button ↗", | ||||
|       "LOADING": "Fetching Bots...", | ||||
|       "TYPE": "Bot Type" | ||||
|       "404": "Keine Bots gefunden, Sie können einen Bot erstellen, indem Sie auf den 'Neuen Bot konfigurieren' Button klicken ↗", | ||||
|       "LOADING": "Bots werden geladen...", | ||||
|       "TYPE": "Bot-Typ" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Löschen", | ||||
|       "TITLE": "Delete Bot", | ||||
|       "TITLE": "Bot löschen", | ||||
|       "SUBMIT": "Löschen", | ||||
|       "CANCEL_BUTTON_TEXT": "Stornieren", | ||||
|       "DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible", | ||||
|       "DESCRIPTION": "Sind Sie sicher, dass Sie diesen Bot löschen wollen? Diese Aktion kann nicht rückgängig gemacht werden", | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Bot deleted successfully", | ||||
|         "SUCCESS_MESSAGE": "Bot erfolgreich gelöscht", | ||||
|         "ERROR_MESSAGE": "Could not able to delete bot, Please try again later" | ||||
|       } | ||||
|     }, | ||||
|     "EDIT": { | ||||
|       "BUTTON_TEXT": "Bearbeiten", | ||||
|       "LOADING": "Fetching Bots...", | ||||
|       "TITLE": "Edit Bot", | ||||
|       "LOADING": "Bots werden geladen...", | ||||
|       "TITLE": "Bot bearbeiten", | ||||
|       "CANCEL_BUTTON_TEXT": "Stornieren", | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Bot updated successfully", | ||||
|         "ERROR_MESSAGE": "Could not update bot, Please try again later" | ||||
|         "SUCCESS_MESSAGE": "Bot erfolgreich aktualisiert", | ||||
|         "ERROR_MESSAGE": "Bot konnte nicht aktualisiert werden, bitte versuchen Sie es später erneut" | ||||
|       } | ||||
|     }, | ||||
|     "TYPES": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Benutzerdefiniertes Attribut erfolgreich hinzugefügt", | ||||
|         "ERROR_MESSAGE": "Konnte kein benutzerdefiniertes Attribut erstellen. Bitte versuchen Sie es später erneut" | ||||
|         "SUCCESS_MESSAGE": "Benutzerdefiniertes Attribut erfolgreich hinzugefügt!", | ||||
|         "ERROR_MESSAGE": "Could not create a Custom Attribute. Please try again later." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -23,7 +23,7 @@ | ||||
|         "ERROR": "Bitte geben Sie einen gültigen Kontonamen ein" | ||||
|       }, | ||||
|       "LANGUAGE": { | ||||
|         "LABEL": "Site language", | ||||
|         "LABEL": "Sprache", | ||||
|         "PLACEHOLDER": "Ihr Kontoname", | ||||
|         "ERROR": "" | ||||
|       }, | ||||
| @@ -55,7 +55,7 @@ | ||||
|       "ENTER_TO_SELECT": "Drücken Sie zur Auswahl die Eingabetaste", | ||||
|       "ENTER_TO_REMOVE": "Drücken Sie zum Entfernen die Eingabetaste", | ||||
|       "SELECT_ONE": "Eines wählen", | ||||
|       "SELECT": "Select" | ||||
|       "SELECT": "Auswählen" | ||||
|     } | ||||
|   }, | ||||
|   "NOTIFICATIONS_PAGE": { | ||||
| @@ -139,6 +139,6 @@ | ||||
|     } | ||||
|   }, | ||||
|   "DASHBOARD_APPS": { | ||||
|     "LOADING_MESSAGE": "Loading Dashboard App..." | ||||
|     "LOADING_MESSAGE": "Dashboard-App wird geladen..." | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -53,6 +53,10 @@ | ||||
|           "ENABLE": "Konversationen aus erwähnten Tweets erstellen" | ||||
|         } | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       }, | ||||
|       "WEBSITE_CHANNEL": { | ||||
|         "TITLE": "Website-Kanal", | ||||
|         "DESC": "Erstellen Sie einen Kanal für Ihre Website und unterstützen Sie Ihre Kunden über unser Website-Widget.", | ||||
| @@ -134,7 +138,7 @@ | ||||
|         "PHONE_NUMBER": { | ||||
|           "LABEL": "Telefonnummer", | ||||
|           "PLACEHOLDER": "Bitte geben Sie die Telefonnummer ein, von der die Nachricht gesendet wird.", | ||||
|           "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." | ||||
|           "ERROR": "Bitte geben Sie eine gültige Telefonnummer an, die mit einem `+` Zeichen beginnt und keine Leerzeichen enthält." | ||||
|         }, | ||||
|         "API_CALLBACK": { | ||||
|           "TITLE": "Callback URL", | ||||
| @@ -185,7 +189,7 @@ | ||||
|           "PHONE_NUMBER": { | ||||
|             "LABEL": "Telefonnummer", | ||||
|             "PLACEHOLDER": "Bitte geben Sie die Telefonnummer ein, von der die Nachricht gesendet wird.", | ||||
|             "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." | ||||
|             "ERROR": "Bitte geben Sie eine gültige Telefonnummer an, die mit einem `+` Zeichen beginnt und keine Leerzeichen enthält." | ||||
|           }, | ||||
|           "SUBMIT_BUTTON": "Bitte geben Sie Ihre Bandbreitenanwendungs-ID ein", | ||||
|           "API": { | ||||
| @@ -214,7 +218,7 @@ | ||||
|         "PHONE_NUMBER": { | ||||
|           "LABEL": "Telefonnummer", | ||||
|           "PLACEHOLDER": "Bitte geben Sie die Telefonnummer ein, von der die Nachricht gesendet wird.", | ||||
|           "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." | ||||
|           "ERROR": "Bitte geben Sie eine gültige Telefonnummer an, die mit einem `+` Zeichen beginnt und keine Leerzeichen enthält." | ||||
|         }, | ||||
|         "PHONE_NUMBER_ID": { | ||||
|           "LABEL": "Telefonnummer-ID", | ||||
| @@ -548,6 +552,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "SSL aktivieren" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "SMTP", | ||||
|       "SUBTITLE": "Setzen Sie Ihre SMTP-Details", | ||||
|   | ||||
| @@ -76,6 +76,13 @@ | ||||
|         "BODY": "<br/><p>Chatwoot wird nun alle eingehenden Konversationen in den <b><i>Kundengespräche</i></b> Channel innerhalb Ihres Slack Arbeitsplatzes synchronisieren.</p><p>Wenn Sie in <b><i>Kunden-Konversationen</i></b> antworten, wird der Slack Kanal eine Antwort an den Kunden durch Chat erzeugen.</p><p>Starten Sie die Antworten mit <b><i>Notiz:</i></b> um private Notizen anstatt Antworten zu erstellen.</p><p>Wenn der Replier auf Slack ein Agentenprofil im Chatwoot unter der gleichen E-Mail hat, werden die Antworten entsprechend assoziiert.</p><p>Wenn der Replier kein Agentenprofil hat, werden die Antworten aus dem Bot-Profil getätigt.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Click here to join", | ||||
|       "LEAVE_THE_ROOM": "Leave the room", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Start a new video call with the customer", | ||||
|       "JOIN_ERROR": "There was an error joining the call, please try again", | ||||
|       "CREATE_ERROR": "There was an error creating a meeting link, please try again" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Löschen", | ||||
|       "API": { | ||||
|   | ||||
| @@ -282,6 +282,7 @@ | ||||
|     } | ||||
|   }, | ||||
|   "KEYBOARD_SHORTCUTS": { | ||||
|     "TOGGLE_MODAL": "View all shortcuts", | ||||
|     "TITLE": { | ||||
|       "OPEN_CONVERSATION": "Unterhaltung öffnen", | ||||
|       "RESOLVE_AND_NEXT": "Lösen und zum Nächsten gehen", | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Επιλέξτε ενός Agent Bot", | ||||
|       "DESC": "Μπορείτε να ορίσετε ένα agent bot από τη λίστα σε αυτά τα εισερχόμενα. Το bot μπορεί αρχικά να χειριστεί τη συνομιλία και να την μεταφέρει σε έναν πράκτορα όταν χρειάζεται.", | ||||
|       "SUBMIT": "Ενημέρωση", | ||||
|       "DISCONNECT": "Disconnect Bot", | ||||
|       "SUCCESS_MESSAGE": "Επιτυχής ενημέρωση του agent bot", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot", | ||||
|       "ERROR_MESSAGE": "Δεν ήταν δυνατή η ενημέρωση του agent bot, δοκιμάστε ξανά αργότερα", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later", | ||||
|       "SELECT_PLACEHOLDER": "Επιλογή Bot" | ||||
|     }, | ||||
|     "ADD": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Η ιδιότητα προστέθηκε με επιτυχία", | ||||
|         "ERROR_MESSAGE": "Δεν ήταν δυνατή η δημιουργία Ιδιότητας, Παρακαλώ δοκιμάστε ξανά αργότερα" | ||||
|         "SUCCESS_MESSAGE": "Η ιδιότητα προστέθηκε με επιτυχία!", | ||||
|         "ERROR_MESSAGE": "Δεν ήταν δυνατή η δημιουργία Ιδιότητας, Παρακαλώ δοκιμάστε ξανά αργότερα." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -53,6 +53,10 @@ | ||||
|           "ENABLE": "Δημιουργία συνομιλιών από τα αναφερόμενα Tweets" | ||||
|         } | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       }, | ||||
|       "WEBSITE_CHANNEL": { | ||||
|         "TITLE": "Κανάλι Ιστοσελίδας", | ||||
|         "DESC": "Δημιουργήστε ένα κανάλι Ιστοσελίδα για να υποστηρίξετε τους πελάτες σας μέσω του πρόσθετου επικοινωνίας (widget) που θα εγκαταστήσετε στην ιστοσελίδα σας.", | ||||
| @@ -548,6 +552,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "Ενεργοποίηση SSL" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "SMTP", | ||||
|       "SUBTITLE": "Ορισμός λεπτομερειών SMTP", | ||||
|   | ||||
| @@ -76,6 +76,13 @@ | ||||
|         "BODY": "<br/><p>Το Chatwoot θα συγχρονίσει τώρα όλες τις εισερχόμενες συνομιλίες στο κανάλι <b><i>πελατών-συνομιλιών</i></b> μέσα στο slack χώρο εργασίας σας.</p><p>Απάντηση σε μια συνομιλία από <b><i>συνομιλίες πελατών</i></b> το κανάλι slack θα δημιουργήσει μια απάντηση για στον πελάτη μέσω chatwoot.</p><p>Ξεκινήστε τις απαντήσεις με το <b><i>note:</i></b> για να δημιουργήσετε ιδιωτικές σημειώσεις αντί για απαντήσεις.</p><p>Αν ο χρήστης στο slack έχει προφίλ πράκτορα στο chatwoot με το ίδιο email, οι απαντήσεις θα συσχετιστούν ανάλογα.</p><p>Εφόσον δεν έχει προφίλ συνδεδεμένου πράκτορα, οι απαντήσεις θα γίνουν από το προφίλ bot.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Κάντε κλικ εδώ για να συμμετάσχετε", | ||||
|       "LEAVE_THE_ROOM": "Αποχώρηση", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Ξεκινήστε μια νέα βιντεοκλήση με τον πελάτη", | ||||
|       "JOIN_ERROR": "Παρουσιάστηκε σφάλμα κατά τη σύνδεση της κλήσης, παρακαλώ προσπαθήστε ξανά", | ||||
|       "CREATE_ERROR": "Παρουσιάστηκε σφάλμα κατά τη δημιουργία ενός συνδέσμου συνάντησης, παρακαλώ προσπαθήστε ξανά" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Διαγραφή", | ||||
|       "API": { | ||||
|   | ||||
| @@ -282,6 +282,7 @@ | ||||
|     } | ||||
|   }, | ||||
|   "KEYBOARD_SHORTCUTS": { | ||||
|     "TOGGLE_MODAL": "Εμφάνιση όλων των συντομεύσεων", | ||||
|     "TITLE": { | ||||
|       "OPEN_CONVERSATION": "Άνοιγμα συνομιλίας", | ||||
|       "RESOLVE_AND_NEXT": "Επίλυση και μετακίνηση στην επόμενη", | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Select an agent bot", | ||||
|       "DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.", | ||||
|       "SUBMIT": "Update", | ||||
|       "DISCONNECT": "Disconnect Bot", | ||||
|       "SUCCESS_MESSAGE": "Successfully updated the agent bot", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot", | ||||
|       "ERROR_MESSAGE": "Could not update the agent bot, please try again later", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later", | ||||
|       "SELECT_PLACEHOLDER": "Select Bot" | ||||
|     }, | ||||
|     "ADD": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Custom Attribute added successfully", | ||||
|         "ERROR_MESSAGE": "Could not able to create a custom attribute, Please try again later" | ||||
|         "SUCCESS_MESSAGE": "Custom Attribute added successfully!", | ||||
|         "ERROR_MESSAGE": "Could not create a Custom Attribute. Please try again later." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -214,7 +214,7 @@ | ||||
|         "PHONE_NUMBER": { | ||||
|           "LABEL": "Phone number", | ||||
|           "PLACEHOLDER": "Please enter the phone number from which message will be sent.", | ||||
|          "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." | ||||
|           "ERROR": "Please provide a valid phone number that starts with a `+` sign and does not contain any spaces." | ||||
|         }, | ||||
|         "PHONE_NUMBER_ID": { | ||||
|           "LABEL": "Phone number ID", | ||||
| @@ -344,6 +344,17 @@ | ||||
|       "FINISH": { | ||||
|         "TITLE": "Nailed It!", | ||||
|         "DESC": "You have successfully finished integrating your Facebook Page with Chatwoot. Next time a customer messages your Page, the conversation will automatically appear on your inbox.<br>We are also providing you with a widget script that you can easily add to your website. Once this is live on your website, customers can message you right from your website without the help of any external tool and the conversation will appear right here, on Chatwoot.<br>Cool, huh? Well, we sure try to be :)" | ||||
|       }, | ||||
|       "EMAIL_PROVIDER": { | ||||
|         "TITLE": "Select your email provider", | ||||
|         "DESCRIPTION": "Select an email provider from the list below. If you don't see your email provider in the list, you can select the other provider option and provide the IMAP and SMTP Credentials." | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "TITLE": "Microsoft Email", | ||||
|         "DESCRIPTION": "Click on the Sign in with Microsoft button to get started. You will redirected to the email sign in page. Once you accept the requested permissions, you would be redirected back to the inbox creation step.", | ||||
|         "EMAIL_PLACEHOLDER": "Enter email address", | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       } | ||||
|     }, | ||||
|     "DETAILS": { | ||||
| @@ -548,6 +559,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "Enable SSL" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "SMTP", | ||||
|       "SUBTITLE": "Set your SMTP details", | ||||
| @@ -666,6 +681,10 @@ | ||||
|       }, | ||||
|       "BRANDING_TEXT": "Powered by Chatwoot", | ||||
|       "SCRIPT_SETTINGS": "\n      window.chatwootSettings = {options};" | ||||
|     }, | ||||
|     "EMAIL_PROVIDERS": { | ||||
|       "MICROSOFT": "Microsoft", | ||||
|       "OTHER_PROVIDERS": "Other Providers" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -35,10 +35,7 @@ | ||||
|       "LIST": { | ||||
|         "404": "There are no webhooks configured for this account.", | ||||
|         "TITLE": "Manage webhooks", | ||||
|         "TABLE_HEADER": [ | ||||
|           "Webhook endpoint", | ||||
|           "Actions" | ||||
|         ] | ||||
|         "TABLE_HEADER": ["Webhook endpoint", "Actions"] | ||||
|       }, | ||||
|       "EDIT": { | ||||
|         "BUTTON_TEXT": "Edit", | ||||
| @@ -76,6 +73,13 @@ | ||||
|         "BODY": "<br/><p>Chatwoot will now sync all the incoming conversations into the <b><i>customer-conversations</i></b> channel inside your slack workplace.</p><p>Replying to a conversation thread in <b><i>customer-conversations</i></b> slack channel will create a response back to the customer through chatwoot.</p><p>Start the replies with <b><i>note:</i></b> to create private notes instead of replies.</p><p>If the replier on slack has an agent profile in chatwoot under the same email, the replies will be associated accordingly.</p><p>When the replier doesn't have an associated agent profile, the replies will be made from the bot profile.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Click here to join", | ||||
|       "LEAVE_THE_ROOM": "Leave the room", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Start a new video call with the customer", | ||||
|       "JOIN_ERROR": "There was an error joining the call, please try again", | ||||
|       "CREATE_ERROR": "There was an error creating a meeting link, please try again" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Delete", | ||||
|       "API": { | ||||
| @@ -93,10 +97,7 @@ | ||||
|       "LIST": { | ||||
|         "404": "There are no dashboard apps configured on this account yet", | ||||
|         "LOADING": "Fetching dashboard apps...", | ||||
|         "TABLE_HEADER": [ | ||||
|           "Name", | ||||
|           "Endpoint" | ||||
|         ], | ||||
|         "TABLE_HEADER": ["Name", "Endpoint"], | ||||
|         "EDIT_TOOLTIP": "Edit app", | ||||
|         "DELETE_TOOLTIP": "Delete app" | ||||
|       }, | ||||
|   | ||||
| @@ -158,7 +158,7 @@ | ||||
|   "COMPONENTS": { | ||||
|     "CODE": { | ||||
|       "BUTTON_TEXT": "Copy", | ||||
|       "COPY_SUCCESSFUL": "Code copied to clipboard successfully" | ||||
|       "COPY_SUCCESSFUL": "Copied to clipboard" | ||||
|     }, | ||||
|     "SHOW_MORE_BLOCK": { | ||||
|       "SHOW_MORE": "Show More", | ||||
|   | ||||
| @@ -24,8 +24,11 @@ | ||||
|       "TITLE": "Seleccione un bot de agente", | ||||
|       "DESC": "Puedes establecer un bot de agente desde la lista a esta bandeja de entrada. El bot puede inicialmente manejar la conversación y transferirla a un agente cuando sea necesario.", | ||||
|       "SUBMIT": "Actualizar", | ||||
|       "DISCONNECT": "Desconectar Bot", | ||||
|       "SUCCESS_MESSAGE": "El bot del agente se ha actualizado con éxito", | ||||
|       "DISCONNECTED_SUCCESS_MESSAGE": "Desconectado correctamente el bot agente", | ||||
|       "ERROR_MESSAGE": "No se pudo actualizar el bot de agente, por favor inténtalo de nuevo más tarde", | ||||
|       "DISCONNECTED_ERROR_MESSAGE": "No se pudo desconectar el bot de agente, por favor inténtalo de nuevo más tarde", | ||||
|       "SELECT_PLACEHOLDER": "Seleccionar bot" | ||||
|     }, | ||||
|     "ADD": { | ||||
|   | ||||
| @@ -42,8 +42,8 @@ | ||||
|         } | ||||
|       }, | ||||
|       "API": { | ||||
|         "SUCCESS_MESSAGE": "Atributo personalizado añadido correctamente", | ||||
|         "ERROR_MESSAGE": "No se pudo crear un atributo personalizado, por favor inténtalo de nuevo" | ||||
|         "SUCCESS_MESSAGE": "Atributo personalizado añadido correctamente!", | ||||
|         "ERROR_MESSAGE": "Could not create a Custom Attribute. Please try again later." | ||||
|       } | ||||
|     }, | ||||
|     "DELETE": { | ||||
|   | ||||
| @@ -53,6 +53,10 @@ | ||||
|           "ENABLE": "Crear conversaciones de los Tweets mencionados" | ||||
|         } | ||||
|       }, | ||||
|       "MICROSOFT": { | ||||
|         "HELP": "To add your Microsoft account as a channel, you need to authenticate your Microsoft account by clicking on 'Sign in with Microsoft' ", | ||||
|         "ERROR_MESSAGE": "There was an error connecting to Microsoft, please try again" | ||||
|       }, | ||||
|       "WEBSITE_CHANNEL": { | ||||
|         "TITLE": "Canal del sitio web", | ||||
|         "DESC": "Cree un canal para su sitio web y comience a apoyar a sus clientes a través de nuestro widget de sitio web.", | ||||
| @@ -548,6 +552,10 @@ | ||||
|       }, | ||||
|       "ENABLE_SSL": "Activar SSL" | ||||
|     }, | ||||
|     "MICROSOFT": { | ||||
|       "TITLE": "Microsoft", | ||||
|       "SUBTITLE": "Reauthorize your MICROSOFT account" | ||||
|     }, | ||||
|     "SMTP": { | ||||
|       "TITLE": "SMTP", | ||||
|       "SUBTITLE": "Configura tus detalles de SMTP", | ||||
|   | ||||
| @@ -76,6 +76,13 @@ | ||||
|         "BODY": "<br/><p>Chatwoot ahora sincronizará todas las conversaciones entrantes en el canal <b><i>de conversaciones del cliente</i></b> dentro de tu lugar de trabajo slack.</p><p>Respondiendo a un tema de conversación en <b><i>conversaciones de clientes</i></b> canal de slack creará una respuesta al cliente a través de chatwoot.</p><p>Inicie las respuestas con <b><i>nota:</i></b> para crear notas privadas en lugar de respuestas.</p><p>Si el respondente de slack tiene un perfil de agente en el chatwoot bajo el mismo correo electrónico, las respuestas se asociarán en consecuencia.</p><p>Cuando el replicador no tiene un perfil de agente asociado, las respuestas se harán con el perfil del bot.</p>" | ||||
|       } | ||||
|     }, | ||||
|     "DYTE": { | ||||
|       "CLICK_HERE_TO_JOIN": "Click here to join", | ||||
|       "LEAVE_THE_ROOM": "Leave the room", | ||||
|       "START_VIDEO_CALL_HELP_TEXT": "Start a new video call with the customer", | ||||
|       "JOIN_ERROR": "There was an error joining the call, please try again", | ||||
|       "CREATE_ERROR": "There was an error creating a meeting link, please try again" | ||||
|     }, | ||||
|     "DELETE": { | ||||
|       "BUTTON_TEXT": "Eliminar", | ||||
|       "API": { | ||||
|   | ||||
| @@ -282,6 +282,7 @@ | ||||
|     } | ||||
|   }, | ||||
|   "KEYBOARD_SHORTCUTS": { | ||||
|     "TOGGLE_MODAL": "View all shortcuts", | ||||
|     "TITLE": { | ||||
|       "OPEN_CONVERSATION": "Abrir conversación", | ||||
|       "RESOLVE_AND_NEXT": "Resolver y pasar al siguiente", | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Sojan
					Sojan