Merge branch 'release/2.5.0'
This commit is contained in:
commit
63b1013b45
878 changed files with 21085 additions and 6933 deletions
|
@ -161,13 +161,15 @@ USE_INBOX_AVATAR_FOR_BOT=true
|
|||
## NewRelic
|
||||
# https://docs.newrelic.com/docs/agents/ruby-agent/configuration/ruby-agent-configuration/
|
||||
# NEW_RELIC_LICENSE_KEY=
|
||||
# Set this to true to allow newrelic apm to send logs.
|
||||
# This is turned off by default.
|
||||
# NEW_RELIC_APPLICATION_LOGGING_ENABLED=
|
||||
|
||||
## Datadog
|
||||
## https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md#environment-variables
|
||||
# DD_TRACE_AGENT_URL=
|
||||
|
||||
|
||||
|
||||
## IP look up configuration
|
||||
## ref https://github.com/alexreisner/geocoder/blob/master/README_API_GUIDE.md
|
||||
## works only on accounts with ip look up feature enabled
|
||||
|
|
3
.github/workflows/run_foss_spec.yml
vendored
3
.github/workflows/run_foss_spec.yml
vendored
|
@ -42,7 +42,8 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
|
|
8
Gemfile
8
Gemfile
|
@ -97,14 +97,14 @@ gem 'brakeman'
|
|||
gem 'ddtrace'
|
||||
gem 'newrelic_rpm'
|
||||
gem 'scout_apm'
|
||||
gem 'sentry-rails'
|
||||
gem 'sentry-ruby'
|
||||
gem 'sentry-sidekiq'
|
||||
gem 'sentry-rails', '~> 5.3'
|
||||
gem 'sentry-ruby', '~> 5.3'
|
||||
gem 'sentry-sidekiq', '~> 5.3'
|
||||
|
||||
##-- background job processing --##
|
||||
gem 'sidekiq', '~> 6.4.0'
|
||||
# We want cron jobs
|
||||
gem 'sidekiq-cron'
|
||||
gem 'sidekiq-cron', '~> 1.3'
|
||||
|
||||
##-- Push notification service --##
|
||||
gem 'fcm'
|
||||
|
|
158
Gemfile.lock
158
Gemfile.lock
|
@ -9,63 +9,63 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (6.1.4.7)
|
||||
actionpack (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
actioncable (6.1.5.1)
|
||||
actionpack (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.1.4.7)
|
||||
actionpack (= 6.1.4.7)
|
||||
activejob (= 6.1.4.7)
|
||||
activerecord (= 6.1.4.7)
|
||||
activestorage (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
actionmailbox (6.1.5.1)
|
||||
actionpack (= 6.1.5.1)
|
||||
activejob (= 6.1.5.1)
|
||||
activerecord (= 6.1.5.1)
|
||||
activestorage (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.4.7)
|
||||
actionpack (= 6.1.4.7)
|
||||
actionview (= 6.1.4.7)
|
||||
activejob (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
actionmailer (6.1.5.1)
|
||||
actionpack (= 6.1.5.1)
|
||||
actionview (= 6.1.5.1)
|
||||
activejob (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.1.4.7)
|
||||
actionview (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
actionpack (6.1.5.1)
|
||||
actionview (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.1.4.7)
|
||||
actionpack (= 6.1.4.7)
|
||||
activerecord (= 6.1.4.7)
|
||||
activestorage (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
actiontext (6.1.5.1)
|
||||
actionpack (= 6.1.5.1)
|
||||
activerecord (= 6.1.5.1)
|
||||
activestorage (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
actionview (6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
active_record_query_trace (1.8)
|
||||
activejob (6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
activejob (6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
activerecord (6.1.4.7)
|
||||
activemodel (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
activemodel (6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
activerecord (6.1.5.1)
|
||||
activemodel (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
activerecord-import (1.3.0)
|
||||
activerecord (>= 4.2)
|
||||
activestorage (6.1.4.7)
|
||||
actionpack (= 6.1.4.7)
|
||||
activejob (= 6.1.4.7)
|
||||
activerecord (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
marcel (~> 1.0.0)
|
||||
activestorage (6.1.5.1)
|
||||
actionpack (= 6.1.5.1)
|
||||
activejob (= 6.1.5.1)
|
||||
activerecord (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (6.1.4.7)
|
||||
activesupport (6.1.5.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
|
@ -136,7 +136,7 @@ GEM
|
|||
climate_control (1.0.1)
|
||||
coderay (1.1.3)
|
||||
commonmarker (0.23.4)
|
||||
concurrent-ruby (1.1.9)
|
||||
concurrent-ruby (1.1.10)
|
||||
connection_pool (2.2.5)
|
||||
crack (0.4.5)
|
||||
rexml
|
||||
|
@ -183,7 +183,7 @@ GEM
|
|||
email_reply_trimmer (0.1.13)
|
||||
erubi (1.10.0)
|
||||
erubis (2.7.0)
|
||||
et-orbi (1.2.6)
|
||||
et-orbi (1.2.7)
|
||||
tzinfo
|
||||
execjs (2.8.1)
|
||||
facebook-messenger (2.0.1)
|
||||
|
@ -210,8 +210,8 @@ GEM
|
|||
ruby_parser (~> 3.0)
|
||||
sexp_processor (~> 4.0)
|
||||
foreman (0.87.2)
|
||||
fugit (1.5.2)
|
||||
et-orbi (~> 1.1, >= 1.1.8)
|
||||
fugit (1.5.3)
|
||||
et-orbi (~> 1, >= 1.2.7)
|
||||
raabro (~> 1.4)
|
||||
gapic-common (0.3.4)
|
||||
google-protobuf (~> 3.12, >= 3.12.2)
|
||||
|
@ -349,7 +349,7 @@ GEM
|
|||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
loofah (2.14.0)
|
||||
loofah (2.17.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
|
@ -376,16 +376,16 @@ GEM
|
|||
net-http-persistent (4.0.1)
|
||||
connection_pool (~> 2.2)
|
||||
netrc (0.11.0)
|
||||
newrelic_rpm (8.4.0)
|
||||
newrelic_rpm (8.7.0)
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.13.4)
|
||||
nokogiri (1.13.5)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.4-arm64-darwin)
|
||||
nokogiri (1.13.5-arm64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.4-x86_64-darwin)
|
||||
nokogiri (1.13.5-x86_64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.4-x86_64-linux)
|
||||
nokogiri (1.13.5-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
oauth (0.5.8)
|
||||
orm_adapter (0.5.0)
|
||||
|
@ -419,31 +419,31 @@ GEM
|
|||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-timeout (0.6.0)
|
||||
rails (6.1.4.7)
|
||||
actioncable (= 6.1.4.7)
|
||||
actionmailbox (= 6.1.4.7)
|
||||
actionmailer (= 6.1.4.7)
|
||||
actionpack (= 6.1.4.7)
|
||||
actiontext (= 6.1.4.7)
|
||||
actionview (= 6.1.4.7)
|
||||
activejob (= 6.1.4.7)
|
||||
activemodel (= 6.1.4.7)
|
||||
activerecord (= 6.1.4.7)
|
||||
activestorage (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
rails (6.1.5.1)
|
||||
actioncable (= 6.1.5.1)
|
||||
actionmailbox (= 6.1.5.1)
|
||||
actionmailer (= 6.1.5.1)
|
||||
actionpack (= 6.1.5.1)
|
||||
actiontext (= 6.1.5.1)
|
||||
actionview (= 6.1.5.1)
|
||||
activejob (= 6.1.5.1)
|
||||
activemodel (= 6.1.5.1)
|
||||
activerecord (= 6.1.5.1)
|
||||
activestorage (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.4.7)
|
||||
railties (= 6.1.5.1)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.4.2)
|
||||
loofah (~> 2.3)
|
||||
railties (6.1.4.7)
|
||||
actionpack (= 6.1.4.7)
|
||||
activesupport (= 6.1.4.7)
|
||||
railties (6.1.5.1)
|
||||
actionpack (= 6.1.5.1)
|
||||
activesupport (= 6.1.5.1)
|
||||
method_source
|
||||
rake (>= 0.13)
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
|
@ -533,16 +533,16 @@ GEM
|
|||
activesupport (>= 4)
|
||||
selectize-rails (0.12.6)
|
||||
semantic_range (3.0.0)
|
||||
sentry-rails (5.1.0)
|
||||
sentry-rails (5.3.0)
|
||||
railties (>= 5.0)
|
||||
sentry-ruby-core (~> 5.1.0)
|
||||
sentry-ruby (5.1.0)
|
||||
sentry-ruby-core (~> 5.3.0)
|
||||
sentry-ruby (5.3.0)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sentry-ruby-core (= 5.1.0)
|
||||
sentry-ruby-core (5.1.0)
|
||||
sentry-ruby-core (= 5.3.0)
|
||||
sentry-ruby-core (5.3.0)
|
||||
concurrent-ruby
|
||||
sentry-sidekiq (5.1.0)
|
||||
sentry-ruby-core (~> 5.1.0)
|
||||
sentry-sidekiq (5.3.0)
|
||||
sentry-ruby-core (~> 5.3.0)
|
||||
sidekiq (>= 3.0)
|
||||
sexp_processor (4.16.0)
|
||||
shoulda-matchers (5.1.0)
|
||||
|
@ -551,8 +551,8 @@ GEM
|
|||
connection_pool (>= 2.2.2)
|
||||
rack (~> 2.0)
|
||||
redis (>= 4.2.0)
|
||||
sidekiq-cron (1.2.0)
|
||||
fugit (~> 1.1)
|
||||
sidekiq-cron (1.4.0)
|
||||
fugit (~> 1)
|
||||
sidekiq (>= 4.2.1)
|
||||
signet (0.16.0)
|
||||
addressable (~> 2.8)
|
||||
|
@ -726,12 +726,12 @@ DEPENDENCIES
|
|||
rubocop-rspec
|
||||
scout_apm
|
||||
seed_dump
|
||||
sentry-rails
|
||||
sentry-ruby
|
||||
sentry-sidekiq
|
||||
sentry-rails (~> 5.3)
|
||||
sentry-ruby (~> 5.3)
|
||||
sentry-sidekiq (~> 5.3)
|
||||
shoulda-matchers
|
||||
sidekiq (~> 6.4.0)
|
||||
sidekiq-cron
|
||||
sidekiq-cron (~> 1.3)
|
||||
simplecov (= 0.17.1)
|
||||
slack-ruby-client
|
||||
spring
|
||||
|
@ -755,4 +755,4 @@ RUBY VERSION
|
|||
ruby 3.0.2p107
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.8
|
||||
2.3.10
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class Campaigns::CampaignConversationBuilder
|
||||
pattr_initialize [:contact_inbox_id!, :campaign_display_id!, :conversation_additional_attributes]
|
||||
pattr_initialize [:contact_inbox_id!, :campaign_display_id!, :conversation_additional_attributes, :custom_attributes]
|
||||
|
||||
def perform
|
||||
@contact_inbox = ContactInbox.find(@contact_inbox_id)
|
||||
|
@ -21,7 +21,8 @@ class Campaigns::CampaignConversationBuilder
|
|||
|
||||
def message_params
|
||||
ActionController::Parameters.new({
|
||||
content: @campaign.message
|
||||
content: @campaign.message,
|
||||
campaign_id: @campaign.id
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -32,7 +33,8 @@ class Campaigns::CampaignConversationBuilder
|
|||
contact_id: @contact_inbox.contact_id,
|
||||
contact_inbox_id: @contact_inbox.id,
|
||||
campaign_id: @campaign.id,
|
||||
additional_attributes: conversation_additional_attributes
|
||||
additional_attributes: conversation_additional_attributes,
|
||||
custom_attributes: custom_attributes || {}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,11 +15,10 @@ class ContactBuilder
|
|||
end
|
||||
|
||||
def create_contact_inbox(contact)
|
||||
::ContactInbox.create!(
|
||||
::ContactInbox.create_with(hmac_verified: hmac_verified || false).find_or_create_by!(
|
||||
contact_id: contact.id,
|
||||
inbox_id: inbox.id,
|
||||
source_id: source_id,
|
||||
hmac_verified: hmac_verified || false
|
||||
source_id: source_id
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||
rescue Koala::Facebook::AuthenticationError
|
||||
Rails.logger.error "Facebook Authorization expired for Inbox #{@inbox.id}"
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception
|
||||
true
|
||||
end
|
||||
|
||||
|
@ -43,7 +43,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||
return if contact.present?
|
||||
|
||||
@contact = Contact.create!(contact_params.except(:remote_avatar_url))
|
||||
@contact_inbox = ContactInbox.create(contact: contact, inbox: @inbox, source_id: @sender_id)
|
||||
@contact_inbox = ContactInbox.find_or_create_by!(contact: contact, inbox: @inbox, source_id: @sender_id)
|
||||
end
|
||||
|
||||
def build_message
|
||||
|
@ -128,10 +128,10 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||
result = {}
|
||||
# OAuthException, code: 100, error_subcode: 2018218, message: (#100) No profile available for this user
|
||||
# We don't need to capture this error as we don't care about contact params in case of echo messages
|
||||
Sentry.capture_exception(e) unless @outgoing_echo
|
||||
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception unless @outgoing_echo
|
||||
rescue StandardError => e
|
||||
result = {}
|
||||
Sentry.capture_exception(e)
|
||||
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception
|
||||
end
|
||||
process_contact_params_result(result)
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ class Messages::Instagram::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||
@inbox.channel.authorization_error!
|
||||
raise
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception
|
||||
true
|
||||
end
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ class Messages::MessageBuilder
|
|||
@user = user
|
||||
@message_type = params[:message_type] || 'outgoing'
|
||||
@attachments = params[:attachments]
|
||||
@automation_rule = @params&.dig(:content_attributes, :automation_rule_id)
|
||||
return unless params.instance_of?(ActionController::Parameters)
|
||||
|
||||
@in_reply_to = params.to_unsafe_h&.dig(:content_attributes, :in_reply_to)
|
||||
|
@ -64,6 +65,14 @@ class Messages::MessageBuilder
|
|||
@params[:external_created_at].present? ? { external_created_at: @params[:external_created_at] } : {}
|
||||
end
|
||||
|
||||
def automation_rule_id
|
||||
@automation_rule.present? ? { content_attributes: { automation_rule_id: @automation_rule } } : {}
|
||||
end
|
||||
|
||||
def campaign_id
|
||||
@params[:campaign_id].present? ? { additional_attributes: { campaign_id: @params[:campaign_id] } } : {}
|
||||
end
|
||||
|
||||
def message_sender
|
||||
return if @params[:sender_type] != 'AgentBot'
|
||||
|
||||
|
@ -82,6 +91,6 @@ class Messages::MessageBuilder
|
|||
items: @items,
|
||||
in_reply_to: @in_reply_to,
|
||||
echo_id: @params[:echo_id]
|
||||
}.merge(external_created_at)
|
||||
}.merge(external_created_at).merge(automation_rule_id).merge(campaign_id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,16 +53,7 @@ class Messages::Messenger::MessageBuilder
|
|||
|
||||
def fetch_story_link(attachment)
|
||||
message = attachment.message
|
||||
begin
|
||||
k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook?
|
||||
result = k.get_object(message.source_id, fields: %w[story from]) || {}
|
||||
rescue Koala::Facebook::AuthenticationError
|
||||
@inbox.channel.authorization_error!
|
||||
raise
|
||||
rescue StandardError => e
|
||||
result = {}
|
||||
Sentry.capture_exception(e)
|
||||
end
|
||||
result = get_story_object_from_source_id(message.source_id)
|
||||
story_id = result['story']['mention']['id']
|
||||
story_sender = result['from']['username']
|
||||
message.content_attributes[:story_sender] = story_sender
|
||||
|
@ -70,4 +61,15 @@ class Messages::Messenger::MessageBuilder
|
|||
message.content = I18n.t('conversations.messages.instagram_story_content', story_sender: story_sender)
|
||||
message.save!
|
||||
end
|
||||
|
||||
def get_story_object_from_source_id(source_id)
|
||||
k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook?
|
||||
k.get_object(source_id, fields: %w[story from]) || {}
|
||||
rescue Koala::Facebook::AuthenticationError
|
||||
@inbox.channel.authorization_error!
|
||||
raise
|
||||
rescue StandardError => e
|
||||
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ class V2::ReportBuilder
|
|||
attr_reader :account, :params
|
||||
|
||||
DEFAULT_GROUP_BY = 'day'.freeze
|
||||
AGENT_RESULTS_PER_PAGE = 25
|
||||
|
||||
def initialize(account, params)
|
||||
@account = account
|
||||
|
@ -45,7 +46,7 @@ class V2::ReportBuilder
|
|||
if params[:type].equal?(:account)
|
||||
conversations
|
||||
else
|
||||
agent_metrics
|
||||
agent_metrics.sort_by { |hash| hash[:metric][:open] }.reverse
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -79,20 +80,23 @@ class V2::ReportBuilder
|
|||
end
|
||||
|
||||
def agent_metrics
|
||||
users = @account.users
|
||||
users = users.where(id: params[:user_id]) if params[:user_id].present?
|
||||
users.each_with_object([]) do |user, arr|
|
||||
@user = user
|
||||
account_users = @account.account_users.page(params[:page]).per(AGENT_RESULTS_PER_PAGE)
|
||||
account_users.each_with_object([]) do |account_user, arr|
|
||||
@user = account_user.user
|
||||
arr << {
|
||||
user: { id: user.id, name: user.name, thumbnail: user.avatar_url },
|
||||
id: @user.id,
|
||||
name: @user.name,
|
||||
email: @user.email,
|
||||
thumbnail: @user.avatar_url,
|
||||
availability: account_user.availability_status,
|
||||
metric: conversations
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def conversations
|
||||
@open_conversations = scope.conversations.open
|
||||
first_response_count = scope.reporting_events.where(name: 'first_response', conversation_id: @open_conversations.pluck('id')).count
|
||||
@open_conversations = scope.conversations.where(account_id: @account.id).open
|
||||
first_response_count = @account.reporting_events.where(name: 'first_response', conversation_id: @open_conversations.pluck('id')).count
|
||||
metric = {
|
||||
open: @open_conversations.count,
|
||||
unattended: @open_conversations.count - first_response_count
|
||||
|
|
|
@ -45,6 +45,8 @@ class RoomChannel < ApplicationCable::Channel
|
|||
end
|
||||
|
||||
def current_account
|
||||
return if current_user.blank?
|
||||
|
||||
@current_account ||= if @current_user.is_a? Contact
|
||||
@current_user.account
|
||||
else
|
||||
|
|
|
@ -17,6 +17,16 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
|||
@automation_rule
|
||||
end
|
||||
|
||||
def attach_file
|
||||
file_blob = ActiveStorage::Blob.create_and_upload!(
|
||||
key: nil,
|
||||
io: params[:attachment].tempfile,
|
||||
filename: params[:attachment].original_filename,
|
||||
content_type: params[:attachment].content_type
|
||||
)
|
||||
render json: { blob_key: file_blob.key, blob_id: file_blob.id }
|
||||
end
|
||||
|
||||
def show; end
|
||||
|
||||
def update
|
||||
|
@ -25,6 +35,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
|||
@automation_rule.actions = params[:actions] if params[:actions]
|
||||
@automation_rule.save!
|
||||
process_attachments
|
||||
|
||||
rescue StandardError => e
|
||||
Rails.logger.error e
|
||||
render json: { error: @automation_rule.errors.messages }.to_json, status: :unprocessable_entity
|
||||
|
@ -43,17 +54,19 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
|||
@automation_rule = new_rule
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_attachments
|
||||
return if params[:attachments].blank?
|
||||
actions = @automation_rule.actions.filter_map { |k, _v| k if k['action_name'] == 'send_attachment' }
|
||||
return if actions.blank?
|
||||
|
||||
params[:attachments].each do |uploaded_attachment|
|
||||
@automation_rule.files.attach(uploaded_attachment)
|
||||
actions.each do |action|
|
||||
blob_id = action['action_params']
|
||||
blob = ActiveStorage::Blob.find_by(id: blob_id)
|
||||
@automation_rule.files.attach(blob)
|
||||
end
|
||||
@automation_rule
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def automation_rules_permit
|
||||
params.permit(
|
||||
:name, :description, :event_name, :account_id, :active,
|
||||
|
|
|
@ -15,7 +15,7 @@ class Api::V1::Accounts::CallbacksController < Api::V1::Accounts::BaseController
|
|||
set_instagram_id(page_access_token, facebook_channel)
|
||||
set_avatar(@facebook_inbox, page_id)
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
ChatwootExceptionTracker.new(e).capture_exception
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -60,7 +60,7 @@ class Api::V1::Accounts::CallbacksController < Api::V1::Accounts::BaseController
|
|||
set_instagram_id(access_token, fb_page)
|
||||
fb_page&.reauthorized!
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
ChatwootExceptionTracker.new(e).capture_exception
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
|
|||
build_inbox
|
||||
setup_webhooks if @twilio_channel.sms?
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
render_could_not_create_error(e.message)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class Api::V1::Accounts::Conversations::BaseController < Api::V1::Accounts::Base
|
|||
private
|
||||
|
||||
def conversation
|
||||
@conversation ||= Current.account.conversations.find_by(display_id: params[:conversation_id])
|
||||
@conversation ||= Current.account.conversations.find_by!(display_id: params[:conversation_id])
|
||||
authorize @conversation.inbox, :show?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,6 @@ class Api::V1::Accounts::Kbase::BaseController < Api::V1::Accounts::BaseControll
|
|||
private
|
||||
|
||||
def portal
|
||||
@portal ||= Current.account.kbase_portals.find_by(id: params[:portal_id])
|
||||
@portal ||= Current.account.kbase_portals.find_by(slug: params[:portal_id])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
class Api::V1::Accounts::Kbase::PortalsController < Api::V1::Accounts::Kbase::BaseController
|
||||
class Api::V1::Accounts::Kbase::PortalsController < Api::V1::Accounts::BaseController
|
||||
before_action :fetch_portal, except: [:index, :create]
|
||||
|
||||
def index
|
||||
@portals = Current.account.kbase_portals
|
||||
end
|
||||
|
||||
def show; end
|
||||
|
||||
def create
|
||||
@portal = Current.account.kbase_portals.create!(portal_params)
|
||||
end
|
||||
|
@ -21,7 +23,11 @@ class Api::V1::Accounts::Kbase::PortalsController < Api::V1::Accounts::Kbase::Ba
|
|||
private
|
||||
|
||||
def fetch_portal
|
||||
@portal = current_account.kbase_portals.find(params[:id])
|
||||
@portal = Current.account.kbase_portals.find_by(slug: permitted_params[:id])
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.permit(:id)
|
||||
end
|
||||
|
||||
def portal_params
|
||||
|
|
|
@ -23,7 +23,7 @@ class Api::V1::Accounts::WebhooksController < Api::V1::Accounts::BaseController
|
|||
private
|
||||
|
||||
def webhook_params
|
||||
params.require(:webhook).permit(:inbox_id, :url)
|
||||
params.require(:webhook).permit(:inbox_id, :url, subscriptions: [])
|
||||
end
|
||||
|
||||
def fetch_webhook
|
||||
|
|
|
@ -10,7 +10,7 @@ class Api::V1::WebhooksController < ApplicationController
|
|||
twitter_consumer.consume
|
||||
head :ok
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
ChatwootExceptionTracker.new(e).capture_exception
|
||||
head :ok
|
||||
end
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ class Api::V1::Widget::BaseController < ApplicationController
|
|||
end
|
||||
|
||||
def contact_email
|
||||
permitted_params[:contact][:email].downcase if permitted_params[:contact].present?
|
||||
permitted_params.dig(:contact, :email)&.downcase
|
||||
end
|
||||
|
||||
def contact_name
|
||||
|
@ -79,7 +79,7 @@ class Api::V1::Widget::BaseController < ApplicationController
|
|||
end
|
||||
|
||||
def contact_phone_number
|
||||
params[:contact][:phone_number]
|
||||
permitted_params.dig(:contact, :phone_number)
|
||||
end
|
||||
|
||||
def browser_params
|
||||
|
|
|
@ -69,7 +69,8 @@ class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController
|
|||
end
|
||||
|
||||
def permitted_params
|
||||
params.permit(:id, :typing_status, :website_token, :email, contact: [:name, :email], message: [:content, :referer_url, :timestamp, :echo_id],
|
||||
params.permit(:id, :typing_status, :website_token, :email, contact: [:name, :email, :phone_number],
|
||||
message: [:content, :referer_url, :timestamp, :echo_id],
|
||||
custom_attributes: {})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -82,7 +82,8 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
|||
def conversation_params
|
||||
{
|
||||
type: params[:type].to_sym,
|
||||
user_id: params[:user_id]
|
||||
user_id: params[:user_id],
|
||||
page: params[:page].presence || 1
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -9,8 +9,7 @@ module RequestExceptionHandler
|
|||
|
||||
def handle_with_exception
|
||||
yield
|
||||
rescue ActiveRecord::RecordNotFound => e
|
||||
Sentry.capture_exception(e)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_not_found_error('Resource could not be found')
|
||||
rescue Pundit::NotAuthorizedError
|
||||
render_unauthorized('You are not authorized to do this action')
|
||||
|
|
|
@ -38,9 +38,12 @@ class DashboardController < ActionController::Base
|
|||
end
|
||||
|
||||
def app_config
|
||||
{ APP_VERSION: Chatwoot.config[:version],
|
||||
{
|
||||
APP_VERSION: Chatwoot.config[:version],
|
||||
VAPID_PUBLIC_KEY: VapidService.public_key,
|
||||
ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false'),
|
||||
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', '') }
|
||||
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''),
|
||||
FACEBOOK_API_VERSION: 'v13.0'
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,6 +21,10 @@ class Platform::Api::V1::UsersController < PlatformController
|
|||
|
||||
def update
|
||||
@resource.assign_attributes(user_update_params)
|
||||
|
||||
# We are using devise's reconfirmable flow for changing emails
|
||||
# But in case of platform APIs we don't want user to go through this extra step
|
||||
@resource.skip_reconfirmation! if user_update_params[:email].present?
|
||||
@resource.save!
|
||||
end
|
||||
|
||||
|
|
|
@ -43,6 +43,6 @@ class Public::Api::V1::Inboxes::ContactsController < Public::Api::V1::InboxesCon
|
|||
end
|
||||
|
||||
def permitted_params
|
||||
params.permit(:identifier, :identifier_hash, :email, :name, :avatar_url, custom_attributes: {})
|
||||
params.permit(:identifier, :identifier_hash, :email, :name, :avatar_url, :phone_number, custom_attributes: {})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ class MessageFinder
|
|||
|
||||
def current_messages
|
||||
if @params[:before].present?
|
||||
messages.reorder('created_at desc').where('id < ?', @params[:before]).limit(20).reverse
|
||||
messages.reorder('created_at desc').where('id < ?', @params[:before].to_i).limit(20).reverse
|
||||
else
|
||||
messages.reorder('created_at desc').limit(20).reverse
|
||||
end
|
||||
|
|
|
@ -17,30 +17,30 @@ module ReportHelper
|
|||
end
|
||||
|
||||
def conversations_count
|
||||
(get_grouped_values scope.conversations).count
|
||||
(get_grouped_values scope.conversations.where(account_id: account.id)).count
|
||||
end
|
||||
|
||||
def incoming_messages_count
|
||||
(get_grouped_values scope.messages.incoming.unscope(:order)).count
|
||||
(get_grouped_values scope.messages.where(account_id: account.id).incoming.unscope(:order)).count
|
||||
end
|
||||
|
||||
def outgoing_messages_count
|
||||
(get_grouped_values scope.messages.outgoing.unscope(:order)).count
|
||||
(get_grouped_values scope.messages.where(account_id: account.id).outgoing.unscope(:order)).count
|
||||
end
|
||||
|
||||
def resolutions_count
|
||||
(get_grouped_values scope.conversations.resolved).count
|
||||
(get_grouped_values scope.conversations.where(account_id: account.id).resolved).count
|
||||
end
|
||||
|
||||
def avg_first_response_time
|
||||
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'first_response'))
|
||||
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'first_response', account_id: account.id))
|
||||
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
|
||||
|
||||
grouped_reporting_events.average(:value)
|
||||
end
|
||||
|
||||
def avg_resolution_time
|
||||
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'conversation_resolved'))
|
||||
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'conversation_resolved', account_id: account.id))
|
||||
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
|
||||
|
||||
grouped_reporting_events.average(:value)
|
||||
|
@ -48,7 +48,7 @@ module ReportHelper
|
|||
|
||||
def avg_resolution_time_summary
|
||||
reporting_events = scope.reporting_events
|
||||
.where(name: 'conversation_resolved', created_at: range)
|
||||
.where(name: 'conversation_resolved', account_id: account.id, created_at: range)
|
||||
avg_rt = params[:business_hours] ? reporting_events.average(:value_in_business_hours) : reporting_events.average(:value)
|
||||
|
||||
return 0 if avg_rt.blank?
|
||||
|
@ -58,7 +58,7 @@ module ReportHelper
|
|||
|
||||
def avg_first_response_time_summary
|
||||
reporting_events = scope.reporting_events
|
||||
.where(name: 'first_response', created_at: range)
|
||||
.where(name: 'first_response', account_id: account.id, created_at: range)
|
||||
avg_frt = params[:business_hours] ? reporting_events.average(:value_in_business_hours) : reporting_events.average(:value)
|
||||
|
||||
return 0 if avg_frt.blank?
|
||||
|
|
|
@ -9,6 +9,14 @@ class AutomationsAPI extends ApiClient {
|
|||
clone(automationId) {
|
||||
return axios.post(`${this.url}/${automationId}/clone`);
|
||||
}
|
||||
|
||||
attachment(file) {
|
||||
return axios.post(`${this.url}/attach_file`, file, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new AutomationsAPI();
|
||||
|
|
|
@ -44,6 +44,15 @@ class ReportsAPI extends ApiClient {
|
|||
});
|
||||
}
|
||||
|
||||
getConversationMetric(type = 'account', page = 1) {
|
||||
return axios.get(`${this.url}/conversations`, {
|
||||
params: {
|
||||
type,
|
||||
page,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getAgentReports(since, until) {
|
||||
return axios.get(`${this.url}/agents`, {
|
||||
params: { since, until },
|
||||
|
|
|
@ -97,5 +97,18 @@ describe('#Reports API', () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('#getConversationMetric', () => {
|
||||
reportsAPI.getConversationMetric('account');
|
||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||
'/api/v2/reports/conversations',
|
||||
{
|
||||
params: {
|
||||
type: 'account',
|
||||
page: 1,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
margin-right: var(--space-small);
|
||||
}
|
||||
|
||||
.margin-bottom-small {
|
||||
margin-bottom: var(--space-small);
|
||||
}
|
||||
|
||||
.margin-right-smaller {
|
||||
margin-right: var(--space-smaller);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,16 @@
|
|||
padding: 0 $space-small;
|
||||
}
|
||||
|
||||
.video-js {
|
||||
background: transparent;
|
||||
// Override min-height : 50px in foundation
|
||||
//
|
||||
max-height: $space-mega * 2.4;
|
||||
min-height: 4.8rem;
|
||||
padding: var(--space-normal) 0 0;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
>textarea {
|
||||
@include ghost-input();
|
||||
@include margin(0);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
:menu-config="activeSecondaryMenu"
|
||||
:current-role="currentRole"
|
||||
@add-label="showAddLabelPopup"
|
||||
@toggle-accounts="toggleAccountModal"
|
||||
/>
|
||||
</aside>
|
||||
</template>
|
||||
|
|
|
@ -3,7 +3,8 @@ import { frontendURL } from '../../../../helper/URLHelper';
|
|||
const reports = accountId => ({
|
||||
parentNav: 'reports',
|
||||
routes: [
|
||||
'settings_account_reports',
|
||||
'account_overview_reports',
|
||||
'conversation_reports',
|
||||
'csat_reports',
|
||||
'agent_reports',
|
||||
'label_reports',
|
||||
|
@ -16,7 +17,14 @@ const reports = accountId => ({
|
|||
label: 'REPORTS_OVERVIEW',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL(`accounts/${accountId}/reports/overview`),
|
||||
toStateName: 'settings_account_reports',
|
||||
toStateName: 'account_overview_reports',
|
||||
},
|
||||
{
|
||||
icon: 'chat',
|
||||
label: 'REPORTS_CONVERSATION',
|
||||
hasSubMenu: false,
|
||||
toState: frontendURL(`accounts/${accountId}/reports/conversation`),
|
||||
toStateName: 'conversation_reports',
|
||||
},
|
||||
{
|
||||
icon: 'emoji',
|
||||
|
|
|
@ -1,14 +1,34 @@
|
|||
<template>
|
||||
<div v-if="showShowCurrentAccountContext" class="account-context--group">
|
||||
<div
|
||||
v-if="showShowCurrentAccountContext"
|
||||
class="account-context--group"
|
||||
@mouseover="setShowSwitch"
|
||||
@mouseleave="resetShowSwitch"
|
||||
>
|
||||
{{ $t('SIDEBAR.CURRENTLY_VIEWING_ACCOUNT') }}
|
||||
<p class="account-context--name text-ellipsis">
|
||||
{{ account.name }}
|
||||
</p>
|
||||
<transition name="fade">
|
||||
<div v-if="showSwitchButton" class="account-context--switch-group">
|
||||
<woot-button
|
||||
variant="clear"
|
||||
icon="arrow-swap"
|
||||
class="cursor-pointer"
|
||||
@click="$emit('toggle-accounts')"
|
||||
>
|
||||
{{ $t('SIDEBAR.SWITCH') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
data() {
|
||||
return { showSwitchButton: false };
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
account: 'getCurrentAccount',
|
||||
|
@ -18,6 +38,14 @@ export default {
|
|||
return this.userAccounts.length > 1 && this.account.name;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setShowSwitch() {
|
||||
this.showSwitchButton = true;
|
||||
},
|
||||
resetShowSwitch() {
|
||||
this.showSwitchButton = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
@ -27,10 +55,49 @@ export default {
|
|||
font-size: var(--font-size-mini);
|
||||
padding: var(--space-small);
|
||||
margin-bottom: var(--space-small);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: var(--b-100);
|
||||
}
|
||||
|
||||
.account-context--name {
|
||||
font-weight: var(--font-weight-medium);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.account-context--switch-group {
|
||||
--overlay-shadow: linear-gradient(
|
||||
to right,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 1) 50%
|
||||
);
|
||||
|
||||
align-items: center;
|
||||
background-image: var(--overlay-shadow);
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: var(--border-radius-normal);
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: var(--border-radius-normal);
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: end;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 300ms ease;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div v-if="hasSecondaryMenu" class="main-nav secondary-menu">
|
||||
<account-context />
|
||||
<account-context @toggle-accounts="toggleAccountModal" />
|
||||
<transition-group name="menu-list" tag="ul" class="menu vertical">
|
||||
<secondary-nav-item
|
||||
v-for="menuItem in accessibleMenuItems"
|
||||
|
@ -224,6 +224,9 @@ export default {
|
|||
showAddLabelPopup() {
|
||||
this.$emit('add-label');
|
||||
},
|
||||
toggleAccountModal() {
|
||||
this.$emit('toggle-accounts');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -52,6 +52,11 @@
|
|||
class="answer--text-input"
|
||||
placeholder="Enter url"
|
||||
/>
|
||||
<automation-action-file-input
|
||||
v-if="inputType === 'attachment'"
|
||||
v-model="action_params"
|
||||
:initial-file-name="initialFileName"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<woot-button
|
||||
|
@ -84,9 +89,11 @@
|
|||
|
||||
<script>
|
||||
import AutomationActionTeamMessageInput from './AutomationActionTeamMessageInput.vue';
|
||||
import AutomationActionFileInput from './AutomationFileInput.vue';
|
||||
export default {
|
||||
components: {
|
||||
AutomationActionTeamMessageInput,
|
||||
AutomationActionFileInput,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
|
@ -109,6 +116,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
initialFileName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
action_name: {
|
||||
|
@ -187,6 +198,7 @@ export default {
|
|||
.filter__answer--wrap {
|
||||
margin-right: var(--space-smaller);
|
||||
flex-grow: 1;
|
||||
max-width: 50%;
|
||||
|
||||
input {
|
||||
margin-bottom: 0;
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<label class="input-wrapper" :class="uploadState">
|
||||
<input
|
||||
v-if="uploadState !== 'processing'"
|
||||
type="file"
|
||||
name="attachment"
|
||||
:class="uploadState === 'processing' ? 'disabled' : ''"
|
||||
@change="onChangeFile"
|
||||
/>
|
||||
<spinner v-if="uploadState === 'processing'" />
|
||||
<fluent-icon v-if="uploadState === 'idle'" icon="file-upload" />
|
||||
<fluent-icon
|
||||
v-if="uploadState === 'uploaded'"
|
||||
icon="checkmark-circle"
|
||||
type="outline"
|
||||
class="success-icon"
|
||||
/>
|
||||
<fluent-icon
|
||||
v-if="uploadState === 'failed'"
|
||||
icon="dismiss-circle"
|
||||
type="outline"
|
||||
class="error-icon"
|
||||
/>
|
||||
<p class="file-button">{{ label }}</p>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Spinner from 'shared/components/Spinner';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
export default {
|
||||
components: {
|
||||
Spinner,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
initialFileName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uploadState: 'idle',
|
||||
label: this.$t('AUTOMATION.ATTACHMENT.LABEL_IDLE'),
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.initialFileName) {
|
||||
this.label = this.initialFileName;
|
||||
this.uploadState = 'uploaded';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onChangeFile(event) {
|
||||
this.uploadState = 'processing';
|
||||
this.label = this.$t('AUTOMATION.ATTACHMENT.LABEL_UPLOADING');
|
||||
try {
|
||||
const file = event.target.files[0];
|
||||
const formData = new FormData();
|
||||
formData.append('attachment', file, file.name);
|
||||
const id = await this.$store.dispatch(
|
||||
'automations/uploadAttachment',
|
||||
formData
|
||||
);
|
||||
this.$emit('input', [id]);
|
||||
this.uploadState = 'uploaded';
|
||||
this.label = this.$t('AUTOMATION.ATTACHMENT.LABEL_UPLOADED');
|
||||
} catch (error) {
|
||||
this.uploadState = 'failed';
|
||||
this.label = this.$t('AUTOMATION.ATTACHMENT.LABEL_UPLOAD_FAILED');
|
||||
this.showAlert(this.$t('AUTOMATION.ATTACHMENT.UPLOAD_ERROR'));
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
input[type='file'] {
|
||||
display: none;
|
||||
}
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
height: 39px;
|
||||
background-color: var(--white);
|
||||
border-radius: var(--border-radius-small);
|
||||
border: 1px dashed var(--w-100);
|
||||
padding: var(--space-smaller) var(--space-small);
|
||||
align-items: center;
|
||||
font-size: var(--font-size-mini);
|
||||
cursor: pointer;
|
||||
}
|
||||
.success-icon {
|
||||
margin-right: var(--space-small);
|
||||
color: var(--g-500);
|
||||
}
|
||||
.error-icon {
|
||||
margin-right: var(--space-small);
|
||||
color: var(--r-500);
|
||||
}
|
||||
|
||||
.processing {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.file-button {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
50
app/javascript/dashboard/components/widgets/ShowMore.vue
Normal file
50
app/javascript/dashboard/components/widgets/ShowMore.vue
Normal file
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<span>
|
||||
{{ textToBeDisplayed }}
|
||||
<button class="show-more--button" @click="toggleShowMore">
|
||||
{{ buttonLabel }}
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 120,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showMore: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
textToBeDisplayed() {
|
||||
if (this.showMore) {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
return this.text.slice(0, this.limit) + '...';
|
||||
},
|
||||
buttonLabel() {
|
||||
const i18nKey = !this.showMore ? 'SHOW_MORE' : 'SHOW_LESS';
|
||||
return this.$t(`COMPONENTS.SHOW_MORE_BLOCK.${i18nKey}`);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleShowMore() {
|
||||
this.showMore = !this.showMore;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.show-more--button {
|
||||
color: var(--w-500);
|
||||
}
|
||||
</style>
|
|
@ -1,16 +1,29 @@
|
|||
<template>
|
||||
<div class="audio-wave-wrapper">
|
||||
<div id="audio-wave"></div>
|
||||
<audio id="audio-wave" class="video-js vjs-fill vjs-default-skin"></audio>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
import MicrophonePlugin from 'wavesurfer.js/dist/plugin/wavesurfer.microphone.js';
|
||||
import RecordRTC from 'recordrtc';
|
||||
import 'video.js/dist/video-js.css';
|
||||
import 'videojs-record/dist/css/videojs.record.css';
|
||||
|
||||
import videojs from 'video.js';
|
||||
|
||||
import inboxMixin from '../../../../shared/mixins/inboxMixin';
|
||||
import alertMixin from '../../../../shared/mixins/alertMixin';
|
||||
|
||||
import Recorder from 'opus-recorder';
|
||||
import encoderWorker from 'opus-recorder/dist/encoderWorker.min';
|
||||
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
import MicrophonePlugin from 'wavesurfer.js/dist/plugin/wavesurfer.microphone.js';
|
||||
import 'videojs-wavesurfer/dist/videojs.wavesurfer.js';
|
||||
|
||||
import 'videojs-record/dist/videojs.record.js';
|
||||
import 'videojs-record/dist/plugins/videojs.record.opus-recorder.js';
|
||||
import { format, addSeconds } from 'date-fns';
|
||||
|
||||
WaveSurfer.microphone = MicrophonePlugin;
|
||||
|
||||
export default {
|
||||
|
@ -18,104 +31,116 @@ export default {
|
|||
mixins: [inboxMixin, alertMixin],
|
||||
data() {
|
||||
return {
|
||||
wavesurfer: false,
|
||||
recorder: false,
|
||||
recordingInterval: false,
|
||||
recordingDateStarted: new Date().getTime(),
|
||||
timeDuration: '00:00',
|
||||
player: false,
|
||||
recordingDateStarted: new Date(0),
|
||||
initialTimeDuration: '00:00',
|
||||
options: {
|
||||
container: '#audio-wave',
|
||||
backend: 'WebAudio',
|
||||
interact: true,
|
||||
cursorWidth: 1,
|
||||
plugins: [
|
||||
WaveSurfer.microphone.create({
|
||||
bufferSize: 4096,
|
||||
numberOfInputChannels: 1,
|
||||
numberOfOutputChannels: 1,
|
||||
constraints: {
|
||||
video: false,
|
||||
audio: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
optionsRecorder: {
|
||||
type: 'audio',
|
||||
mimeType: 'audio/wav',
|
||||
disableLogs: true,
|
||||
recorderType: RecordRTC.StereoAudioRecorder,
|
||||
sampleRate: 44100,
|
||||
numberOfAudioChannels: 2,
|
||||
checkForInactiveTracks: true,
|
||||
bufferSize: 4096,
|
||||
recorderOptions: {
|
||||
debug: true,
|
||||
controls: true,
|
||||
bigPlayButton: false,
|
||||
fluid: false,
|
||||
controlBar: {
|
||||
deviceButton: false,
|
||||
fullscreenToggle: false,
|
||||
cameraButton: false,
|
||||
volumePanel: false,
|
||||
},
|
||||
plugins: {
|
||||
wavesurfer: {
|
||||
backend: 'WebAudio',
|
||||
waveColor: '#1f93ff',
|
||||
progressColor: 'rgb(25, 118, 204)',
|
||||
cursorColor: 'rgba(43, 51, 63, 0.7)',
|
||||
backgroundColor: 'none',
|
||||
barWidth: 1,
|
||||
cursorWidth: 1,
|
||||
hideScrollbar: true,
|
||||
plugins: [
|
||||
WaveSurfer.microphone.create({
|
||||
bufferSize: 4096,
|
||||
numberOfInputChannels: 1,
|
||||
numberOfOutputChannels: 1,
|
||||
constraints: {
|
||||
video: false,
|
||||
audio: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
record: {
|
||||
audio: true,
|
||||
video: false,
|
||||
displayMilliseconds: false,
|
||||
maxLength: 300,
|
||||
audioEngine: 'opus-recorder',
|
||||
audioWorkerURL: encoderWorker,
|
||||
audioChannels: 1,
|
||||
audioSampleRate: 48000,
|
||||
audioBitRate: 128,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isRecording() {
|
||||
if (this.recorder) {
|
||||
return this.recorder.getState() === 'recording';
|
||||
}
|
||||
return false;
|
||||
return this.player && this.player.record().isRecording();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.wavesurfer = WaveSurfer.create(this.options);
|
||||
this.wavesurfer.on('play', this.playingRecorder);
|
||||
this.wavesurfer.on('pause', this.pausedRecorder);
|
||||
this.wavesurfer.microphone.on('deviceReady', this.startRecording);
|
||||
this.wavesurfer.microphone.on('deviceError', this.deviceError);
|
||||
this.wavesurfer.microphone.start();
|
||||
this.fireStateRecorderTimerChanged(this.initialTimeDuration);
|
||||
window.Recorder = Recorder;
|
||||
this.fireProgressRecord(this.initialTimeDuration);
|
||||
this.player = videojs('#audio-wave', this.recorderOptions, () => {
|
||||
this.$nextTick(() => {
|
||||
this.player.record().getDevice();
|
||||
});
|
||||
});
|
||||
this.player.on('deviceReady', this.deviceReady);
|
||||
this.player.on('deviceError', this.deviceError);
|
||||
this.player.on('startRecord', this.startRecord);
|
||||
this.player.on('stopRecord', this.stopRecord);
|
||||
this.player.on('progressRecord', this.progressRecord);
|
||||
this.player.on('finishRecord', this.finishRecord);
|
||||
this.player.on('playbackFinish', this.playbackFinish);
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.recorder) {
|
||||
this.recorder.destroy();
|
||||
if (this.player) {
|
||||
this.player.dispose();
|
||||
}
|
||||
if (this.wavesurfer) {
|
||||
this.wavesurfer.destroy();
|
||||
if (window.Recorder) {
|
||||
window.Recorder = undefined;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startRecording(stream) {
|
||||
this.recorder = RecordRTC(stream, this.optionsRecorder);
|
||||
this.recorder.onStateChanged = this.onStateRecorderChanged;
|
||||
this.recorder.startRecording();
|
||||
deviceReady() {
|
||||
this.player.record().start();
|
||||
},
|
||||
startRecord() {
|
||||
this.fireStateRecorderChanged('recording');
|
||||
},
|
||||
stopRecord() {
|
||||
this.fireStateRecorderChanged('stopped');
|
||||
},
|
||||
finishRecord() {
|
||||
const file = new File(
|
||||
[this.player.recordedData],
|
||||
this.player.recordedData.name,
|
||||
{ type: this.player.recordedData.type }
|
||||
);
|
||||
this.fireRecorderBlob(file);
|
||||
},
|
||||
progressRecord() {
|
||||
this.fireProgressRecord(this.formatTimeProgress());
|
||||
},
|
||||
stopAudioRecording() {
|
||||
if (this.isRecording) {
|
||||
this.recorder.stopRecording(() => {
|
||||
this.wavesurfer.microphone.stopDevice();
|
||||
this.wavesurfer.loadBlob(this.recorder.getBlob());
|
||||
this.wavesurfer.stop();
|
||||
this.fireRecorderBlob(this.getAudioFile());
|
||||
});
|
||||
}
|
||||
this.player.record().stop();
|
||||
},
|
||||
getAudioFile() {
|
||||
if (this.hasAudio()) {
|
||||
return new File([this.recorder.getBlob()], this.getAudioFileName(), {
|
||||
type: 'audio/wav',
|
||||
});
|
||||
}
|
||||
return false;
|
||||
},
|
||||
hasAudio() {
|
||||
return !(this.isRecording || this.wavesurfer.isPlaying());
|
||||
},
|
||||
playingRecorder() {
|
||||
this.fireStateRecorderChanged('playing');
|
||||
},
|
||||
pausedRecorder() {
|
||||
this.fireStateRecorderChanged('paused');
|
||||
},
|
||||
deviceError(err) {
|
||||
deviceError() {
|
||||
const deviceError = this.player.deviceErrorCode;
|
||||
const deviceErrorName = deviceError?.name.toLowerCase();
|
||||
if (
|
||||
err?.name &&
|
||||
(err.name.toLowerCase().includes('notallowederror') ||
|
||||
err.name.toLowerCase().includes('permissiondeniederror'))
|
||||
deviceErrorName?.includes('notallowederror') ||
|
||||
deviceErrorName?.includes('permissiondeniederror')
|
||||
) {
|
||||
this.showAlert(
|
||||
this.$t('CONVERSATION.REPLYBOX.TIP_AUDIORECORDER_PERMISSION')
|
||||
|
@ -127,56 +152,37 @@ export default {
|
|||
);
|
||||
}
|
||||
},
|
||||
onStateRecorderChanged(state) {
|
||||
// recording stopped inactive destroyed
|
||||
switch (state) {
|
||||
case 'recording':
|
||||
this.timerDurationChanged();
|
||||
break;
|
||||
case 'stopped':
|
||||
this.timerDurationChanged();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.fireStateRecorderChanged(state);
|
||||
},
|
||||
timerDurationChanged() {
|
||||
if (this.isRecording) {
|
||||
this.recordingInterval = setInterval(() => {
|
||||
this.calculateTimeDuration(
|
||||
(new Date().getTime() - this.recordingDateStarted) / 1000
|
||||
);
|
||||
this.fireStateRecorderTimerChanged(this.timeDuration);
|
||||
}, 1000);
|
||||
} else {
|
||||
clearInterval(this.recordingInterval);
|
||||
}
|
||||
},
|
||||
calculateTimeDuration(secs) {
|
||||
let hr = Math.floor(secs / 3600);
|
||||
let min = Math.floor((secs - hr * 3600) / 60);
|
||||
let sec = Math.floor(secs - hr * 3600 - min * 60);
|
||||
if (min < 10) {
|
||||
min = '0' + min;
|
||||
}
|
||||
if (sec < 10) {
|
||||
sec = '0' + sec;
|
||||
}
|
||||
if (hr <= 0) {
|
||||
this.timeDuration = min + ':' + sec;
|
||||
} else {
|
||||
if (hr < 10) {
|
||||
hr = '0' + hr;
|
||||
}
|
||||
this.timeDuration = hr + ':' + min + ':' + sec;
|
||||
}
|
||||
formatTimeProgress() {
|
||||
return format(
|
||||
addSeconds(
|
||||
new Date(this.recordingDateStarted.getTimezoneOffset() * 1000 * 60),
|
||||
this.player.record().getDuration()
|
||||
),
|
||||
'mm:ss'
|
||||
);
|
||||
},
|
||||
playPause() {
|
||||
this.wavesurfer.playPause();
|
||||
if (this.player.wavesurfer().surfer.isPlaying()) {
|
||||
this.fireStateRecorderChanged('paused');
|
||||
} else {
|
||||
this.fireStateRecorderChanged('playing');
|
||||
}
|
||||
this.player.wavesurfer().surfer.playPause();
|
||||
},
|
||||
play() {
|
||||
this.fireStateRecorderChanged('playing');
|
||||
this.player.wavesurfer().play();
|
||||
},
|
||||
pause() {
|
||||
this.fireStateRecorderChanged('paused');
|
||||
this.player.wavesurfer().pause();
|
||||
},
|
||||
playbackFinish() {
|
||||
this.fireStateRecorderChanged('paused');
|
||||
this.player.wavesurfer().pause();
|
||||
},
|
||||
fireRecorderBlob(blob) {
|
||||
this.$emit('recorder-blob', {
|
||||
this.$emit('finish-record', {
|
||||
name: blob.name,
|
||||
type: blob.type,
|
||||
size: blob.size,
|
||||
|
@ -186,29 +192,8 @@ export default {
|
|||
fireStateRecorderChanged(state) {
|
||||
this.$emit('state-recorder-changed', state);
|
||||
},
|
||||
fireStateRecorderTimerChanged(duration) {
|
||||
this.$emit('state-recorder-timer-changed', duration);
|
||||
},
|
||||
getAudioFileName() {
|
||||
const d = new Date();
|
||||
return `audio-${d.getFullYear()}-${d.getMonth()}-${d.getDate()}-${this.getRandomString()}.wav`;
|
||||
},
|
||||
getRandomString() {
|
||||
if (
|
||||
window.crypto &&
|
||||
window.crypto.getRandomValues &&
|
||||
navigator.userAgent.indexOf('Safari') === -1
|
||||
) {
|
||||
let a = window.crypto.getRandomValues(new Uint32Array(3));
|
||||
let token = '';
|
||||
for (let i = 0, l = a.length; i < l; i += 1) {
|
||||
token += a[i].toString(36);
|
||||
}
|
||||
return token.toLowerCase();
|
||||
}
|
||||
return (Math.random() * new Date().getTime())
|
||||
.toString(36)
|
||||
.replace(/\./g, '');
|
||||
fireProgressRecord(duration) {
|
||||
this.$emit('state-recorder-progress-changed', duration);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -217,7 +202,9 @@ export default {
|
|||
<style lang="scss">
|
||||
.audio-wave-wrapper {
|
||||
min-height: 8rem;
|
||||
max-height: 12rem;
|
||||
overflow: hidden;
|
||||
height: 8rem;
|
||||
}
|
||||
.video-js .vjs-control-bar {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
:readable-time="readableTime"
|
||||
:source-id="data.source_id"
|
||||
:inbox-id="data.inbox_id"
|
||||
:message-read="showReadTicks"
|
||||
/>
|
||||
</div>
|
||||
<spinner v-if="isPending" size="tiny" />
|
||||
|
@ -153,6 +154,14 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hasUserReadMessage: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isWebWidgetInbox: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -268,6 +277,14 @@ export default {
|
|||
isOutgoing() {
|
||||
return this.data.message_type === MESSAGE_TYPE.OUTGOING;
|
||||
},
|
||||
showReadTicks() {
|
||||
return (
|
||||
(this.isOutgoing || this.isTemplate) &&
|
||||
this.hasUserReadMessage &&
|
||||
this.isWebWidgetInbox &&
|
||||
!this.data.private
|
||||
);
|
||||
},
|
||||
isTemplate() {
|
||||
return this.data.message_type === MESSAGE_TYPE.TEMPLATE;
|
||||
},
|
||||
|
|
|
@ -48,6 +48,10 @@
|
|||
:data="message"
|
||||
:is-a-tweet="isATweet"
|
||||
:has-instagram-story="hasInstagramStory"
|
||||
:has-user-read-message="
|
||||
hasUserReadMessage(message.created_at, getLastSeenAt)
|
||||
"
|
||||
:is-web-widget-inbox="isAWebWidgetInbox"
|
||||
/>
|
||||
<li v-show="getUnreadCount != 0" class="unread--toast">
|
||||
<span class="text-uppercase">
|
||||
|
@ -66,6 +70,10 @@
|
|||
:data="message"
|
||||
:is-a-tweet="isATweet"
|
||||
:has-instagram-story="hasInstagramStory"
|
||||
:has-user-read-message="
|
||||
hasUserReadMessage(message.created_at, getLastSeenAt)
|
||||
"
|
||||
:is-web-widget-inbox="isAWebWidgetInbox"
|
||||
/>
|
||||
</ul>
|
||||
<div
|
||||
|
@ -141,6 +149,7 @@ export default {
|
|||
listLoadingStatus: 'getAllMessagesLoaded',
|
||||
getUnreadCount: 'getUnreadCount',
|
||||
loadingChatList: 'getChatListLoadingStatus',
|
||||
conversationLastSeen: 'getConversationLastSeen',
|
||||
}),
|
||||
inboxId() {
|
||||
return this.currentChat.inbox_id;
|
||||
|
@ -241,6 +250,11 @@ export default {
|
|||
}
|
||||
return 'arrow-chevron-left';
|
||||
},
|
||||
getLastSeenAt() {
|
||||
if (this.conversationLastSeen) return this.conversationLastSeen;
|
||||
const { contact_last_seen_at: contactLastSeenAt } = this.currentChat;
|
||||
return contactLastSeenAt;
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
<woot-audio-recorder
|
||||
v-if="showAudioRecorderEditor"
|
||||
ref="audioRecorderInput"
|
||||
@state-recorder-timer-changed="onStateRecorderTimerChanged"
|
||||
@state-recorder-progress-changed="onStateProgressRecorderChanged"
|
||||
@state-recorder-changed="onStateRecorderChanged"
|
||||
@recorder-blob="onRecorderBlob"
|
||||
@finish-record="onFinishRecorder"
|
||||
/>
|
||||
<resizable-text-area
|
||||
v-else-if="!showRichContentEditor"
|
||||
|
@ -80,8 +80,8 @@
|
|||
>
|
||||
<p
|
||||
v-if="isSignatureAvailable"
|
||||
v-dompurify-html="formatMessage(messageSignature)"
|
||||
class="message-signature"
|
||||
v-html="formatMessage(messageSignature)"
|
||||
/>
|
||||
<p v-else class="message-signature">
|
||||
{{ $t('CONVERSATION.FOOTER.MESSAGE_SIGNATURE_NOT_CONFIGURED') }}
|
||||
|
@ -103,7 +103,7 @@
|
|||
:show-emoji-picker="showEmojiPicker"
|
||||
:on-send="sendMessage"
|
||||
:is-send-disabled="isReplyButtonDisabled"
|
||||
:recording-audio-duration-text="recordingAudioDuration"
|
||||
:recording-audio-duration-text="recordingAudioDurationText"
|
||||
:recording-audio-state="recordingAudioState"
|
||||
:is-recording-audio="isRecordingAudio"
|
||||
:set-format-mode="setFormatMode"
|
||||
|
@ -193,7 +193,7 @@ export default {
|
|||
attachedFiles: [],
|
||||
isRecordingAudio: false,
|
||||
recordingAudioState: '',
|
||||
recordingAudioDuration: '',
|
||||
recordingAudioDurationText: '',
|
||||
isUploading: false,
|
||||
replyType: REPLY_EDITOR_MODES.REPLY,
|
||||
mentionSearchKey: '',
|
||||
|
@ -585,11 +585,13 @@ export default {
|
|||
}
|
||||
},
|
||||
toggleAudioRecorderPlayPause() {
|
||||
if (this.isRecordingAudio && !this.isRecorderAudioStopped) {
|
||||
this.isRecorderAudioStopped = true;
|
||||
this.$refs.audioRecorderInput.stopAudioRecording();
|
||||
} else if (this.isRecordingAudio && this.isRecorderAudioStopped) {
|
||||
this.$refs.audioRecorderInput.playPause();
|
||||
if (this.isRecordingAudio) {
|
||||
if (!this.isRecorderAudioStopped) {
|
||||
this.isRecorderAudioStopped = true;
|
||||
this.$refs.audioRecorderInput.stopAudioRecording();
|
||||
} else if (this.isRecorderAudioStopped) {
|
||||
this.$refs.audioRecorderInput.playPause();
|
||||
}
|
||||
}
|
||||
},
|
||||
hideEmojiPicker() {
|
||||
|
@ -612,19 +614,17 @@ export default {
|
|||
onFocus() {
|
||||
this.isFocused = true;
|
||||
},
|
||||
onStateRecorderTimerChanged(time) {
|
||||
this.recordingAudioDuration = time;
|
||||
onStateProgressRecorderChanged(duration) {
|
||||
this.recordingAudioDurationText = duration;
|
||||
},
|
||||
onStateRecorderChanged(state) {
|
||||
this.recordingAudioState = state;
|
||||
if (state.includes('notallowederror')) {
|
||||
if (state && 'notallowederror'.includes(state)) {
|
||||
this.toggleAudioRecorder();
|
||||
}
|
||||
},
|
||||
onRecorderBlob(file) {
|
||||
if (file) {
|
||||
this.onFileUpload(file);
|
||||
}
|
||||
onFinishRecorder(file) {
|
||||
return file && this.onFileUpload(file);
|
||||
},
|
||||
toggleTyping(status) {
|
||||
const conversationId = this.currentChat.id;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<template>
|
||||
<div class="message-text--metadata">
|
||||
<span class="time">{{ readableTime }}</span>
|
||||
<span class="time" :class="{ delivered: messageRead }">{{
|
||||
readableTime
|
||||
}}</span>
|
||||
<span v-if="showSentIndicator" class="time">
|
||||
<fluent-icon
|
||||
v-tooltip.top-start="$t('CHAT_LIST.SENT')"
|
||||
|
@ -8,6 +10,13 @@
|
|||
size="16"
|
||||
/>
|
||||
</span>
|
||||
<fluent-icon
|
||||
v-if="messageRead"
|
||||
v-tooltip.top-start="$t('CHAT_LIST.MESSAGE_READ')"
|
||||
icon="checkmark-double"
|
||||
class="action--icon read-tick"
|
||||
size="12"
|
||||
/>
|
||||
<fluent-icon
|
||||
v-if="isEmail"
|
||||
v-tooltip.top-start="$t('CHAT_LIST.RECEIVED_VIA_EMAIL')"
|
||||
|
@ -120,6 +129,10 @@ export default {
|
|||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
messageRead: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
inbox() {
|
||||
|
@ -173,6 +186,10 @@ export default {
|
|||
}
|
||||
|
||||
.action--icon {
|
||||
&.read-tick {
|
||||
color: var(--v-100);
|
||||
margin-top: calc(var(--space-micro) + var(--space-micro) / 2);
|
||||
}
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
|
@ -237,6 +254,15 @@ export default {
|
|||
position: absolute;
|
||||
right: var(--space-small);
|
||||
white-space: nowrap;
|
||||
&.delivered {
|
||||
right: var(--space-medium);
|
||||
line-height: 2;
|
||||
}
|
||||
}
|
||||
.read-tick {
|
||||
position: absolute;
|
||||
bottom: var(--space-small);
|
||||
right: var(--space-small);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
'hide--quoted': !showQuotedContent,
|
||||
}"
|
||||
>
|
||||
<div class="text-content" v-html="message"></div>
|
||||
<div v-dompurify-html="message" class="text-content"></div>
|
||||
<button
|
||||
v-if="displayQuotedButton"
|
||||
class="quoted-text--button"
|
||||
|
|
|
@ -23,6 +23,8 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||
'contact.updated': this.onContactUpdate,
|
||||
'conversation.mentioned': this.onConversationMentioned,
|
||||
'notification.created': this.onNotificationCreated,
|
||||
'first.reply.created': this.onFirstReplyCreated,
|
||||
'conversation.read': this.onConversationRead,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -64,6 +66,11 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||
this.fetchConversationStats();
|
||||
};
|
||||
|
||||
onConversationRead = data => {
|
||||
const { contact_last_seen_at: lastSeen } = data;
|
||||
this.app.$store.dispatch('updateConversationRead', lastSeen);
|
||||
};
|
||||
|
||||
onLogout = () => AuthAPI.logout();
|
||||
|
||||
onMessageCreated = data => {
|
||||
|
@ -122,6 +129,7 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||
|
||||
fetchConversationStats = () => {
|
||||
bus.$emit('fetch_conversation_stats');
|
||||
bus.$emit('fetch_overview_reports');
|
||||
};
|
||||
|
||||
onContactDelete = data => {
|
||||
|
@ -139,6 +147,10 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||
onNotificationCreated = data => {
|
||||
this.app.$store.dispatch('notifications/addNotification', data);
|
||||
};
|
||||
|
||||
onFirstReplyCreated = () => {
|
||||
bus.$emit('fetch_overview_reports');
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
const allElementsString = arr => {
|
||||
return arr.every(elem => typeof elem === 'string');
|
||||
};
|
||||
|
||||
const allElementsNumbers = arr => {
|
||||
return arr.every(elem => typeof elem === 'number');
|
||||
};
|
||||
|
||||
const formatArray = params => {
|
||||
if (params.length <= 0) {
|
||||
params = [];
|
||||
} else if (params.every(elem => typeof elem === 'string')) {
|
||||
} else if (allElementsString(params) || allElementsNumbers(params)) {
|
||||
params = [...params];
|
||||
} else {
|
||||
params = params.map(val => val.id);
|
||||
|
|
100
app/javascript/dashboard/helper/preChat.js
Normal file
100
app/javascript/dashboard/helper/preChat.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
import i18n from 'widget/i18n/index';
|
||||
const defaultTranslations = Object.fromEntries(
|
||||
Object.entries(i18n).filter(([key]) => key.includes('en'))
|
||||
).en;
|
||||
|
||||
export const standardFieldKeys = {
|
||||
emailAddress: {
|
||||
key: 'EMAIL_ADDRESS',
|
||||
label: 'Email Id',
|
||||
placeholder: 'Please enter your email address',
|
||||
},
|
||||
fullName: {
|
||||
key: 'FULL_NAME',
|
||||
label: 'Full Name',
|
||||
placeholder: 'Please enter your full name',
|
||||
},
|
||||
phoneNumber: {
|
||||
key: 'PHONE_NUMBER',
|
||||
label: 'Phone Number',
|
||||
placeholder: 'Please enter your phone number',
|
||||
},
|
||||
};
|
||||
|
||||
export const getLabel = ({ key, label }) => {
|
||||
return defaultTranslations.PRE_CHAT_FORM.FIELDS[key]
|
||||
? defaultTranslations.PRE_CHAT_FORM.FIELDS[key].LABEL
|
||||
: label;
|
||||
};
|
||||
export const getPlaceHolder = ({ key, placeholder }) => {
|
||||
return defaultTranslations.PRE_CHAT_FORM.FIELDS[key]
|
||||
? defaultTranslations.PRE_CHAT_FORM.FIELDS[key].PLACEHOLDER
|
||||
: placeholder;
|
||||
};
|
||||
|
||||
export const getCustomFields = ({ standardFields, customAttributes }) => {
|
||||
let customFields = [];
|
||||
const { pre_chat_fields: preChatFields } = standardFields;
|
||||
customAttributes.forEach(attribute => {
|
||||
const itemExist = preChatFields.find(
|
||||
item => item.name === attribute.attribute_key
|
||||
);
|
||||
if (!itemExist) {
|
||||
customFields.push({
|
||||
label: attribute.attribute_display_name,
|
||||
placeholder: attribute.attribute_display_name,
|
||||
name: attribute.attribute_key,
|
||||
type: attribute.attribute_display_type,
|
||||
values: attribute.attribute_values,
|
||||
field_type: attribute.attribute_model,
|
||||
required: false,
|
||||
enabled: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
return customFields;
|
||||
};
|
||||
|
||||
export const getFormattedPreChatFields = ({ preChatFields }) => {
|
||||
return preChatFields.map(item => {
|
||||
return {
|
||||
...item,
|
||||
label: getLabel({
|
||||
key: standardFieldKeys[item.name]
|
||||
? standardFieldKeys[item.name].key
|
||||
: item.name,
|
||||
label: item.label ? item.label : item.name,
|
||||
}),
|
||||
placeholder: getPlaceHolder({
|
||||
key: standardFieldKeys[item.name]
|
||||
? standardFieldKeys[item.name].key
|
||||
: item.name,
|
||||
placeholder: item.placeholder ? item.placeholder : item.name,
|
||||
}),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const getPreChatFields = ({
|
||||
preChatFormOptions = {},
|
||||
customAttributes = [],
|
||||
}) => {
|
||||
const { pre_chat_message, pre_chat_fields } = preChatFormOptions;
|
||||
let customFields = {};
|
||||
let preChatFields = {};
|
||||
|
||||
const formattedPreChatFields = getFormattedPreChatFields({
|
||||
preChatFields: pre_chat_fields,
|
||||
});
|
||||
|
||||
customFields = getCustomFields({
|
||||
standardFields: { pre_chat_fields: formattedPreChatFields },
|
||||
customAttributes,
|
||||
});
|
||||
preChatFields = [...formattedPreChatFields, ...customFields];
|
||||
|
||||
return {
|
||||
pre_chat_message,
|
||||
pre_chat_fields: preChatFields,
|
||||
};
|
||||
};
|
|
@ -1,25 +0,0 @@
|
|||
export const createMessengerScript = pageId => `
|
||||
<script>
|
||||
window.fbAsyncInit = function() {
|
||||
FB.init({
|
||||
appId: "${window.chatwootConfig.fbAppId}",
|
||||
xfbml: true,
|
||||
version: "v4.0"
|
||||
});
|
||||
};
|
||||
(function(d, s, id){
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) { return; }
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/sdk.js";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));
|
||||
|
||||
</script>
|
||||
<div class="fb-messengermessageus"
|
||||
messenger_app_id="${window.chatwootConfig.fbAppId}"
|
||||
page_id="${pageId}"
|
||||
color="blue"
|
||||
size="standard" >
|
||||
</div>
|
||||
`;
|
47
app/javascript/dashboard/helper/specs/inboxFixture.js
Normal file
47
app/javascript/dashboard/helper/specs/inboxFixture.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
export default {
|
||||
customFields: {
|
||||
pre_chat_message: 'Share your queries or comments here.',
|
||||
pre_chat_fields: [
|
||||
{
|
||||
label: 'Email Address',
|
||||
name: 'emailAddress',
|
||||
type: 'email',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
|
||||
placeholder: 'Please enter your email address',
|
||||
},
|
||||
{
|
||||
label: 'Full Name',
|
||||
name: 'fullName',
|
||||
type: 'text',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
placeholder: 'Please enter your full name',
|
||||
},
|
||||
{
|
||||
label: 'Phone Number',
|
||||
name: 'phoneNumber',
|
||||
type: 'text',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
placeholder: 'Please enter your phone number',
|
||||
},
|
||||
],
|
||||
},
|
||||
customAttributes: [
|
||||
{
|
||||
id: 101,
|
||||
attribute_description: 'Order Identifier',
|
||||
attribute_display_name: 'Order Id',
|
||||
attribute_display_type: 'number',
|
||||
attribute_key: 'order_id',
|
||||
attribute_model: 'conversation_attribute',
|
||||
attribute_values: Array(0),
|
||||
created_at: '2021-11-29T10:20:04.563Z',
|
||||
},
|
||||
],
|
||||
};
|
76
app/javascript/dashboard/helper/specs/preChat.spec.js
Normal file
76
app/javascript/dashboard/helper/specs/preChat.spec.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
import {
|
||||
getPreChatFields,
|
||||
getFormattedPreChatFields,
|
||||
getCustomFields,
|
||||
} from '../preChat';
|
||||
import inboxFixture from './inboxFixture';
|
||||
|
||||
const { customFields, customAttributes } = inboxFixture;
|
||||
describe('#Pre chat Helpers', () => {
|
||||
describe('getPreChatFields', () => {
|
||||
it('should return correct pre-chat fields form options passed', () => {
|
||||
expect(getPreChatFields({ preChatFormOptions: customFields })).toEqual(
|
||||
customFields
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('getFormattedPreChatFields', () => {
|
||||
it('should return correct custom fields', () => {
|
||||
expect(
|
||||
getFormattedPreChatFields({
|
||||
preChatFields: customFields.pre_chat_fields,
|
||||
})
|
||||
).toEqual([
|
||||
{
|
||||
label: 'Email Address',
|
||||
name: 'emailAddress',
|
||||
placeholder: 'Please enter your email address',
|
||||
type: 'email',
|
||||
field_type: 'standard',
|
||||
|
||||
required: false,
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: 'Full Name',
|
||||
name: 'fullName',
|
||||
placeholder: 'Please enter your full name',
|
||||
type: 'text',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
label: 'Phone Number',
|
||||
name: 'phoneNumber',
|
||||
placeholder: 'Please enter your phone number',
|
||||
type: 'text',
|
||||
field_type: 'standard',
|
||||
required: false,
|
||||
enabled: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('getCustomFields', () => {
|
||||
it('should return correct custom fields', () => {
|
||||
expect(
|
||||
getCustomFields({
|
||||
standardFields: { pre_chat_fields: customFields.pre_chat_fields },
|
||||
customAttributes,
|
||||
})
|
||||
).toEqual([
|
||||
{
|
||||
enabled: false,
|
||||
label: 'Order Id',
|
||||
placeholder: 'Order Id',
|
||||
name: 'order_id',
|
||||
required: false,
|
||||
field_type: 'conversation_attribute',
|
||||
type: 'number',
|
||||
values: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -89,7 +89,9 @@
|
|||
"DELETE_MESSAGE": "يجب أن يكون لديك على الأقل شرط واحد للحفظ"
|
||||
},
|
||||
"ACTION": {
|
||||
"DELETE_MESSAGE": "يجب أن يكون لديك على الأقل شرط واحد للحفظ"
|
||||
"DELETE_MESSAGE": "يجب أن يكون لديك على الأقل شرط واحد للحفظ",
|
||||
"TEAM_MESSAGE_INPUT_PLACEHOLDER": "اكتب رسالتك هنا",
|
||||
"TEAM_DROPDOWN_PLACEHOLDER": "اختيار فريق"
|
||||
},
|
||||
"TOGGLE": {
|
||||
"ACTIVATION_TITLE": "تفعيل قاعدة الأتمتة",
|
||||
|
@ -102,6 +104,13 @@
|
|||
"DEACTIVATION_ERROR": "تعذر إلغاء تنشيط قاعدة الأتمتة، الرجاء المحاولة مرة أخرى لاحقاً",
|
||||
"CONFIRMATION_LABEL": "نعم",
|
||||
"CANCEL_LABEL": "لا"
|
||||
},
|
||||
"ATTACHMENT": {
|
||||
"UPLOAD_ERROR": "تعذر تحميل المرفق، الرجاء المحاولة مرة أخرى",
|
||||
"LABEL_IDLE": "ارفع المرفق",
|
||||
"LABEL_UPLOADING": "جاري الرفع...",
|
||||
"LABEL_UPLOADED": "تم الرفع بنجاح",
|
||||
"LABEL_UPLOAD_FAILED": "فشل الرفع"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"NO_MESSAGES": "لا توجد رسائل",
|
||||
"NO_CONTENT": "لم يتم العثور على محتوى",
|
||||
"HIDE_QUOTED_TEXT": "Hide Quoted Text",
|
||||
"SHOW_QUOTED_TEXT": "Show Quoted Text"
|
||||
"SHOW_QUOTED_TEXT": "Show Quoted Text",
|
||||
"MESSAGE_READ": "قرائة"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,14 @@
|
|||
"SUCCESS_MESSAGE": "تم حفظ جهة الاتصال بنجاح",
|
||||
"ERROR_MESSAGE": "حدث خطأ، الرجاء المحاولة مرة أخرى"
|
||||
},
|
||||
"DELETE_NOTE": {
|
||||
"CONFIRM": {
|
||||
"TITLE": "تأكيد الحذف",
|
||||
"MESSAGE": "هل أنت متأكد من حذف هذه الملاحظة؟",
|
||||
"YES": "نعم، احذف",
|
||||
"NO": "لا، احتفظ به"
|
||||
}
|
||||
},
|
||||
"DELETE_CONTACT": {
|
||||
"BUTTON_LABEL": "حذف جهة الاتصال",
|
||||
"TITLE": "حذف جهة الاتصال",
|
||||
|
|
|
@ -60,6 +60,13 @@
|
|||
"NOTIFICATIONS_PAGE": {
|
||||
"HEADER": "الإشعارات",
|
||||
"MARK_ALL_DONE": "وضع علامة على جميع المنجز",
|
||||
"DELETE_TITLE": "تم الحذف",
|
||||
"UNREAD_NOTIFICATION": {
|
||||
"TITLE": "إشعارات غير مقروءة",
|
||||
"ALL_NOTIFICATIONS": "عرض جميع الإشعارات",
|
||||
"LOADING_UNREAD_MESSAGE": "تحميل الإشعارات الغير مقروءة...",
|
||||
"EMPTY_MESSAGE": "ليس لديك إشعارات غير مقروءة"
|
||||
},
|
||||
"LIST": {
|
||||
"LOADING_MESSAGE": "جاري تحميل الإشعارات...",
|
||||
"404": "لا يوجد إشعارات",
|
||||
|
@ -101,6 +108,7 @@
|
|||
"GO_TO_CONVERSATION_DASHBOARD": "الذهاب إلى لوحة المحادثة",
|
||||
"GO_TO_CONTACTS_DASHBOARD": "الذهاب إلى لوحة جهات الاتصال",
|
||||
"GO_TO_REPORTS_OVERVIEW": "الذهاب إلى نظرة التقارير",
|
||||
"GO_TO_CONVERSATION_REPORTS": "الذهاب إلى تقارير المحادثات",
|
||||
"GO_TO_AGENT_REPORTS": "الذهاب إلى تقارير الوكيل",
|
||||
"GO_TO_LABEL_REPORTS": "انتقل إلى تقارير التسمية",
|
||||
"GO_TO_INBOX_REPORTS": "الذهاب إلى تقارير صندوق الوارد",
|
||||
|
|
|
@ -341,10 +341,6 @@
|
|||
"AUTO_ASSIGNMENT_SUCCESS_MESSAGE": "تم تحديث إعدادات الإسناد التلقائي بنجاح",
|
||||
"ERROR_MESSAGE": "تعذر تحديث لون صندوق الدردشة. الرجاء المحاولة مرة أخرى لاحقاً."
|
||||
},
|
||||
"AUTO_ASSIGNMENT": {
|
||||
"ENABLED": "مفعل",
|
||||
"DISABLED": "معطّل"
|
||||
},
|
||||
"EMAIL_COLLECT_BOX": {
|
||||
"ENABLED": "مفعل",
|
||||
"DISABLED": "معطّل"
|
||||
|
@ -394,13 +390,16 @@
|
|||
"FEATURES": {
|
||||
"LABEL": "الخصائص",
|
||||
"DISPLAY_FILE_PICKER": "عرض أداة انتقاء الملفات في الـ widget",
|
||||
"DISPLAY_EMOJI_PICKER": "عرض منتقي الرموز التعبيرية على الـ widget"
|
||||
"DISPLAY_EMOJI_PICKER": "عرض منتقي الرموز التعبيرية على الـ widget",
|
||||
"ALLOW_END_CONVERSATION": "السماح للمستخدمين بإنهاء المحادثة من عنصر واجهة المستخدم"
|
||||
},
|
||||
"SETTINGS_POPUP": {
|
||||
"MESSENGER_HEADING": "كود \"الماسنجر\"",
|
||||
"MESSENGER_SUB_HEAD": "ضع هذا الكود داخل وسم الـ body في موقعك",
|
||||
"INBOX_AGENTS": "موظف الدعم",
|
||||
"INBOX_AGENTS_SUB_TEXT": "إضافة أو إزالة موظفين من قناة التواصل هذه",
|
||||
"AGENT_ASSIGNMENT": "إسناد المحادثات",
|
||||
"AGENT_ASSIGNMENT_SUB_TEXT": "تحديث إعدادات إسناد المحادثات",
|
||||
"UPDATE": "تحديث",
|
||||
"ENABLE_EMAIL_COLLECT_BOX": "تفعيل صندوق جمع البريد الإلكتروني",
|
||||
"ENABLE_EMAIL_COLLECT_BOX_SUB_TEXT": "تمكين أو تعطيل مربع جمع البريد الإلكتروني في محادثة جديدة",
|
||||
|
@ -431,6 +430,15 @@
|
|||
},
|
||||
"PRE_CHAT_FORM": {
|
||||
"DESCRIPTION": "نماذج ما قبل الدردشة تمكنك من التقاط معلومات المستخدم قبل بدء المحادثة معك.",
|
||||
"SET_FIELDS": "حقول نموذج الدردشة السابقة",
|
||||
"SET_FIELDS_HEADER": {
|
||||
"FIELDS": "الحقول",
|
||||
"LABEL": "الوسم",
|
||||
"PLACE_HOLDER": "المحتوى",
|
||||
"KEY": "المفتاح",
|
||||
"TYPE": "النوع",
|
||||
"REQUIRED": "مطلوب"
|
||||
},
|
||||
"ENABLE": {
|
||||
"LABEL": "تمكين نموذج الدردشة السابقة",
|
||||
"OPTIONS": {
|
||||
|
@ -468,6 +476,7 @@
|
|||
"IMAP": {
|
||||
"TITLE": "IMAP",
|
||||
"SUBTITLE": "تعيين تفاصيل IMAP الخاصة بك",
|
||||
"NOTE_TEXT": "لتمكين SMTP ، الرجاء تكوين IMAP.",
|
||||
"UPDATE": "تحديث الإعدادات",
|
||||
"TOGGLE_AVAILABILITY": "تمكين تكوين IMAP لهذا البريد الوارد",
|
||||
"TOGGLE_HELP": "تمكين IMAP سيساعد المستخدم على تلقي البريد الإلكتروني",
|
||||
|
@ -483,9 +492,9 @@
|
|||
"LABEL": "المنفذ",
|
||||
"PLACE_HOLDER": "المنفذ"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "البريد الإلكتروني",
|
||||
"PLACE_HOLDER": "البريد الإلكتروني"
|
||||
"LOGIN": {
|
||||
"LABEL": "تسجيل الدخول",
|
||||
"PLACE_HOLDER": "تسجيل الدخول"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "كلمة المرور",
|
||||
|
@ -511,9 +520,9 @@
|
|||
"LABEL": "المنفذ",
|
||||
"PLACE_HOLDER": "المنفذ"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "البريد الإلكتروني",
|
||||
"PLACE_HOLDER": "البريد الإلكتروني"
|
||||
"LOGIN": {
|
||||
"LABEL": "تسجيل الدخول",
|
||||
"PLACE_HOLDER": "تسجيل الدخول"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "كلمة المرور",
|
||||
|
@ -526,7 +535,9 @@
|
|||
"ENCRYPTION": "التشفير",
|
||||
"SSL_TLS": "SSL/TLS",
|
||||
"START_TLS": "STARTTLS",
|
||||
"OPEN_SSL_VERIFY_MODE": "فتح وضع التحقق من SSL"
|
||||
}
|
||||
"OPEN_SSL_VERIFY_MODE": "فتح وضع التحقق من SSL",
|
||||
"AUTH_MECHANISM": "المصادقة"
|
||||
},
|
||||
"NOTE": "ملاحظة: "
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,29 @@
|
|||
"INTEGRATION_SETTINGS": {
|
||||
"HEADER": "خيارات الربط",
|
||||
"WEBHOOK": {
|
||||
"SUBSCRIBED_EVENTS": "الأحداث المشتركة",
|
||||
"FORM": {
|
||||
"CANCEL": "إلغاء",
|
||||
"DESC": "أحداث Webhook توفر لك معلومات في الوقت الحقيقي حول ما يحدث في حساب Chatwoot الخاص بك. الرجاء إدخال عنوان URL صالح لتكوين callback.",
|
||||
"SUBSCRIPTIONS": {
|
||||
"LABEL": "الأحداث",
|
||||
"EVENTS": {
|
||||
"CONVERSATION_CREATED": "تم إنشاء المحادثة",
|
||||
"CONVERSATION_STATUS_CHANGED": "تم تغيير حالة المحادثة",
|
||||
"CONVERSATION_UPDATED": "تم تحديث المحادثة",
|
||||
"MESSAGE_CREATED": "تم إنشاء رسالة",
|
||||
"MESSAGE_UPDATED": "تم تحديث الرسالة",
|
||||
"WEBWIDGET_TRIGGERED": "أداة الدردشة المباشرة مفتوحة من قبل المستخدم"
|
||||
}
|
||||
},
|
||||
"END_POINT": {
|
||||
"LABEL": "رابط Webhook",
|
||||
"PLACEHOLDER": "مثال: https://example/api/webhook",
|
||||
"ERROR": "الرجاء إدخال عنوان URL صالح"
|
||||
},
|
||||
"EDIT_SUBMIT": "تحديث الويبهوك",
|
||||
"ADD_SUBMIT": "إنشاء webhook"
|
||||
},
|
||||
"TITLE": "Webhook",
|
||||
"CONFIGURE": "تهيئة",
|
||||
"HEADER": "إعدادات الـ Webhook",
|
||||
|
@ -20,35 +43,16 @@
|
|||
"EDIT": {
|
||||
"BUTTON_TEXT": "تعديل",
|
||||
"TITLE": "تعديل webhook",
|
||||
"CANCEL": "إلغاء",
|
||||
"DESC": "أحداث Webhook توفر لك معلومات في الوقت الحقيقي حول ما يحدث في حساب Chatwoot الخاص بك. الرجاء إدخال عنوان URL صالح لتكوين callback.",
|
||||
"FORM": {
|
||||
"END_POINT": {
|
||||
"LABEL": "رابط Webhook",
|
||||
"PLACEHOLDER": "مثال: https://example/api/webhook",
|
||||
"ERROR": "الرجاء إدخال عنوان URL صالح"
|
||||
},
|
||||
"SUBMIT": "تعديل webhook"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "تم تحديث رابط Webhook بنجاح",
|
||||
"SUCCESS_MESSAGE": "تم تحديث تكوين ويبهوك بنجاح",
|
||||
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
||||
}
|
||||
},
|
||||
"ADD": {
|
||||
"CANCEL": "إلغاء",
|
||||
"TITLE": "إضافة webhook جديد",
|
||||
"DESC": "أحداث Webhook توفر لك معلومات في الوقت الحقيقي حول ما يحدث في حساب Chatwoot الخاص بك. الرجاء إدخال عنوان URL صالح لتكوين callback.",
|
||||
"FORM": {
|
||||
"END_POINT": {
|
||||
"LABEL": "رابط Webhook",
|
||||
"PLACEHOLDER": "مثال: https://example/api/webhook",
|
||||
"ERROR": "الرجاء إدخال عنوان URL صالح"
|
||||
},
|
||||
"SUBMIT": "إنشاء webhook"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "تم إضافة Webhook بنجاح",
|
||||
"SUCCESS_MESSAGE": "تم إضافة إعدادات Webhook بنجاح",
|
||||
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
||||
}
|
||||
},
|
||||
|
@ -60,7 +64,7 @@
|
|||
},
|
||||
"CONFIRM": {
|
||||
"TITLE": "تأكيد الحذف",
|
||||
"MESSAGE": "هل أنت متأكد من الحذف ",
|
||||
"MESSAGE": "هل أنت متأكد من حذف webhook؟ (%{webhookURL})",
|
||||
"YES": "نعم، احذف ",
|
||||
"NO": "لا، احتفظ به"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"REPORT": {
|
||||
"HEADER": "نظرة عامة",
|
||||
"HEADER": "المحادثات",
|
||||
"LOADING_CHART": "تحميل بيانات الرسم البياني...",
|
||||
"NO_ENOUGH_DATA": "لم يتم جمع بيانات بقدر كافي لإنشاء التقرير، الرجاء المحاولة مرة أخرى لاحقاً.",
|
||||
"DOWNLOAD_AGENT_REPORTS": "تنزيل تقارير الوكيل",
|
||||
|
@ -19,11 +19,15 @@
|
|||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "وقت الاستجابة الأولى",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الرد الأول هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "وقت إغلاق المحادثات",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الحل هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "عدد مرات الإغلاق",
|
||||
|
@ -108,7 +112,8 @@
|
|||
"id": 4,
|
||||
"groupBy": "السنة"
|
||||
}
|
||||
]
|
||||
],
|
||||
"BUSINESS_HOURS": "ساعات العمل"
|
||||
},
|
||||
"AGENT_REPORTS": {
|
||||
"HEADER": "نظرة عامة للوكلاء",
|
||||
|
@ -131,11 +136,15 @@
|
|||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "وقت الاستجابة الأولى",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الرد الأول هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "وقت إغلاق المحادثات",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الحل هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "عدد مرات الإغلاق",
|
||||
|
@ -194,11 +203,15 @@
|
|||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "وقت الاستجابة الأولى",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الرد الأول هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "وقت إغلاق المحادثات",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الحل هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "عدد مرات الإغلاق",
|
||||
|
@ -257,11 +270,15 @@
|
|||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "وقت الاستجابة الأولى",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الرد الأول هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "وقت إغلاق المحادثات",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الحل هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "عدد مرات الإغلاق",
|
||||
|
@ -320,11 +337,15 @@
|
|||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "وقت الاستجابة الأولى",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الرد الأول هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "وقت إغلاق المحادثات",
|
||||
"DESC": "(متوسط)"
|
||||
"DESC": "(متوسط)",
|
||||
"INFO_TEXT": "العدد الإجمالي للمحادثات المستخدمة في الحساب:",
|
||||
"TOOLTIP_TEXT": "وقت الحل هو %{metricValue} (على أساس %{conversationCount} محادثات)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "عدد مرات الإغلاق",
|
||||
|
@ -392,5 +413,33 @@
|
|||
"TOOLTIP": "العدد الإجمالي للردود / العدد الإجمالي لرسائل الاستقصاء التي أرسلتها CSAT * 100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OVERVIEW_REPORTS": {
|
||||
"HEADER": "نظرة عامة",
|
||||
"LIVE": "مباشر",
|
||||
"ACCOUNT_CONVERSATIONS": {
|
||||
"HEADER": "المحادثات المفتوحة",
|
||||
"LOADING_MESSAGE": "جارٍ تحميل مقاييس المحادثات...",
|
||||
"OPEN": "فتح",
|
||||
"UNATTENDED": "بدون حضور",
|
||||
"UNASSIGNED": "غير مسند"
|
||||
},
|
||||
"AGENT_CONVERSATIONS": {
|
||||
"HEADER": "المحادثات من قبل الوكلاء",
|
||||
"LOADING_MESSAGE": "جاري تحميل مقاييس الوكيل...",
|
||||
"NO_AGENTS": "لا توجد أي محادثات من قبل الوكلاء",
|
||||
"TABLE_HEADER": {
|
||||
"AGENT": "موظف الدعم",
|
||||
"OPEN": "افتتحت",
|
||||
"UNATTENDED": "بدون حضور",
|
||||
"STATUS": "الحالة"
|
||||
}
|
||||
},
|
||||
"AGENT_STATUS": {
|
||||
"HEADER": "حالة الوكيل",
|
||||
"ONLINE": "متصل",
|
||||
"BUSY": "مشغول",
|
||||
"OFFLINE": "غير متصل"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
"SUCCESS_MESSAGE": "تم تغيير كلمة المرور بنجاح",
|
||||
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
||||
},
|
||||
"CAPTCHA": {
|
||||
"ERROR": "انتهت صلاحية التحقق. الرجاء حل كلمة التحقق مرة أخرى."
|
||||
},
|
||||
"SUBMIT": "إرسال"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,6 +131,10 @@
|
|||
"BUTTON_TEXT": "نسخ",
|
||||
"COPY_SUCCESSFUL": "تم نسخ الكود إلى الحافظة بنجاح"
|
||||
},
|
||||
"SHOW_MORE_BLOCK": {
|
||||
"SHOW_MORE": "إظهار المزيد",
|
||||
"SHOW_LESS": "إظهار أقل"
|
||||
},
|
||||
"FILE_BUBBLE": {
|
||||
"DOWNLOAD": "تنزيل",
|
||||
"UPLOADING": "جاري الرفع..."
|
||||
|
@ -147,6 +151,7 @@
|
|||
},
|
||||
"SIDEBAR": {
|
||||
"CURRENTLY_VIEWING_ACCOUNT": "مشاهدة حاليا:",
|
||||
"SWITCH": "تبديل",
|
||||
"CONVERSATIONS": "المحادثات",
|
||||
"ALL_CONVERSATIONS": "كل المحادثات",
|
||||
"MENTIONED_CONVERSATIONS": "الإشارات",
|
||||
|
@ -173,8 +178,8 @@
|
|||
"NEW_LABEL": "علامة جديدة",
|
||||
"NEW_TEAM": "فريق جديد",
|
||||
"NEW_INBOX": "صندوق الوارد الجديد",
|
||||
"REPORTS_OVERVIEW": "نظرة عامة",
|
||||
"CSAT": "CSAT",
|
||||
"REPORTS_CONVERSATION": "المحادثات",
|
||||
"CSAT": "تقييم رضاء العملاء",
|
||||
"CAMPAIGNS": "الحملات",
|
||||
"ONGOING": "جارية",
|
||||
"ONE_OFF": "إيقاف واحد",
|
||||
|
@ -183,7 +188,8 @@
|
|||
"REPORTS_INBOX": "صندوق الوارد",
|
||||
"REPORTS_TEAM": "الفريق",
|
||||
"SET_AVAILABILITY_TITLE": "تعيين نفسك كـ",
|
||||
"BETA": "تجريبي"
|
||||
"BETA": "تجريبي",
|
||||
"REPORTS_OVERVIEW": "نظرة عامة"
|
||||
},
|
||||
"CREATE_ACCOUNT": {
|
||||
"NO_ACCOUNT_WARNING": "أوه! لم نتمكن من العثور على الحساب. الرجاء إنشاء حساب جديد للمتابعة.",
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
"PASSWORD": {
|
||||
"LABEL": "كلمة المرور",
|
||||
"PLACEHOLDER": "كلمة المرور",
|
||||
"ERROR": "كلمة المرور قصيرة جداً"
|
||||
"ERROR": "كلمة المرور قصيرة جداً",
|
||||
"IS_INVALID_PASSWORD": "يجب أن تحتوي كلمة المرور على الأقل على حرف كبير واحد وحرف صغير واحد ورقم واحد وحرف خاص واحد"
|
||||
},
|
||||
"CONFIRM_PASSWORD": {
|
||||
"LABEL": "تأكيد كلمة المرور",
|
||||
|
|
|
@ -89,7 +89,9 @@
|
|||
"DELETE_MESSAGE": "You need to have atleast one condition to save"
|
||||
},
|
||||
"ACTION": {
|
||||
"DELETE_MESSAGE": "You need to have atleast one action to save"
|
||||
"DELETE_MESSAGE": "You need to have atleast one action to save",
|
||||
"TEAM_MESSAGE_INPUT_PLACEHOLDER": "Enter your message here",
|
||||
"TEAM_DROPDOWN_PLACEHOLDER": "Select teams"
|
||||
},
|
||||
"TOGGLE": {
|
||||
"ACTIVATION_TITLE": "Activate Automation Rule",
|
||||
|
@ -102,6 +104,13 @@
|
|||
"DEACTIVATION_ERROR": "Could not Deactivate Automation, Please try again later",
|
||||
"CONFIRMATION_LABEL": "Yes",
|
||||
"CANCEL_LABEL": "No"
|
||||
},
|
||||
"ATTACHMENT": {
|
||||
"UPLOAD_ERROR": "Could not upload attachment, Please try again",
|
||||
"LABEL_IDLE": "Upload Attachment",
|
||||
"LABEL_UPLOADING": "Качване...",
|
||||
"LABEL_UPLOADED": "Succesfully Uploaded",
|
||||
"LABEL_UPLOAD_FAILED": "Upload Failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"NO_MESSAGES": "Няма съобщения",
|
||||
"NO_CONTENT": "Няма налично съдържание",
|
||||
"HIDE_QUOTED_TEXT": "Скриване на цитирания текст",
|
||||
"SHOW_QUOTED_TEXT": "Показване на цитирания текст"
|
||||
"SHOW_QUOTED_TEXT": "Показване на цитирания текст",
|
||||
"MESSAGE_READ": "Read"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,14 @@
|
|||
"SUCCESS_MESSAGE": "Успешно запазване на контактите",
|
||||
"ERROR_MESSAGE": "Възникна грешка, моля опитайте отново"
|
||||
},
|
||||
"DELETE_NOTE": {
|
||||
"CONFIRM": {
|
||||
"TITLE": "Потвърди изтриването",
|
||||
"MESSAGE": "Are you want sure to delete this note?",
|
||||
"YES": "Yes, Delete it",
|
||||
"NO": "No, Keep it"
|
||||
}
|
||||
},
|
||||
"DELETE_CONTACT": {
|
||||
"BUTTON_LABEL": "Изтриване на контакта",
|
||||
"TITLE": "Изтриване на контакта",
|
||||
|
|
|
@ -60,6 +60,13 @@
|
|||
"NOTIFICATIONS_PAGE": {
|
||||
"HEADER": "Notifications",
|
||||
"MARK_ALL_DONE": "Mark All Done",
|
||||
"DELETE_TITLE": "deleted",
|
||||
"UNREAD_NOTIFICATION": {
|
||||
"TITLE": "Unread Notifications",
|
||||
"ALL_NOTIFICATIONS": "View all notifications",
|
||||
"LOADING_UNREAD_MESSAGE": "Loading unread notifications...",
|
||||
"EMPTY_MESSAGE": "You have no unread notifications"
|
||||
},
|
||||
"LIST": {
|
||||
"LOADING_MESSAGE": "Loading notifications...",
|
||||
"404": "No Notifications",
|
||||
|
@ -101,6 +108,7 @@
|
|||
"GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard",
|
||||
"GO_TO_CONTACTS_DASHBOARD": "Go to Contacts Dashboard",
|
||||
"GO_TO_REPORTS_OVERVIEW": "Go to Reports Overview",
|
||||
"GO_TO_CONVERSATION_REPORTS": "Go to Conversation Reports",
|
||||
"GO_TO_AGENT_REPORTS": "Go to Agent Reports",
|
||||
"GO_TO_LABEL_REPORTS": "Go to Label Reports",
|
||||
"GO_TO_INBOX_REPORTS": "Go to Inbox Reports",
|
||||
|
|
|
@ -341,10 +341,6 @@
|
|||
"AUTO_ASSIGNMENT_SUCCESS_MESSAGE": "Auto assignment updated successfully",
|
||||
"ERROR_MESSAGE": "Could not update widget color. Please try again later."
|
||||
},
|
||||
"AUTO_ASSIGNMENT": {
|
||||
"ENABLED": "Enabled",
|
||||
"DISABLED": "Disabled"
|
||||
},
|
||||
"EMAIL_COLLECT_BOX": {
|
||||
"ENABLED": "Enabled",
|
||||
"DISABLED": "Disabled"
|
||||
|
@ -394,13 +390,16 @@
|
|||
"FEATURES": {
|
||||
"LABEL": "Features",
|
||||
"DISPLAY_FILE_PICKER": "Display file picker on the widget",
|
||||
"DISPLAY_EMOJI_PICKER": "Display emoji picker on the widget"
|
||||
"DISPLAY_EMOJI_PICKER": "Display emoji picker on the widget",
|
||||
"ALLOW_END_CONVERSATION": "Allow users to end conversation from the widget"
|
||||
},
|
||||
"SETTINGS_POPUP": {
|
||||
"MESSENGER_HEADING": "Messenger Script",
|
||||
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
|
||||
"INBOX_AGENTS": "Агенти",
|
||||
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
|
||||
"AGENT_ASSIGNMENT": "Conversation Assignment",
|
||||
"AGENT_ASSIGNMENT_SUB_TEXT": "Update conversation assignment settings",
|
||||
"UPDATE": "Update",
|
||||
"ENABLE_EMAIL_COLLECT_BOX": "Enable email collect box",
|
||||
"ENABLE_EMAIL_COLLECT_BOX_SUB_TEXT": "Enable or disable email collect box on new conversation",
|
||||
|
@ -431,6 +430,15 @@
|
|||
},
|
||||
"PRE_CHAT_FORM": {
|
||||
"DESCRIPTION": "Pre chat forms enable you to capture user information before they start conversation with you.",
|
||||
"SET_FIELDS": "Pre chat form fields",
|
||||
"SET_FIELDS_HEADER": {
|
||||
"FIELDS": "Fields",
|
||||
"LABEL": "Label",
|
||||
"PLACE_HOLDER": "Placeholder",
|
||||
"KEY": "Ключ",
|
||||
"TYPE": "Тип",
|
||||
"REQUIRED": "Required"
|
||||
},
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable pre chat form",
|
||||
"OPTIONS": {
|
||||
|
@ -439,7 +447,7 @@
|
|||
}
|
||||
},
|
||||
"PRE_CHAT_MESSAGE": {
|
||||
"LABEL": "Pre Chat Message",
|
||||
"LABEL": "Pre chat message",
|
||||
"PLACEHOLDER": "This message would be visible to the users along with the form"
|
||||
},
|
||||
"REQUIRE_EMAIL": {
|
||||
|
@ -468,6 +476,7 @@
|
|||
"IMAP": {
|
||||
"TITLE": "IMAP",
|
||||
"SUBTITLE": "Set your IMAP details",
|
||||
"NOTE_TEXT": "To enable SMTP, please configure IMAP.",
|
||||
"UPDATE": "Update IMAP settings",
|
||||
"TOGGLE_AVAILABILITY": "Enable IMAP configuration for this inbox",
|
||||
"TOGGLE_HELP": "Enabling IMAP will help the user to recieve email",
|
||||
|
@ -483,9 +492,9 @@
|
|||
"LABEL": "Port",
|
||||
"PLACE_HOLDER": "Port"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "Email",
|
||||
"PLACE_HOLDER": "Email"
|
||||
"LOGIN": {
|
||||
"LABEL": "Login",
|
||||
"PLACE_HOLDER": "Login"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "Password",
|
||||
|
@ -511,9 +520,9 @@
|
|||
"LABEL": "Port",
|
||||
"PLACE_HOLDER": "Port"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "Email",
|
||||
"PLACE_HOLDER": "Email"
|
||||
"LOGIN": {
|
||||
"LABEL": "Login",
|
||||
"PLACE_HOLDER": "Login"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "Password",
|
||||
|
@ -526,7 +535,9 @@
|
|||
"ENCRYPTION": "Encryption",
|
||||
"SSL_TLS": "SSL/TLS",
|
||||
"START_TLS": "STARTTLS",
|
||||
"OPEN_SSL_VERIFY_MODE": "Open SSL Verify Mode"
|
||||
}
|
||||
"OPEN_SSL_VERIFY_MODE": "Open SSL Verify Mode",
|
||||
"AUTH_MECHANISM": "Authentication"
|
||||
},
|
||||
"NOTE": "Note: "
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,29 @@
|
|||
"INTEGRATION_SETTINGS": {
|
||||
"HEADER": "Integrations",
|
||||
"WEBHOOK": {
|
||||
"SUBSCRIBED_EVENTS": "Subscribed Events",
|
||||
"FORM": {
|
||||
"CANCEL": "Отмени",
|
||||
"DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.",
|
||||
"SUBSCRIPTIONS": {
|
||||
"LABEL": "Events",
|
||||
"EVENTS": {
|
||||
"CONVERSATION_CREATED": "Conversation Created",
|
||||
"CONVERSATION_STATUS_CHANGED": "Conversation Status Changed",
|
||||
"CONVERSATION_UPDATED": "Conversation Updated",
|
||||
"MESSAGE_CREATED": "Message created",
|
||||
"MESSAGE_UPDATED": "Message updated",
|
||||
"WEBWIDGET_TRIGGERED": "Live chat widget opened by the user"
|
||||
}
|
||||
},
|
||||
"END_POINT": {
|
||||
"LABEL": "Webhook URL",
|
||||
"PLACEHOLDER": "Example: https://example/api/webhook",
|
||||
"ERROR": "Please enter a valid URL"
|
||||
},
|
||||
"EDIT_SUBMIT": "Update webhook",
|
||||
"ADD_SUBMIT": "Create webhook"
|
||||
},
|
||||
"TITLE": "Webhook",
|
||||
"CONFIGURE": "Configure",
|
||||
"HEADER": "Webhook settings",
|
||||
|
@ -20,35 +43,16 @@
|
|||
"EDIT": {
|
||||
"BUTTON_TEXT": "Редактирай",
|
||||
"TITLE": "Edit webhook",
|
||||
"CANCEL": "Отмени",
|
||||
"DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.",
|
||||
"FORM": {
|
||||
"END_POINT": {
|
||||
"LABEL": "Webhook URL",
|
||||
"PLACEHOLDER": "Example: https://example/api/webhook",
|
||||
"ERROR": "Please enter a valid URL"
|
||||
},
|
||||
"SUBMIT": "Edit webhook"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Webhook URL updated successfully",
|
||||
"SUCCESS_MESSAGE": "Webhook configuration updated successfully",
|
||||
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
||||
}
|
||||
},
|
||||
"ADD": {
|
||||
"CANCEL": "Отмени",
|
||||
"TITLE": "Add new webhook",
|
||||
"DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.",
|
||||
"FORM": {
|
||||
"END_POINT": {
|
||||
"LABEL": "Webhook URL",
|
||||
"PLACEHOLDER": "Example: https://example/api/webhook",
|
||||
"ERROR": "Please enter a valid URL"
|
||||
},
|
||||
"SUBMIT": "Create webhook"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Webhook added successfully",
|
||||
"SUCCESS_MESSAGE": "Webhook configuration added successfully",
|
||||
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
||||
}
|
||||
},
|
||||
|
@ -60,7 +64,7 @@
|
|||
},
|
||||
"CONFIRM": {
|
||||
"TITLE": "Потвърди изтриването",
|
||||
"MESSAGE": "Сигурни ли сте за изтриването ",
|
||||
"MESSAGE": "Are you sure to delete the webhook? (%{webhookURL})",
|
||||
"YES": "Да, изтрий ",
|
||||
"NO": "No, Keep it"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"REPORT": {
|
||||
"HEADER": "Overview",
|
||||
"HEADER": "Разговори",
|
||||
"LOADING_CHART": "Loading chart data...",
|
||||
"NO_ENOUGH_DATA": "We've not received enough data points to generate report, Please try again later.",
|
||||
"DOWNLOAD_AGENT_REPORTS": "Download agent reports",
|
||||
|
@ -18,12 +18,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "First response time",
|
||||
"DESC": "( Avg )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Resolution Time",
|
||||
"DESC": "( Avg )"
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Resolution Count",
|
||||
|
@ -108,7 +112,8 @@
|
|||
"id": 4,
|
||||
"groupBy": "Year"
|
||||
}
|
||||
]
|
||||
],
|
||||
"BUSINESS_HOURS": "Business Hours"
|
||||
},
|
||||
"AGENT_REPORTS": {
|
||||
"HEADER": "Agents Overview",
|
||||
|
@ -130,12 +135,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "First response time",
|
||||
"DESC": "( Avg )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Resolution Time",
|
||||
"DESC": "( Avg )"
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Resolution Count",
|
||||
|
@ -193,12 +202,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "First response time",
|
||||
"DESC": "( Avg )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Resolution Time",
|
||||
"DESC": "( Avg )"
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Resolution Count",
|
||||
|
@ -256,12 +269,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "First response time",
|
||||
"DESC": "( Avg )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Resolution Time",
|
||||
"DESC": "( Avg )"
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Resolution Count",
|
||||
|
@ -319,12 +336,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "First response time",
|
||||
"DESC": "( Avg )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Resolution Time",
|
||||
"DESC": "( Avg )"
|
||||
"DESC": "( Avg )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Resolution Count",
|
||||
|
@ -392,5 +413,33 @@
|
|||
"TOOLTIP": "Total number of responses / Total number of CSAT survey messages sent * 100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OVERVIEW_REPORTS": {
|
||||
"HEADER": "Overview",
|
||||
"LIVE": "Live",
|
||||
"ACCOUNT_CONVERSATIONS": {
|
||||
"HEADER": "Open Conversations",
|
||||
"LOADING_MESSAGE": "Loading conversation metrics...",
|
||||
"OPEN": "Отворен",
|
||||
"UNATTENDED": "Unattended",
|
||||
"UNASSIGNED": "Неназначен"
|
||||
},
|
||||
"AGENT_CONVERSATIONS": {
|
||||
"HEADER": "Conversations by agents",
|
||||
"LOADING_MESSAGE": "Loading agent metrics...",
|
||||
"NO_AGENTS": "There are no conversations by agents",
|
||||
"TABLE_HEADER": {
|
||||
"AGENT": "Агент",
|
||||
"OPEN": "OPEN",
|
||||
"UNATTENDED": "Unattended",
|
||||
"STATUS": "Статус"
|
||||
}
|
||||
},
|
||||
"AGENT_STATUS": {
|
||||
"HEADER": "Agent status",
|
||||
"ONLINE": "Online",
|
||||
"BUSY": "Busy",
|
||||
"OFFLINE": "Offline"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
"SUCCESS_MESSAGE": "Successfully changed the password",
|
||||
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
||||
},
|
||||
"CAPTCHA": {
|
||||
"ERROR": "Verification expired. Please solve captcha again."
|
||||
},
|
||||
"SUBMIT": "Изпращане"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
},
|
||||
"MESSAGE_SIGNATURE_SECTION": {
|
||||
"TITLE": "Personal message signature",
|
||||
"NOTE": "Create a personal message signature that would be added to all the messages you send from the platform. Use the rich content editor to create a highly personalised signature.",
|
||||
"NOTE": "Create a personal message signature that would be added to all the messages you send from your email inbox. Use the rich content editor to create a highly personalised signature.",
|
||||
"BTN_TEXT": "Save message signature",
|
||||
"API_ERROR": "Couldn't save signature! Try again",
|
||||
"API_SUCCESS": "Signature saved successfully"
|
||||
|
@ -131,6 +131,10 @@
|
|||
"BUTTON_TEXT": "Copy",
|
||||
"COPY_SUCCESSFUL": "Code copied to clipboard successfully"
|
||||
},
|
||||
"SHOW_MORE_BLOCK": {
|
||||
"SHOW_MORE": "Show More",
|
||||
"SHOW_LESS": "Show Less"
|
||||
},
|
||||
"FILE_BUBBLE": {
|
||||
"DOWNLOAD": "Download",
|
||||
"UPLOADING": "Качване..."
|
||||
|
@ -147,6 +151,7 @@
|
|||
},
|
||||
"SIDEBAR": {
|
||||
"CURRENTLY_VIEWING_ACCOUNT": "Currently viewing:",
|
||||
"SWITCH": "Switch",
|
||||
"CONVERSATIONS": "Разговори",
|
||||
"ALL_CONVERSATIONS": "All Conversations",
|
||||
"MENTIONED_CONVERSATIONS": "Споменавания",
|
||||
|
@ -173,7 +178,7 @@
|
|||
"NEW_LABEL": "New label",
|
||||
"NEW_TEAM": "New team",
|
||||
"NEW_INBOX": "New inbox",
|
||||
"REPORTS_OVERVIEW": "Overview",
|
||||
"REPORTS_CONVERSATION": "Разговори",
|
||||
"CSAT": "CSAT",
|
||||
"CAMPAIGNS": "Campaigns",
|
||||
"ONGOING": "Ongoing",
|
||||
|
@ -183,7 +188,8 @@
|
|||
"REPORTS_INBOX": "Входяща кутия",
|
||||
"REPORTS_TEAM": "Team",
|
||||
"SET_AVAILABILITY_TITLE": "Set yourself as",
|
||||
"BETA": "Beta"
|
||||
"BETA": "Beta",
|
||||
"REPORTS_OVERVIEW": "Overview"
|
||||
},
|
||||
"CREATE_ACCOUNT": {
|
||||
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
"PASSWORD": {
|
||||
"LABEL": "Password",
|
||||
"PLACEHOLDER": "Password",
|
||||
"ERROR": "Password is too short"
|
||||
"ERROR": "Password is too short",
|
||||
"IS_INVALID_PASSWORD": "Password should contain atleast 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character"
|
||||
},
|
||||
"CONFIRM_PASSWORD": {
|
||||
"LABEL": "Confirm Password",
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
"SELECT_ALL": "select all agents",
|
||||
"SELECTED_COUNT": "%{selected} out of %{total} agents selected.",
|
||||
"BUTTON_TEXT": "Add agents",
|
||||
"AGENT_VALIDATION_ERROR": "Select atleaset one agent."
|
||||
"AGENT_VALIDATION_ERROR": "Select at least one agent."
|
||||
},
|
||||
"FINISH": {
|
||||
"TITLE": "Your team is ready!",
|
||||
|
|
|
@ -89,7 +89,9 @@
|
|||
"DELETE_MESSAGE": "You need to have atleast one condition to save"
|
||||
},
|
||||
"ACTION": {
|
||||
"DELETE_MESSAGE": "You need to have atleast one action to save"
|
||||
"DELETE_MESSAGE": "You need to have atleast one action to save",
|
||||
"TEAM_MESSAGE_INPUT_PLACEHOLDER": "Enter your message here",
|
||||
"TEAM_DROPDOWN_PLACEHOLDER": "Select teams"
|
||||
},
|
||||
"TOGGLE": {
|
||||
"ACTIVATION_TITLE": "Activate Automation Rule",
|
||||
|
@ -102,6 +104,13 @@
|
|||
"DEACTIVATION_ERROR": "Could not Deactivate Automation, Please try again later",
|
||||
"CONFIRMATION_LABEL": "Si",
|
||||
"CANCEL_LABEL": "No"
|
||||
},
|
||||
"ATTACHMENT": {
|
||||
"UPLOAD_ERROR": "Could not upload attachment, Please try again",
|
||||
"LABEL_IDLE": "Upload Attachment",
|
||||
"LABEL_UPLOADING": "S'està carregant...",
|
||||
"LABEL_UPLOADED": "Succesfully Uploaded",
|
||||
"LABEL_UPLOAD_FAILED": "Upload Failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"NO_MESSAGES": "Cap Missatge",
|
||||
"NO_CONTENT": "No content available",
|
||||
"HIDE_QUOTED_TEXT": "Hide Quoted Text",
|
||||
"SHOW_QUOTED_TEXT": "Show Quoted Text"
|
||||
"SHOW_QUOTED_TEXT": "Show Quoted Text",
|
||||
"MESSAGE_READ": "Read"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,14 @@
|
|||
"SUCCESS_MESSAGE": "Contacts saved successfully",
|
||||
"ERROR_MESSAGE": "S'ha produït un error; tornau-ho a provar"
|
||||
},
|
||||
"DELETE_NOTE": {
|
||||
"CONFIRM": {
|
||||
"TITLE": "Confirma l'esborrat",
|
||||
"MESSAGE": "Are you want sure to delete this note?",
|
||||
"YES": "Yes, Delete it",
|
||||
"NO": "No, manten-la"
|
||||
}
|
||||
},
|
||||
"DELETE_CONTACT": {
|
||||
"BUTTON_LABEL": "Delete Contact",
|
||||
"TITLE": "Delete contact",
|
||||
|
|
|
@ -60,6 +60,13 @@
|
|||
"NOTIFICATIONS_PAGE": {
|
||||
"HEADER": "Notificacions",
|
||||
"MARK_ALL_DONE": "Marca Tot Fet",
|
||||
"DELETE_TITLE": "deleted",
|
||||
"UNREAD_NOTIFICATION": {
|
||||
"TITLE": "Unread Notifications",
|
||||
"ALL_NOTIFICATIONS": "View all notifications",
|
||||
"LOADING_UNREAD_MESSAGE": "Loading unread notifications...",
|
||||
"EMPTY_MESSAGE": "You have no unread notifications"
|
||||
},
|
||||
"LIST": {
|
||||
"LOADING_MESSAGE": "Carregant notificacions...",
|
||||
"404": "Cap Notificació",
|
||||
|
@ -101,6 +108,7 @@
|
|||
"GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard",
|
||||
"GO_TO_CONTACTS_DASHBOARD": "Go to Contacts Dashboard",
|
||||
"GO_TO_REPORTS_OVERVIEW": "Go to Reports Overview",
|
||||
"GO_TO_CONVERSATION_REPORTS": "Go to Conversation Reports",
|
||||
"GO_TO_AGENT_REPORTS": "Go to Agent Reports",
|
||||
"GO_TO_LABEL_REPORTS": "Go to Label Reports",
|
||||
"GO_TO_INBOX_REPORTS": "Go to Inbox Reports",
|
||||
|
|
|
@ -341,10 +341,6 @@
|
|||
"AUTO_ASSIGNMENT_SUCCESS_MESSAGE": "Assignació automàtica actualitzada correctament",
|
||||
"ERROR_MESSAGE": "No s'ha pogut actualitzar el color del widget. Torneu-ho a provar més endavant."
|
||||
},
|
||||
"AUTO_ASSIGNMENT": {
|
||||
"ENABLED": "Habilita",
|
||||
"DISABLED": "Inhabilita"
|
||||
},
|
||||
"EMAIL_COLLECT_BOX": {
|
||||
"ENABLED": "Habilita",
|
||||
"DISABLED": "Inhabilita"
|
||||
|
@ -394,13 +390,16 @@
|
|||
"FEATURES": {
|
||||
"LABEL": "Característiques",
|
||||
"DISPLAY_FILE_PICKER": "Mostra el selector de fitxers al widget",
|
||||
"DISPLAY_EMOJI_PICKER": "Mostra el selector d'emoji al widget"
|
||||
"DISPLAY_EMOJI_PICKER": "Mostra el selector d'emoji al widget",
|
||||
"ALLOW_END_CONVERSATION": "Allow users to end conversation from the widget"
|
||||
},
|
||||
"SETTINGS_POPUP": {
|
||||
"MESSENGER_HEADING": "Script del missatger",
|
||||
"MESSENGER_SUB_HEAD": "Col·loca aquest botó dins de l'etiqueta body",
|
||||
"INBOX_AGENTS": "Agents",
|
||||
"INBOX_AGENTS_SUB_TEXT": "Afegir o eliminar agents d'aquesta safata d'entrada",
|
||||
"AGENT_ASSIGNMENT": "Conversation Assignment",
|
||||
"AGENT_ASSIGNMENT_SUB_TEXT": "Update conversation assignment settings",
|
||||
"UPDATE": "Actualitza",
|
||||
"ENABLE_EMAIL_COLLECT_BOX": "Enable email collect box",
|
||||
"ENABLE_EMAIL_COLLECT_BOX_SUB_TEXT": "Enable or disable email collect box on new conversation",
|
||||
|
@ -431,6 +430,15 @@
|
|||
},
|
||||
"PRE_CHAT_FORM": {
|
||||
"DESCRIPTION": "Pre chat forms enable you to capture user information before they start conversation with you.",
|
||||
"SET_FIELDS": "Pre chat form fields",
|
||||
"SET_FIELDS_HEADER": {
|
||||
"FIELDS": "Fields",
|
||||
"LABEL": "Label",
|
||||
"PLACE_HOLDER": "Placeholder",
|
||||
"KEY": "Key",
|
||||
"TYPE": "Type",
|
||||
"REQUIRED": "Required"
|
||||
},
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable pre chat form",
|
||||
"OPTIONS": {
|
||||
|
@ -439,7 +447,7 @@
|
|||
}
|
||||
},
|
||||
"PRE_CHAT_MESSAGE": {
|
||||
"LABEL": "Pre Chat Message",
|
||||
"LABEL": "Pre chat message",
|
||||
"PLACEHOLDER": "This message would be visible to the users along with the form"
|
||||
},
|
||||
"REQUIRE_EMAIL": {
|
||||
|
@ -468,6 +476,7 @@
|
|||
"IMAP": {
|
||||
"TITLE": "IMAP",
|
||||
"SUBTITLE": "Set your IMAP details",
|
||||
"NOTE_TEXT": "To enable SMTP, please configure IMAP.",
|
||||
"UPDATE": "Update IMAP settings",
|
||||
"TOGGLE_AVAILABILITY": "Enable IMAP configuration for this inbox",
|
||||
"TOGGLE_HELP": "Enabling IMAP will help the user to recieve email",
|
||||
|
@ -483,9 +492,9 @@
|
|||
"LABEL": "Port",
|
||||
"PLACE_HOLDER": "Port"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "Correu electrònic",
|
||||
"PLACE_HOLDER": "Correu electrònic"
|
||||
"LOGIN": {
|
||||
"LABEL": "Inicia la sessió",
|
||||
"PLACE_HOLDER": "Inicia la sessió"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "Contrasenya",
|
||||
|
@ -511,9 +520,9 @@
|
|||
"LABEL": "Port",
|
||||
"PLACE_HOLDER": "Port"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "Correu electrònic",
|
||||
"PLACE_HOLDER": "Correu electrònic"
|
||||
"LOGIN": {
|
||||
"LABEL": "Inicia la sessió",
|
||||
"PLACE_HOLDER": "Inicia la sessió"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "Contrasenya",
|
||||
|
@ -526,7 +535,9 @@
|
|||
"ENCRYPTION": "Encryption",
|
||||
"SSL_TLS": "SSL/TLS",
|
||||
"START_TLS": "STARTTLS",
|
||||
"OPEN_SSL_VERIFY_MODE": "Open SSL Verify Mode"
|
||||
}
|
||||
"OPEN_SSL_VERIFY_MODE": "Open SSL Verify Mode",
|
||||
"AUTH_MECHANISM": "Authentication"
|
||||
},
|
||||
"NOTE": "Note: "
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,29 @@
|
|||
"INTEGRATION_SETTINGS": {
|
||||
"HEADER": "Integracions",
|
||||
"WEBHOOK": {
|
||||
"SUBSCRIBED_EVENTS": "Subscribed Events",
|
||||
"FORM": {
|
||||
"CANCEL": "Cancel·la",
|
||||
"DESC": "Els esdeveniments de Webhook us proporcionen informació en temps real sobre el que passa al vostre compte de Chatwoot. Introduïu una URL vàlid per configurar un callback.",
|
||||
"SUBSCRIPTIONS": {
|
||||
"LABEL": "Events",
|
||||
"EVENTS": {
|
||||
"CONVERSATION_CREATED": "Conversation Created",
|
||||
"CONVERSATION_STATUS_CHANGED": "Conversation Status Changed",
|
||||
"CONVERSATION_UPDATED": "Conversation Updated",
|
||||
"MESSAGE_CREATED": "Message created",
|
||||
"MESSAGE_UPDATED": "Message updated",
|
||||
"WEBWIDGET_TRIGGERED": "Live chat widget opened by the user"
|
||||
}
|
||||
},
|
||||
"END_POINT": {
|
||||
"LABEL": "URL del webhook",
|
||||
"PLACEHOLDER": "Exemple: https://example/api/webhook",
|
||||
"ERROR": "Introduïu una URL vàlid"
|
||||
},
|
||||
"EDIT_SUBMIT": "Update webhook",
|
||||
"ADD_SUBMIT": "Crear webhook"
|
||||
},
|
||||
"TITLE": "Webhook",
|
||||
"CONFIGURE": "Configura",
|
||||
"HEADER": "Configuració Webhook",
|
||||
|
@ -20,35 +43,16 @@
|
|||
"EDIT": {
|
||||
"BUTTON_TEXT": "Edita",
|
||||
"TITLE": "Edit webhook",
|
||||
"CANCEL": "Cancel·la",
|
||||
"DESC": "Els esdeveniments de Webhook us proporcionen informació en temps real sobre el que passa al vostre compte de Chatwoot. Introduïu una URL vàlid per configurar un callback.",
|
||||
"FORM": {
|
||||
"END_POINT": {
|
||||
"LABEL": "URL del webhook",
|
||||
"PLACEHOLDER": "Exemple: https://example/api/webhook",
|
||||
"ERROR": "Introduïu una URL vàlid"
|
||||
},
|
||||
"SUBMIT": "Edit webhook"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Webhook URL updated successfully",
|
||||
"SUCCESS_MESSAGE": "Webhook configuration updated successfully",
|
||||
"ERROR_MESSAGE": "No s'ha pogut connectar amb el servidor Woot. Torna-ho a provar més endavant"
|
||||
}
|
||||
},
|
||||
"ADD": {
|
||||
"CANCEL": "Cancel·la",
|
||||
"TITLE": "Afegir un nou webhook",
|
||||
"DESC": "Els esdeveniments de Webhook us proporcionen informació en temps real sobre el que passa al vostre compte de Chatwoot. Introduïu una URL vàlid per configurar un callback.",
|
||||
"FORM": {
|
||||
"END_POINT": {
|
||||
"LABEL": "URL del webhook",
|
||||
"PLACEHOLDER": "Exemple: https://example/api/webhook",
|
||||
"ERROR": "Introduïu una URL vàlid"
|
||||
},
|
||||
"SUBMIT": "Crear webhook"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "S'ha afegit el Webhook correctament",
|
||||
"SUCCESS_MESSAGE": "Webhook configuration added successfully",
|
||||
"ERROR_MESSAGE": "No s'ha pogut connectar amb el servidor Woot. Torna-ho a provar més endavant"
|
||||
}
|
||||
},
|
||||
|
@ -60,7 +64,7 @@
|
|||
},
|
||||
"CONFIRM": {
|
||||
"TITLE": "Confirma l'esborrat",
|
||||
"MESSAGE": "N'estàs segur ",
|
||||
"MESSAGE": "Are you sure to delete the webhook? (%{webhookURL})",
|
||||
"YES": "Si, esborra ",
|
||||
"NO": "No, manten-la"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"REPORT": {
|
||||
"HEADER": "Overview",
|
||||
"HEADER": "Converses",
|
||||
"LOADING_CHART": "S'estan carregant dades del gràfic...",
|
||||
"NO_ENOUGH_DATA": "No hem rebut suficients punts de dades per generar l'informe. Torneu-ho a provar més endavant.",
|
||||
"DOWNLOAD_AGENT_REPORTS": "Descarregar Informes d'Agent",
|
||||
|
@ -18,12 +18,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Primer temps de resposta",
|
||||
"DESC": "( Promig )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Temps de resolució",
|
||||
"DESC": "( Promig )"
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Total de resolucions",
|
||||
|
@ -108,7 +112,8 @@
|
|||
"id": 4,
|
||||
"groupBy": "Year"
|
||||
}
|
||||
]
|
||||
],
|
||||
"BUSINESS_HOURS": "Business Hours"
|
||||
},
|
||||
"AGENT_REPORTS": {
|
||||
"HEADER": "Agents Overview",
|
||||
|
@ -130,12 +135,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Primer temps de resposta",
|
||||
"DESC": "( Promig )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Temps de resolució",
|
||||
"DESC": "( Promig )"
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Total de resolucions",
|
||||
|
@ -193,12 +202,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Primer temps de resposta",
|
||||
"DESC": "( Promig )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Temps de resolució",
|
||||
"DESC": "( Promig )"
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Total de resolucions",
|
||||
|
@ -256,12 +269,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Primer temps de resposta",
|
||||
"DESC": "( Promig )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Temps de resolució",
|
||||
"DESC": "( Promig )"
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Total de resolucions",
|
||||
|
@ -319,12 +336,16 @@
|
|||
"DESC": "( Total )"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Primer temps de resposta",
|
||||
"DESC": "( Promig )"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Temps de resolució",
|
||||
"DESC": "( Promig )"
|
||||
"DESC": "( Promig )",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Total de resolucions",
|
||||
|
@ -392,5 +413,33 @@
|
|||
"TOOLTIP": "Total number of responses / Total number of CSAT survey messages sent * 100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OVERVIEW_REPORTS": {
|
||||
"HEADER": "Overview",
|
||||
"LIVE": "Live",
|
||||
"ACCOUNT_CONVERSATIONS": {
|
||||
"HEADER": "Open Conversations",
|
||||
"LOADING_MESSAGE": "Loading conversation metrics...",
|
||||
"OPEN": "Obrir",
|
||||
"UNATTENDED": "Unattended",
|
||||
"UNASSIGNED": "Sense assignar"
|
||||
},
|
||||
"AGENT_CONVERSATIONS": {
|
||||
"HEADER": "Conversations by agents",
|
||||
"LOADING_MESSAGE": "Loading agent metrics...",
|
||||
"NO_AGENTS": "There are no conversations by agents",
|
||||
"TABLE_HEADER": {
|
||||
"AGENT": "Agent",
|
||||
"OPEN": "OPEN",
|
||||
"UNATTENDED": "Unattended",
|
||||
"STATUS": "Estat"
|
||||
}
|
||||
},
|
||||
"AGENT_STATUS": {
|
||||
"HEADER": "Agent status",
|
||||
"ONLINE": "En línia",
|
||||
"BUSY": "Ocupat",
|
||||
"OFFLINE": "Fora de línia"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
"SUCCESS_MESSAGE": "S'ha canviat la contrasenya correctament",
|
||||
"ERROR_MESSAGE": "No s'ha pogut connectar amb Woot Server. Torneu-ho a provar més endavant"
|
||||
},
|
||||
"CAPTCHA": {
|
||||
"ERROR": "Verification expired. Please solve captcha again."
|
||||
},
|
||||
"SUBMIT": "Envia"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,6 +131,10 @@
|
|||
"BUTTON_TEXT": "Copia",
|
||||
"COPY_SUCCESSFUL": "El codi s'ha copiat al porta-retalls amb èxit"
|
||||
},
|
||||
"SHOW_MORE_BLOCK": {
|
||||
"SHOW_MORE": "Show More",
|
||||
"SHOW_LESS": "Show Less"
|
||||
},
|
||||
"FILE_BUBBLE": {
|
||||
"DOWNLOAD": "Descarrega",
|
||||
"UPLOADING": "S'està carregant..."
|
||||
|
@ -147,6 +151,7 @@
|
|||
},
|
||||
"SIDEBAR": {
|
||||
"CURRENTLY_VIEWING_ACCOUNT": "Currently viewing:",
|
||||
"SWITCH": "Switch",
|
||||
"CONVERSATIONS": "Converses",
|
||||
"ALL_CONVERSATIONS": "All Conversations",
|
||||
"MENTIONED_CONVERSATIONS": "Mentions",
|
||||
|
@ -173,7 +178,7 @@
|
|||
"NEW_LABEL": "New label",
|
||||
"NEW_TEAM": "New team",
|
||||
"NEW_INBOX": "New inbox",
|
||||
"REPORTS_OVERVIEW": "Overview",
|
||||
"REPORTS_CONVERSATION": "Converses",
|
||||
"CSAT": "CSAT",
|
||||
"CAMPAIGNS": "Campaigns",
|
||||
"ONGOING": "Ongoing",
|
||||
|
@ -183,7 +188,8 @@
|
|||
"REPORTS_INBOX": "Inbox",
|
||||
"REPORTS_TEAM": "Team",
|
||||
"SET_AVAILABILITY_TITLE": "Set yourself as",
|
||||
"BETA": "Beta"
|
||||
"BETA": "Beta",
|
||||
"REPORTS_OVERVIEW": "Overview"
|
||||
},
|
||||
"CREATE_ACCOUNT": {
|
||||
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
"PASSWORD": {
|
||||
"LABEL": "Contrasenya",
|
||||
"PLACEHOLDER": "Contrasenya",
|
||||
"ERROR": "La contrasenya és massa curta"
|
||||
"ERROR": "La contrasenya és massa curta",
|
||||
"IS_INVALID_PASSWORD": "Password should contain atleast 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character"
|
||||
},
|
||||
"CONFIRM_PASSWORD": {
|
||||
"LABEL": "Confirma la contrasenya",
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
"SELECT_ALL": "select all agents",
|
||||
"SELECTED_COUNT": "%{selected} out of %{total} agents selected.",
|
||||
"BUTTON_TEXT": "Afegir agents",
|
||||
"AGENT_VALIDATION_ERROR": "Select atleaset one agent."
|
||||
"AGENT_VALIDATION_ERROR": "Select at least one agent."
|
||||
},
|
||||
"FINISH": {
|
||||
"TITLE": "Your team is ready!",
|
||||
|
|
|
@ -89,7 +89,9 @@
|
|||
"DELETE_MESSAGE": "You need to have atleast one condition to save"
|
||||
},
|
||||
"ACTION": {
|
||||
"DELETE_MESSAGE": "You need to have atleast one action to save"
|
||||
"DELETE_MESSAGE": "You need to have atleast one action to save",
|
||||
"TEAM_MESSAGE_INPUT_PLACEHOLDER": "Enter your message here",
|
||||
"TEAM_DROPDOWN_PLACEHOLDER": "Select teams"
|
||||
},
|
||||
"TOGGLE": {
|
||||
"ACTIVATION_TITLE": "Activate Automation Rule",
|
||||
|
@ -102,6 +104,13 @@
|
|||
"DEACTIVATION_ERROR": "Could not Deactivate Automation, Please try again later",
|
||||
"CONFIRMATION_LABEL": "Ano",
|
||||
"CANCEL_LABEL": "Ne"
|
||||
},
|
||||
"ATTACHMENT": {
|
||||
"UPLOAD_ERROR": "Could not upload attachment, Please try again",
|
||||
"LABEL_IDLE": "Upload Attachment",
|
||||
"LABEL_UPLOADING": "Nahrávání...",
|
||||
"LABEL_UPLOADED": "Succesfully Uploaded",
|
||||
"LABEL_UPLOAD_FAILED": "Upload Failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"NO_MESSAGES": "Žádné zprávy",
|
||||
"NO_CONTENT": "Žádný obsah k dispozici",
|
||||
"HIDE_QUOTED_TEXT": "Hide Quoted Text",
|
||||
"SHOW_QUOTED_TEXT": "Show Quoted Text"
|
||||
"SHOW_QUOTED_TEXT": "Show Quoted Text",
|
||||
"MESSAGE_READ": "Read"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,14 @@
|
|||
"SUCCESS_MESSAGE": "Contacts saved successfully",
|
||||
"ERROR_MESSAGE": "Došlo k chybě, zkuste to prosím znovu"
|
||||
},
|
||||
"DELETE_NOTE": {
|
||||
"CONFIRM": {
|
||||
"TITLE": "Potvrdit odstranění",
|
||||
"MESSAGE": "Are you want sure to delete this note?",
|
||||
"YES": "Yes, Delete it",
|
||||
"NO": "No, Keep it"
|
||||
}
|
||||
},
|
||||
"DELETE_CONTACT": {
|
||||
"BUTTON_LABEL": "Delete Contact",
|
||||
"TITLE": "Delete contact",
|
||||
|
|
|
@ -60,6 +60,13 @@
|
|||
"NOTIFICATIONS_PAGE": {
|
||||
"HEADER": "Oznámení",
|
||||
"MARK_ALL_DONE": "Označit vše dokončeno",
|
||||
"DELETE_TITLE": "deleted",
|
||||
"UNREAD_NOTIFICATION": {
|
||||
"TITLE": "Unread Notifications",
|
||||
"ALL_NOTIFICATIONS": "View all notifications",
|
||||
"LOADING_UNREAD_MESSAGE": "Loading unread notifications...",
|
||||
"EMPTY_MESSAGE": "You have no unread notifications"
|
||||
},
|
||||
"LIST": {
|
||||
"LOADING_MESSAGE": "Načítání upozornění...",
|
||||
"404": "Žádná upozornění",
|
||||
|
@ -101,6 +108,7 @@
|
|||
"GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard",
|
||||
"GO_TO_CONTACTS_DASHBOARD": "Go to Contacts Dashboard",
|
||||
"GO_TO_REPORTS_OVERVIEW": "Go to Reports Overview",
|
||||
"GO_TO_CONVERSATION_REPORTS": "Go to Conversation Reports",
|
||||
"GO_TO_AGENT_REPORTS": "Go to Agent Reports",
|
||||
"GO_TO_LABEL_REPORTS": "Go to Label Reports",
|
||||
"GO_TO_INBOX_REPORTS": "Go to Inbox Reports",
|
||||
|
|
|
@ -341,10 +341,6 @@
|
|||
"AUTO_ASSIGNMENT_SUCCESS_MESSAGE": "Automatické přiřazení bylo úspěšně aktualizováno",
|
||||
"ERROR_MESSAGE": "Nelze aktualizovat barvu widgetu. Opakujte akci později."
|
||||
},
|
||||
"AUTO_ASSIGNMENT": {
|
||||
"ENABLED": "Povoleno",
|
||||
"DISABLED": "Zakázáno"
|
||||
},
|
||||
"EMAIL_COLLECT_BOX": {
|
||||
"ENABLED": "Povoleno",
|
||||
"DISABLED": "Zakázáno"
|
||||
|
@ -394,13 +390,16 @@
|
|||
"FEATURES": {
|
||||
"LABEL": "Funkce",
|
||||
"DISPLAY_FILE_PICKER": "Display file picker on the widget",
|
||||
"DISPLAY_EMOJI_PICKER": "Display emoji picker on the widget"
|
||||
"DISPLAY_EMOJI_PICKER": "Display emoji picker on the widget",
|
||||
"ALLOW_END_CONVERSATION": "Allow users to end conversation from the widget"
|
||||
},
|
||||
"SETTINGS_POPUP": {
|
||||
"MESSENGER_HEADING": "Messenger skript",
|
||||
"MESSENGER_SUB_HEAD": "Umístěte toto tlačítko dovnitř vašeho tělesného štítku",
|
||||
"INBOX_AGENTS": "Agenti",
|
||||
"INBOX_AGENTS_SUB_TEXT": "Přidat nebo odebrat agenty z této složky doručené pošty",
|
||||
"AGENT_ASSIGNMENT": "Conversation Assignment",
|
||||
"AGENT_ASSIGNMENT_SUB_TEXT": "Update conversation assignment settings",
|
||||
"UPDATE": "Aktualizovat",
|
||||
"ENABLE_EMAIL_COLLECT_BOX": "Enable email collect box",
|
||||
"ENABLE_EMAIL_COLLECT_BOX_SUB_TEXT": "Enable or disable email collect box on new conversation",
|
||||
|
@ -431,6 +430,15 @@
|
|||
},
|
||||
"PRE_CHAT_FORM": {
|
||||
"DESCRIPTION": "Pre chat forms enable you to capture user information before they start conversation with you.",
|
||||
"SET_FIELDS": "Pre chat form fields",
|
||||
"SET_FIELDS_HEADER": {
|
||||
"FIELDS": "Fields",
|
||||
"LABEL": "Label",
|
||||
"PLACE_HOLDER": "Placeholder",
|
||||
"KEY": "Key",
|
||||
"TYPE": "Type",
|
||||
"REQUIRED": "Required"
|
||||
},
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable pre chat form",
|
||||
"OPTIONS": {
|
||||
|
@ -439,7 +447,7 @@
|
|||
}
|
||||
},
|
||||
"PRE_CHAT_MESSAGE": {
|
||||
"LABEL": "Zpráva před chatem",
|
||||
"LABEL": "Pre chat message",
|
||||
"PLACEHOLDER": "This message would be visible to the users along with the form"
|
||||
},
|
||||
"REQUIRE_EMAIL": {
|
||||
|
@ -468,6 +476,7 @@
|
|||
"IMAP": {
|
||||
"TITLE": "IMAP",
|
||||
"SUBTITLE": "Set your IMAP details",
|
||||
"NOTE_TEXT": "To enable SMTP, please configure IMAP.",
|
||||
"UPDATE": "Update IMAP settings",
|
||||
"TOGGLE_AVAILABILITY": "Enable IMAP configuration for this inbox",
|
||||
"TOGGLE_HELP": "Enabling IMAP will help the user to recieve email",
|
||||
|
@ -483,9 +492,9 @@
|
|||
"LABEL": "Port",
|
||||
"PLACE_HOLDER": "Port"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "E-mailová adresa",
|
||||
"PLACE_HOLDER": "E-mailová adresa"
|
||||
"LOGIN": {
|
||||
"LABEL": "Přihlásit se",
|
||||
"PLACE_HOLDER": "Přihlásit se"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "Heslo",
|
||||
|
@ -511,9 +520,9 @@
|
|||
"LABEL": "Port",
|
||||
"PLACE_HOLDER": "Port"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "E-mailová adresa",
|
||||
"PLACE_HOLDER": "E-mailová adresa"
|
||||
"LOGIN": {
|
||||
"LABEL": "Přihlásit se",
|
||||
"PLACE_HOLDER": "Přihlásit se"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "Heslo",
|
||||
|
@ -526,7 +535,9 @@
|
|||
"ENCRYPTION": "Encryption",
|
||||
"SSL_TLS": "SSL/TLS",
|
||||
"START_TLS": "STARTTLS",
|
||||
"OPEN_SSL_VERIFY_MODE": "Open SSL Verify Mode"
|
||||
}
|
||||
"OPEN_SSL_VERIFY_MODE": "Open SSL Verify Mode",
|
||||
"AUTH_MECHANISM": "Authentication"
|
||||
},
|
||||
"NOTE": "Note: "
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,29 @@
|
|||
"INTEGRATION_SETTINGS": {
|
||||
"HEADER": "Integrace",
|
||||
"WEBHOOK": {
|
||||
"SUBSCRIBED_EVENTS": "Subscribed Events",
|
||||
"FORM": {
|
||||
"CANCEL": "Zrušit",
|
||||
"DESC": "Události webhooku vám poskytují reálné informace o tom, co se děje ve vašem Chatwoot účtu. Zadejte prosím platnou URL pro nastavení hovoru.",
|
||||
"SUBSCRIPTIONS": {
|
||||
"LABEL": "Events",
|
||||
"EVENTS": {
|
||||
"CONVERSATION_CREATED": "Conversation Created",
|
||||
"CONVERSATION_STATUS_CHANGED": "Conversation Status Changed",
|
||||
"CONVERSATION_UPDATED": "Conversation Updated",
|
||||
"MESSAGE_CREATED": "Message created",
|
||||
"MESSAGE_UPDATED": "Message updated",
|
||||
"WEBWIDGET_TRIGGERED": "Live chat widget opened by the user"
|
||||
}
|
||||
},
|
||||
"END_POINT": {
|
||||
"LABEL": "URL webového háčku",
|
||||
"PLACEHOLDER": "Příklad: https://example/api/webhook",
|
||||
"ERROR": "Zadejte prosím platnou URL"
|
||||
},
|
||||
"EDIT_SUBMIT": "Update webhook",
|
||||
"ADD_SUBMIT": "Create webhook"
|
||||
},
|
||||
"TITLE": "Webový háček",
|
||||
"CONFIGURE": "Konfigurace",
|
||||
"HEADER": "Nastavení webhooku",
|
||||
|
@ -20,35 +43,16 @@
|
|||
"EDIT": {
|
||||
"BUTTON_TEXT": "Upravit",
|
||||
"TITLE": "Edit webhook",
|
||||
"CANCEL": "Zrušit",
|
||||
"DESC": "Události webhooku vám poskytují reálné informace o tom, co se děje ve vašem Chatwoot účtu. Zadejte prosím platnou URL pro nastavení hovoru.",
|
||||
"FORM": {
|
||||
"END_POINT": {
|
||||
"LABEL": "URL webového háčku",
|
||||
"PLACEHOLDER": "Příklad: https://example/api/webhook",
|
||||
"ERROR": "Zadejte prosím platnou URL"
|
||||
},
|
||||
"SUBMIT": "Edit webhook"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Webhook URL updated successfully",
|
||||
"SUCCESS_MESSAGE": "Webhook configuration updated successfully",
|
||||
"ERROR_MESSAGE": "Nelze se připojit k Woot serveru, opakujte akci později"
|
||||
}
|
||||
},
|
||||
"ADD": {
|
||||
"CANCEL": "Zrušit",
|
||||
"TITLE": "Přidat nový webový háček",
|
||||
"DESC": "Události webhooku vám poskytují reálné informace o tom, co se děje ve vašem Chatwoot účtu. Zadejte prosím platnou URL pro nastavení hovoru.",
|
||||
"FORM": {
|
||||
"END_POINT": {
|
||||
"LABEL": "URL webového háčku",
|
||||
"PLACEHOLDER": "Příklad: https://example/api/webhook",
|
||||
"ERROR": "Zadejte prosím platnou URL"
|
||||
},
|
||||
"SUBMIT": "Vytvořit webový háček"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Webový háček byl úspěšně přidán",
|
||||
"SUCCESS_MESSAGE": "Webhook configuration added successfully",
|
||||
"ERROR_MESSAGE": "Nelze se připojit k Woot serveru, opakujte akci později"
|
||||
}
|
||||
},
|
||||
|
@ -60,7 +64,7 @@
|
|||
},
|
||||
"CONFIRM": {
|
||||
"TITLE": "Potvrdit odstranění",
|
||||
"MESSAGE": "Opravdu chcete odstranit ",
|
||||
"MESSAGE": "Are you sure to delete the webhook? (%{webhookURL})",
|
||||
"YES": "Ano, odstranit ",
|
||||
"NO": "No, Keep it"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"REPORT": {
|
||||
"HEADER": "Overview",
|
||||
"HEADER": "Konverzace",
|
||||
"LOADING_CHART": "Načítání dat mapy...",
|
||||
"NO_ENOUGH_DATA": "Pro vytvoření hlášení jsme neobdrželi dostatek dat, zkuste to prosím později.",
|
||||
"DOWNLOAD_AGENT_REPORTS": "Stáhnout reporty agentů",
|
||||
|
@ -18,12 +18,16 @@
|
|||
"DESC": "( celkem)"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Čas první odpovědi",
|
||||
"DESC": "(Průměrný)"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Čas rozlišení",
|
||||
"DESC": "(Průměrný)"
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Počet rozlišení",
|
||||
|
@ -108,7 +112,8 @@
|
|||
"id": 4,
|
||||
"groupBy": "Year"
|
||||
}
|
||||
]
|
||||
],
|
||||
"BUSINESS_HOURS": "Pracovní doba"
|
||||
},
|
||||
"AGENT_REPORTS": {
|
||||
"HEADER": "Agents Overview",
|
||||
|
@ -130,12 +135,16 @@
|
|||
"DESC": "( celkem)"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Čas první odpovědi",
|
||||
"DESC": "(Průměrný)"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Čas rozlišení",
|
||||
"DESC": "(Průměrný)"
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Počet rozlišení",
|
||||
|
@ -193,12 +202,16 @@
|
|||
"DESC": "( celkem)"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Čas první odpovědi",
|
||||
"DESC": "(Průměrný)"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Čas rozlišení",
|
||||
"DESC": "(Průměrný)"
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Počet rozlišení",
|
||||
|
@ -256,12 +269,16 @@
|
|||
"DESC": "( celkem)"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Čas první odpovědi",
|
||||
"DESC": "(Průměrný)"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Čas rozlišení",
|
||||
"DESC": "(Průměrný)"
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Počet rozlišení",
|
||||
|
@ -319,12 +336,16 @@
|
|||
"DESC": "( celkem)"
|
||||
},
|
||||
"FIRST_RESPONSE_TIME": {
|
||||
"NAME": "Čas první odpovědi",
|
||||
"DESC": "(Průměrný)"
|
||||
"NAME": "First Response Time",
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "First Response Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_TIME": {
|
||||
"NAME": "Čas rozlišení",
|
||||
"DESC": "(Průměrný)"
|
||||
"DESC": "(Průměrný)",
|
||||
"INFO_TEXT": "Total number of conversations used for computation:",
|
||||
"TOOLTIP_TEXT": "Resolution Time is %{metricValue} (based on %{conversationCount} conversations)"
|
||||
},
|
||||
"RESOLUTION_COUNT": {
|
||||
"NAME": "Počet rozlišení",
|
||||
|
@ -392,5 +413,33 @@
|
|||
"TOOLTIP": "Total number of responses / Total number of CSAT survey messages sent * 100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"OVERVIEW_REPORTS": {
|
||||
"HEADER": "Overview",
|
||||
"LIVE": "Live",
|
||||
"ACCOUNT_CONVERSATIONS": {
|
||||
"HEADER": "Open Conversations",
|
||||
"LOADING_MESSAGE": "Loading conversation metrics...",
|
||||
"OPEN": "Otevřít",
|
||||
"UNATTENDED": "Unattended",
|
||||
"UNASSIGNED": "Nepřiřazeno"
|
||||
},
|
||||
"AGENT_CONVERSATIONS": {
|
||||
"HEADER": "Conversations by agents",
|
||||
"LOADING_MESSAGE": "Loading agent metrics...",
|
||||
"NO_AGENTS": "There are no conversations by agents",
|
||||
"TABLE_HEADER": {
|
||||
"AGENT": "Agent",
|
||||
"OPEN": "OPEN",
|
||||
"UNATTENDED": "Unattended",
|
||||
"STATUS": "Stav"
|
||||
}
|
||||
},
|
||||
"AGENT_STATUS": {
|
||||
"HEADER": "Agent status",
|
||||
"ONLINE": "Online",
|
||||
"BUSY": "Zaneprázdněn",
|
||||
"OFFLINE": "Offline"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
"SUCCESS_MESSAGE": "Heslo bylo úspěšně změněno",
|
||||
"ERROR_MESSAGE": "Nelze se připojit k Woot serveru, opakujte akci později"
|
||||
},
|
||||
"CAPTCHA": {
|
||||
"ERROR": "Verification expired. Please solve captcha again."
|
||||
},
|
||||
"SUBMIT": "Odeslat"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,6 +131,10 @@
|
|||
"BUTTON_TEXT": "Kopírovat",
|
||||
"COPY_SUCCESSFUL": "Kód byl úspěšně zkopírován do schránky"
|
||||
},
|
||||
"SHOW_MORE_BLOCK": {
|
||||
"SHOW_MORE": "Show More",
|
||||
"SHOW_LESS": "Show Less"
|
||||
},
|
||||
"FILE_BUBBLE": {
|
||||
"DOWNLOAD": "Stáhnout",
|
||||
"UPLOADING": "Nahrávání..."
|
||||
|
@ -147,6 +151,7 @@
|
|||
},
|
||||
"SIDEBAR": {
|
||||
"CURRENTLY_VIEWING_ACCOUNT": "Currently viewing:",
|
||||
"SWITCH": "Switch",
|
||||
"CONVERSATIONS": "Konverzace",
|
||||
"ALL_CONVERSATIONS": "All Conversations",
|
||||
"MENTIONED_CONVERSATIONS": "Mentions",
|
||||
|
@ -173,7 +178,7 @@
|
|||
"NEW_LABEL": "New label",
|
||||
"NEW_TEAM": "New team",
|
||||
"NEW_INBOX": "New inbox",
|
||||
"REPORTS_OVERVIEW": "Overview",
|
||||
"REPORTS_CONVERSATION": "Konverzace",
|
||||
"CSAT": "CSAT",
|
||||
"CAMPAIGNS": "Kampaně",
|
||||
"ONGOING": "Ongoing",
|
||||
|
@ -183,7 +188,8 @@
|
|||
"REPORTS_INBOX": "Inbox",
|
||||
"REPORTS_TEAM": "Team",
|
||||
"SET_AVAILABILITY_TITLE": "Set yourself as",
|
||||
"BETA": "Beta"
|
||||
"BETA": "Beta",
|
||||
"REPORTS_OVERVIEW": "Overview"
|
||||
},
|
||||
"CREATE_ACCOUNT": {
|
||||
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
"PASSWORD": {
|
||||
"LABEL": "Heslo",
|
||||
"PLACEHOLDER": "Heslo",
|
||||
"ERROR": "Heslo je příliš krátké"
|
||||
"ERROR": "Heslo je příliš krátké",
|
||||
"IS_INVALID_PASSWORD": "Password should contain atleast 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character"
|
||||
},
|
||||
"CONFIRM_PASSWORD": {
|
||||
"LABEL": "Potvrzení hesla",
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
"SELECT_ALL": "select all agents",
|
||||
"SELECTED_COUNT": "%{selected} out of %{total} agents selected.",
|
||||
"BUTTON_TEXT": "Přidat agenty",
|
||||
"AGENT_VALIDATION_ERROR": "Select atleaset one agent."
|
||||
"AGENT_VALIDATION_ERROR": "Select at least one agent."
|
||||
},
|
||||
"FINISH": {
|
||||
"TITLE": "Your team is ready!",
|
||||
|
|
|
@ -89,7 +89,9 @@
|
|||
"DELETE_MESSAGE": "You need to have atleast one condition to save"
|
||||
},
|
||||
"ACTION": {
|
||||
"DELETE_MESSAGE": "You need to have atleast one action to save"
|
||||
"DELETE_MESSAGE": "You need to have atleast one action to save",
|
||||
"TEAM_MESSAGE_INPUT_PLACEHOLDER": "Enter your message here",
|
||||
"TEAM_DROPDOWN_PLACEHOLDER": "Select teams"
|
||||
},
|
||||
"TOGGLE": {
|
||||
"ACTIVATION_TITLE": "Activate Automation Rule",
|
||||
|
@ -102,6 +104,13 @@
|
|||
"DEACTIVATION_ERROR": "Could not Deactivate Automation, Please try again later",
|
||||
"CONFIRMATION_LABEL": "Yes",
|
||||
"CANCEL_LABEL": "No"
|
||||
},
|
||||
"ATTACHMENT": {
|
||||
"UPLOAD_ERROR": "Could not upload attachment, Please try again",
|
||||
"LABEL_IDLE": "Upload Attachment",
|
||||
"LABEL_UPLOADING": "Uploader...",
|
||||
"LABEL_UPLOADED": "Succesfully Uploaded",
|
||||
"LABEL_UPLOAD_FAILED": "Upload Failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"NO_MESSAGES": "No Messages",
|
||||
"NO_CONTENT": "No content available",
|
||||
"HIDE_QUOTED_TEXT": "Hide Quoted Text",
|
||||
"SHOW_QUOTED_TEXT": "Show Quoted Text"
|
||||
"SHOW_QUOTED_TEXT": "Show Quoted Text",
|
||||
"MESSAGE_READ": "Read"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,14 @@
|
|||
"SUCCESS_MESSAGE": "Contacts saved successfully",
|
||||
"ERROR_MESSAGE": "Der opstod en fejl. Prøv venligst igen"
|
||||
},
|
||||
"DELETE_NOTE": {
|
||||
"CONFIRM": {
|
||||
"TITLE": "Bekræft Sletning",
|
||||
"MESSAGE": "Are you want sure to delete this note?",
|
||||
"YES": "Yes, Delete it",
|
||||
"NO": "Nej, behold det"
|
||||
}
|
||||
},
|
||||
"DELETE_CONTACT": {
|
||||
"BUTTON_LABEL": "Delete Contact",
|
||||
"TITLE": "Delete contact",
|
||||
|
|
|
@ -60,6 +60,13 @@
|
|||
"NOTIFICATIONS_PAGE": {
|
||||
"HEADER": "Notifications",
|
||||
"MARK_ALL_DONE": "Mark All Done",
|
||||
"DELETE_TITLE": "deleted",
|
||||
"UNREAD_NOTIFICATION": {
|
||||
"TITLE": "Unread Notifications",
|
||||
"ALL_NOTIFICATIONS": "View all notifications",
|
||||
"LOADING_UNREAD_MESSAGE": "Loading unread notifications...",
|
||||
"EMPTY_MESSAGE": "You have no unread notifications"
|
||||
},
|
||||
"LIST": {
|
||||
"LOADING_MESSAGE": "Loading notifications...",
|
||||
"404": "No Notifications",
|
||||
|
@ -101,6 +108,7 @@
|
|||
"GO_TO_CONVERSATION_DASHBOARD": "Go to Conversation Dashboard",
|
||||
"GO_TO_CONTACTS_DASHBOARD": "Go to Contacts Dashboard",
|
||||
"GO_TO_REPORTS_OVERVIEW": "Go to Reports Overview",
|
||||
"GO_TO_CONVERSATION_REPORTS": "Go to Conversation Reports",
|
||||
"GO_TO_AGENT_REPORTS": "Go to Agent Reports",
|
||||
"GO_TO_LABEL_REPORTS": "Go to Label Reports",
|
||||
"GO_TO_INBOX_REPORTS": "Go to Inbox Reports",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue