Merge branch 'release/1.13.0'
This commit is contained in:
commit
ec1ddc06fd
6
Gemfile
6
Gemfile
@ -27,10 +27,10 @@ gem 'uglifier'
|
|||||||
gem 'flag_shih_tzu'
|
gem 'flag_shih_tzu'
|
||||||
# Random name generator for user names
|
# Random name generator for user names
|
||||||
gem 'haikunator'
|
gem 'haikunator'
|
||||||
# Template parsing safetly
|
# Template parsing safely
|
||||||
gem 'liquid'
|
gem 'liquid'
|
||||||
# Parse Markdown to HTML
|
# Parse Markdown to HTML
|
||||||
gem 'redcarpet'
|
gem 'commonmarker'
|
||||||
|
|
||||||
##-- for active storage --##
|
##-- for active storage --##
|
||||||
gem 'aws-sdk-s3', require: false
|
gem 'aws-sdk-s3', require: false
|
||||||
@ -44,6 +44,8 @@ gem 'pg'
|
|||||||
gem 'redis'
|
gem 'redis'
|
||||||
gem 'redis-namespace'
|
gem 'redis-namespace'
|
||||||
gem 'redis-rack-cache'
|
gem 'redis-rack-cache'
|
||||||
|
# super fast record imports in bulk
|
||||||
|
gem 'activerecord-import'
|
||||||
|
|
||||||
##--- gems for server & infra configuration ---##
|
##--- gems for server & infra configuration ---##
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails'
|
||||||
|
|||||||
132
Gemfile.lock
132
Gemfile.lock
@ -18,56 +18,58 @@ GEM
|
|||||||
specs:
|
specs:
|
||||||
action-cable-testing (0.6.1)
|
action-cable-testing (0.6.1)
|
||||||
actioncable (>= 5.0)
|
actioncable (>= 5.0)
|
||||||
actioncable (6.0.3.4)
|
actioncable (6.0.3.5)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.3.5)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (6.0.3.4)
|
actionmailbox (6.0.3.5)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.3.5)
|
||||||
activejob (= 6.0.3.4)
|
activejob (= 6.0.3.5)
|
||||||
activerecord (= 6.0.3.4)
|
activerecord (= 6.0.3.5)
|
||||||
activestorage (= 6.0.3.4)
|
activestorage (= 6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
actionmailer (6.0.3.4)
|
actionmailer (6.0.3.5)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.3.5)
|
||||||
actionview (= 6.0.3.4)
|
actionview (= 6.0.3.5)
|
||||||
activejob (= 6.0.3.4)
|
activejob (= 6.0.3.5)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (6.0.3.4)
|
actionpack (6.0.3.5)
|
||||||
actionview (= 6.0.3.4)
|
actionview (= 6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
rack (~> 2.0, >= 2.0.8)
|
rack (~> 2.0, >= 2.0.8)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actiontext (6.0.3.4)
|
actiontext (6.0.3.5)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.3.5)
|
||||||
activerecord (= 6.0.3.4)
|
activerecord (= 6.0.3.5)
|
||||||
activestorage (= 6.0.3.4)
|
activestorage (= 6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (6.0.3.4)
|
actionview (6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||||
activejob (6.0.3.4)
|
activejob (6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (6.0.3.4)
|
activemodel (6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
activerecord (6.0.3.4)
|
activerecord (6.0.3.5)
|
||||||
activemodel (= 6.0.3.4)
|
activemodel (= 6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
activestorage (6.0.3.4)
|
activerecord-import (1.0.7)
|
||||||
actionpack (= 6.0.3.4)
|
activerecord (>= 3.2)
|
||||||
activejob (= 6.0.3.4)
|
activestorage (6.0.3.5)
|
||||||
activerecord (= 6.0.3.4)
|
actionpack (= 6.0.3.5)
|
||||||
|
activejob (= 6.0.3.5)
|
||||||
|
activerecord (= 6.0.3.5)
|
||||||
marcel (~> 0.3.1)
|
marcel (~> 0.3.1)
|
||||||
activesupport (6.0.3.4)
|
activesupport (6.0.3.5)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
@ -145,7 +147,9 @@ GEM
|
|||||||
coderay (1.1.3)
|
coderay (1.1.3)
|
||||||
coercible (1.0.0)
|
coercible (1.0.0)
|
||||||
descendants_tracker (~> 0.0.1)
|
descendants_tracker (~> 0.0.1)
|
||||||
concurrent-ruby (1.1.7)
|
commonmarker (0.21.1)
|
||||||
|
ruby-enum (~> 0.5)
|
||||||
|
concurrent-ruby (1.1.8)
|
||||||
connection_pool (2.2.3)
|
connection_pool (2.2.3)
|
||||||
crack (0.4.3)
|
crack (0.4.3)
|
||||||
safe_yaml (~> 1.0.0)
|
safe_yaml (~> 1.0.0)
|
||||||
@ -181,7 +185,7 @@ GEM
|
|||||||
dotenv (= 2.7.6)
|
dotenv (= 2.7.6)
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
equalizer (0.0.11)
|
equalizer (0.0.11)
|
||||||
erubi (1.9.0)
|
erubi (1.10.0)
|
||||||
et-orbi (1.2.4)
|
et-orbi (1.2.4)
|
||||||
tzinfo
|
tzinfo
|
||||||
execjs (2.7.0)
|
execjs (2.7.0)
|
||||||
@ -257,7 +261,7 @@ GEM
|
|||||||
mime-types (~> 3.0)
|
mime-types (~> 3.0)
|
||||||
multi_xml (>= 0.5.2)
|
multi_xml (>= 0.5.2)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.8.5)
|
i18n (1.8.8)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
ice_nine (0.11.2)
|
ice_nine (0.11.2)
|
||||||
inflecto (0.0.2)
|
inflecto (0.0.2)
|
||||||
@ -294,7 +298,7 @@ GEM
|
|||||||
listen (3.3.3)
|
listen (3.3.3)
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
loofah (2.7.0)
|
loofah (2.9.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.7.1)
|
mail (2.7.1)
|
||||||
@ -311,7 +315,7 @@ GEM
|
|||||||
mini_magick (4.10.1)
|
mini_magick (4.10.1)
|
||||||
mini_mime (1.0.2)
|
mini_mime (1.0.2)
|
||||||
mini_portile2 (2.5.0)
|
mini_portile2 (2.5.0)
|
||||||
minitest (5.14.2)
|
minitest (5.14.3)
|
||||||
momentjs-rails (2.20.1)
|
momentjs-rails (2.20.1)
|
||||||
railties (>= 3.1)
|
railties (>= 3.1)
|
||||||
msgpack (1.3.3)
|
msgpack (1.3.3)
|
||||||
@ -321,8 +325,8 @@ GEM
|
|||||||
net-http-persistent (4.0.0)
|
net-http-persistent (4.0.0)
|
||||||
connection_pool (~> 2.2)
|
connection_pool (~> 2.2)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
nio4r (2.5.4)
|
nio4r (2.5.5)
|
||||||
nokogiri (1.11.0)
|
nokogiri (1.11.1)
|
||||||
mini_portile2 (~> 2.5.0)
|
mini_portile2 (~> 2.5.0)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
oauth (0.5.4)
|
oauth (0.5.4)
|
||||||
@ -353,38 +357,37 @@ GEM
|
|||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rails (6.0.3.4)
|
rails (6.0.3.5)
|
||||||
actioncable (= 6.0.3.4)
|
actioncable (= 6.0.3.5)
|
||||||
actionmailbox (= 6.0.3.4)
|
actionmailbox (= 6.0.3.5)
|
||||||
actionmailer (= 6.0.3.4)
|
actionmailer (= 6.0.3.5)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.3.5)
|
||||||
actiontext (= 6.0.3.4)
|
actiontext (= 6.0.3.5)
|
||||||
actionview (= 6.0.3.4)
|
actionview (= 6.0.3.5)
|
||||||
activejob (= 6.0.3.4)
|
activejob (= 6.0.3.5)
|
||||||
activemodel (= 6.0.3.4)
|
activemodel (= 6.0.3.5)
|
||||||
activerecord (= 6.0.3.4)
|
activerecord (= 6.0.3.5)
|
||||||
activestorage (= 6.0.3.4)
|
activestorage (= 6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 6.0.3.4)
|
railties (= 6.0.3.5)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.3.0)
|
rails-html-sanitizer (1.3.0)
|
||||||
loofah (~> 2.3)
|
loofah (~> 2.3)
|
||||||
railties (6.0.3.4)
|
railties (6.0.3.5)
|
||||||
actionpack (= 6.0.3.4)
|
actionpack (= 6.0.3.5)
|
||||||
activesupport (= 6.0.3.4)
|
activesupport (= 6.0.3.5)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.20.3, < 2.0)
|
thor (>= 0.20.3, < 2.0)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
rake (13.0.1)
|
rake (13.0.3)
|
||||||
rb-fsevent (0.10.4)
|
rb-fsevent (0.10.4)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
redcarpet (3.5.1)
|
|
||||||
redis (4.2.1)
|
redis (4.2.1)
|
||||||
redis-namespace (1.8.0)
|
redis-namespace (1.8.0)
|
||||||
redis (>= 3.0.4)
|
redis (>= 3.0.4)
|
||||||
@ -444,6 +447,8 @@ GEM
|
|||||||
rubocop (>= 0.87.0)
|
rubocop (>= 0.87.0)
|
||||||
rubocop-rspec (1.43.2)
|
rubocop-rspec (1.43.2)
|
||||||
rubocop (~> 0.87)
|
rubocop (~> 0.87)
|
||||||
|
ruby-enum (0.9.0)
|
||||||
|
i18n
|
||||||
ruby-progressbar (1.10.1)
|
ruby-progressbar (1.10.1)
|
||||||
ruby2ruby (2.4.4)
|
ruby2ruby (2.4.4)
|
||||||
ruby_parser (~> 3.1)
|
ruby_parser (~> 3.1)
|
||||||
@ -520,7 +525,7 @@ GEM
|
|||||||
inflecto
|
inflecto
|
||||||
virtus
|
virtus
|
||||||
telephone_number (1.4.9)
|
telephone_number (1.4.9)
|
||||||
thor (1.0.1)
|
thor (1.1.0)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tilt (2.0.10)
|
tilt (2.0.10)
|
||||||
time_diff (0.3.0)
|
time_diff (0.3.0)
|
||||||
@ -532,7 +537,7 @@ GEM
|
|||||||
nokogiri (>= 1.6, < 2.0)
|
nokogiri (>= 1.6, < 2.0)
|
||||||
twitty (0.1.1)
|
twitty (0.1.1)
|
||||||
oauth
|
oauth
|
||||||
tzinfo (1.2.8)
|
tzinfo (1.2.9)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
tzinfo-data (1.2020.1)
|
tzinfo-data (1.2020.1)
|
||||||
tzinfo (>= 1.0.0)
|
tzinfo (>= 1.0.0)
|
||||||
@ -575,13 +580,14 @@ GEM
|
|||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
wisper (2.0.0)
|
wisper (2.0.0)
|
||||||
zeitwerk (2.4.1)
|
zeitwerk (2.4.2)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
action-cable-testing
|
action-cable-testing
|
||||||
|
activerecord-import
|
||||||
acts-as-taggable-on
|
acts-as-taggable-on
|
||||||
administrate
|
administrate
|
||||||
annotate
|
annotate
|
||||||
@ -595,6 +601,7 @@ DEPENDENCIES
|
|||||||
bullet
|
bullet
|
||||||
bundle-audit
|
bundle-audit
|
||||||
byebug
|
byebug
|
||||||
|
commonmarker
|
||||||
cypress-on-rails (~> 1.0)
|
cypress-on-rails (~> 1.0)
|
||||||
database_cleaner
|
database_cleaner
|
||||||
devise
|
devise
|
||||||
@ -629,7 +636,6 @@ DEPENDENCIES
|
|||||||
pundit
|
pundit
|
||||||
rack-cors
|
rack-cors
|
||||||
rails
|
rails
|
||||||
redcarpet
|
|
||||||
redis
|
redis
|
||||||
redis-namespace
|
redis-namespace
|
||||||
redis-rack-cache
|
redis-rack-cache
|
||||||
|
|||||||
@ -1,21 +1,14 @@
|
|||||||
class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts::BaseController
|
||||||
before_action :ensure_contact
|
before_action :ensure_contact
|
||||||
before_action :ensure_inbox, only: [:create]
|
before_action :ensure_inbox, only: [:create]
|
||||||
before_action :validate_channel_type
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
source_id = params[:source_id] || SecureRandom.uuid
|
source_id = params[:source_id] || SecureRandom.uuid
|
||||||
@contact_inbox = ContactInbox.create(contact: @contact, inbox: @inbox, source_id: source_id)
|
@contact_inbox = ContactInbox.create!(contact: @contact, inbox: @inbox, source_id: source_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def validate_channel_type
|
|
||||||
return if @inbox.channel_type == 'Channel::Api'
|
|
||||||
|
|
||||||
render json: { error: 'Contact Inbox creation is only allowed in API inboxes' }, status: :unprocessable_entity
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_inbox
|
def ensure_inbox
|
||||||
@inbox = Current.account.inboxes.find(params[:inbox_id])
|
@inbox = Current.account.inboxes.find(params[:inbox_id])
|
||||||
end
|
end
|
||||||
|
|||||||
@ -22,6 +22,14 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
@contacts = fetch_contact_last_seen_at(contacts)
|
@contacts = fetch_contact_last_seen_at(contacts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def import
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
import = Current.account.data_imports.create!(data_type: 'contacts')
|
||||||
|
import.import_file.attach(params[:import_file])
|
||||||
|
end
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
# returns online contacts
|
# returns online contacts
|
||||||
def active
|
def active
|
||||||
contacts = Current.account.contacts.where(id: ::OnlineStatusTracker
|
contacts = Current.account.contacts.where(id: ::OnlineStatusTracker
|
||||||
@ -46,7 +54,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
rescue ActiveRecord::RecordInvalid => e
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
render json: {
|
render json: {
|
||||||
message: e.record.errors.full_messages.join(', '),
|
message: e.record.errors.full_messages.join(', '),
|
||||||
contact: Contact.find_by(email: contact_params[:email])
|
contact: Current.account.contacts.find_by(email: contact_params[:email])
|
||||||
}, status: :unprocessable_entity
|
}, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,20 @@
|
|||||||
class Api::V1::Accounts::Conversations::AssignmentsController < Api::V1::Accounts::Conversations::BaseController
|
class Api::V1::Accounts::Conversations::AssignmentsController < Api::V1::Accounts::Conversations::BaseController
|
||||||
# assign agent to a conversation
|
# assigns agent/team to a conversation
|
||||||
def create
|
def create
|
||||||
|
set_assignee
|
||||||
|
render json: @assignee
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_assignee
|
||||||
# if params[:assignee_id] is not a valid id, it will set to nil, hence unassigning the conversation
|
# if params[:assignee_id] is not a valid id, it will set to nil, hence unassigning the conversation
|
||||||
assignee = Current.account.users.find_by(id: params[:assignee_id])
|
if params.key?(:assignee_id)
|
||||||
@conversation.update_assignee(assignee)
|
@assignee = Current.account.users.find_by(id: params[:assignee_id])
|
||||||
render json: assignee
|
@conversation.update_assignee(@assignee)
|
||||||
|
elsif params.key?(:team_id)
|
||||||
|
@assignee = Current.account.teams.find_by(id: params[:team_id])
|
||||||
|
@conversation.update!(team: @assignee)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -22,9 +22,10 @@ class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseControl
|
|||||||
# get the list of user_ids from params
|
# get the list of user_ids from params
|
||||||
# the missing ones are the agents which are to be deleted from the inbox
|
# the missing ones are the agents which are to be deleted from the inbox
|
||||||
# the new ones are the agents which are to be added to the inbox
|
# the new ones are the agents which are to be added to the inbox
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
agents_to_be_added_ids.each { |user_id| @inbox.add_member(user_id) }
|
agents_to_be_added_ids.each { |user_id| @inbox.add_member(user_id) }
|
||||||
agents_to_be_removed_ids.each { |user_id| @inbox.remove_member(user_id) }
|
agents_to_be_removed_ids.each { |user_id| @inbox.remove_member(user_id) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def agents_to_be_added_ids
|
def agents_to_be_added_ids
|
||||||
|
|||||||
@ -89,6 +89,8 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||||||
:webhook_url,
|
:webhook_url,
|
||||||
:email,
|
:email,
|
||||||
:reply_time,
|
:reply_time,
|
||||||
|
:pre_chat_form_enabled,
|
||||||
|
{ pre_chat_form_options: [:pre_chat_message, :require_email] },
|
||||||
{ selected_feature_flags: [] }
|
{ selected_feature_flags: [] }
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseController
|
||||||
protect_from_forgery with: :null_session
|
RESULTS_PER_PAGE = 15
|
||||||
|
|
||||||
|
protect_from_forgery with: :null_session
|
||||||
before_action :fetch_notification, only: [:update]
|
before_action :fetch_notification, only: [:update]
|
||||||
before_action :set_primary_actor, only: [:read_all]
|
before_action :set_primary_actor, only: [:read_all]
|
||||||
before_action :set_current_page, only: [:index]
|
before_action :set_current_page, only: [:index]
|
||||||
@ -8,17 +9,18 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
|
|||||||
def index
|
def index
|
||||||
@unread_count = current_user.notifications.where(account_id: current_account.id, read_at: nil).count
|
@unread_count = current_user.notifications.where(account_id: current_account.id, read_at: nil).count
|
||||||
@count = notifications.count
|
@count = notifications.count
|
||||||
@notifications = notifications.page @current_page
|
@notifications = notifications.page(@current_page).per(RESULTS_PER_PAGE)
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_all
|
def read_all
|
||||||
|
# rubocop:disable Rails/SkipsModelValidations
|
||||||
if @primary_actor
|
if @primary_actor
|
||||||
current_user.notifications.where(account_id: current_account.id, primary_actor: @primary_actor, read_at: nil)
|
current_user.notifications.where(account_id: current_account.id, primary_actor: @primary_actor, read_at: nil)
|
||||||
.update(read_at: DateTime.now.utc)
|
.update_all(read_at: DateTime.now.utc)
|
||||||
else
|
else
|
||||||
current_user.notifications.where(account_id: current_account.id, read_at: nil).update(read_at: DateTime.now.utc)
|
current_user.notifications.where(account_id: current_account.id, read_at: nil).update_all(read_at: DateTime.now.utc)
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Rails/SkipsModelValidations
|
||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -7,17 +7,41 @@ class Api::V1::Accounts::TeamMembersController < Api::V1::Accounts::BaseControll
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
record = @team.team_members.find_or_create_by(user_id: params[:user_id])
|
ActiveRecord::Base.transaction do
|
||||||
@team_member = record.user
|
@team_members = params[:user_ids].map { |user_id| @team.add_member(user_id) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
members_to_be_added_ids.each { |user_id| @team.add_member(user_id) }
|
||||||
|
members_to_be_removed_ids.each { |user_id| @team.remove_member(user_id) }
|
||||||
|
end
|
||||||
|
@team_members = @team.members
|
||||||
|
render action: 'create'
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@team.team_members.find_by(user_id: params[:user_id])&.destroy
|
ActiveRecord::Base.transaction do
|
||||||
|
params[:user_ids].map { |user_id| @team.remove_member(user_id) }
|
||||||
|
end
|
||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def members_to_be_added_ids
|
||||||
|
params[:user_ids] - current_members_ids
|
||||||
|
end
|
||||||
|
|
||||||
|
def members_to_be_removed_ids
|
||||||
|
current_members_ids - params[:user_ids]
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_members_ids
|
||||||
|
@current_members_ids ||= @team.members.pluck(:id)
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_team
|
def fetch_team
|
||||||
@team = Current.account.teams.find(params[:team_id])
|
@team = Current.account.teams.find(params[:team_id])
|
||||||
end
|
end
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
class Api::V1::Accounts::Twitter::AuthorizationsController < Api::V1::Accounts::BaseController
|
||||||
|
include TwitterConcern
|
||||||
|
|
||||||
|
before_action :check_authorization
|
||||||
|
|
||||||
|
def create
|
||||||
|
@response = twitter_client.request_oauth_token(url: twitter_callback_url)
|
||||||
|
if @response.status == '200'
|
||||||
|
::Redis::Alfred.setex(oauth_token, Current.account.id)
|
||||||
|
render json: { success: true, url: oauth_authorize_endpoint(oauth_token) }
|
||||||
|
else
|
||||||
|
render json: { success: false }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def oauth_token
|
||||||
|
parsed_body['oauth_token']
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_authorize_endpoint(oauth_token)
|
||||||
|
"#{twitter_api_base_url}/oauth/authorize?oauth_token=#{oauth_token}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_authorization
|
||||||
|
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -20,11 +20,7 @@ class Api::V1::Widget::BaseController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def auth_token_params
|
def auth_token_params
|
||||||
@auth_token_params ||= ::Widget::TokenService.new(token: request.headers[header_name]).decode_token
|
@auth_token_params ||= ::Widget::TokenService.new(token: request.headers['X-Auth-Token']).decode_token
|
||||||
end
|
|
||||||
|
|
||||||
def header_name
|
|
||||||
'X-Auth-Token'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_web_widget
|
def set_web_widget
|
||||||
@ -39,6 +35,50 @@ class Api::V1::Widget::BaseController < ApplicationController
|
|||||||
@contact = @contact_inbox.contact
|
@contact = @contact_inbox.contact
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_conversation
|
||||||
|
::Conversation.create!(conversation_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def inbox
|
||||||
|
@inbox ||= ::Inbox.find_by(id: auth_token_params[:inbox_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_params
|
||||||
|
# FIXME: typo referrer in additional attributes, will probably require a migration.
|
||||||
|
{
|
||||||
|
account_id: inbox.account_id,
|
||||||
|
inbox_id: inbox.id,
|
||||||
|
contact_id: @contact.id,
|
||||||
|
contact_inbox_id: @contact_inbox.id,
|
||||||
|
additional_attributes: {
|
||||||
|
browser: browser_params,
|
||||||
|
referer: permitted_params[:message][:referer_url],
|
||||||
|
initiated_at: timestamp_params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_contact(email)
|
||||||
|
contact_with_email = @current_account.contacts.find_by(email: email)
|
||||||
|
if contact_with_email
|
||||||
|
@contact = ::ContactMergeAction.new(
|
||||||
|
account: @current_account,
|
||||||
|
base_contact: contact_with_email,
|
||||||
|
mergee_contact: @contact
|
||||||
|
).perform
|
||||||
|
else
|
||||||
|
@contact.update!(email: email, name: contact_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_email
|
||||||
|
permitted_params[:contact][:email].downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_name
|
||||||
|
params[:contact][:name] || contact_email.split('@')[0]
|
||||||
|
end
|
||||||
|
|
||||||
def browser_params
|
def browser_params
|
||||||
{
|
{
|
||||||
browser_name: browser.name,
|
browser_name: browser.name,
|
||||||
@ -48,4 +88,19 @@ class Api::V1::Widget::BaseController < ApplicationController
|
|||||||
platform_version: browser.platform.version
|
platform_version: browser.platform.version
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def timestamp_params
|
||||||
|
{ timestamp: permitted_params[:message][:timestamp] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def message_params
|
||||||
|
{
|
||||||
|
account_id: conversation.account_id,
|
||||||
|
sender: @contact,
|
||||||
|
content: permitted_params[:message][:content],
|
||||||
|
inbox_id: conversation.inbox_id,
|
||||||
|
echo_id: permitted_params[:message][:echo_id],
|
||||||
|
message_type: :incoming
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -12,7 +12,7 @@ class Api::V1::Widget::ContactsController < Api::V1::Widget::BaseController
|
|||||||
|
|
||||||
def process_hmac
|
def process_hmac
|
||||||
return if params[:identifier_hash].blank?
|
return if params[:identifier_hash].blank?
|
||||||
raise StandardError, 'HMAC failed: Invalid Identifer Hash Provided' unless valid_hmac?
|
raise StandardError, 'HMAC failed: Invalid Identifier Hash Provided' unless valid_hmac?
|
||||||
|
|
||||||
@contact_inbox.update(hmac_verified: true)
|
@contact_inbox.update(hmac_verified: true)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -5,6 +5,14 @@ class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController
|
|||||||
@conversation = conversation
|
@conversation = conversation
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
update_contact(contact_email) if @contact.email.blank? && contact_email.present?
|
||||||
|
@conversation = create_conversation
|
||||||
|
conversation.messages.create(message_params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def update_last_seen
|
def update_last_seen
|
||||||
head :ok && return if conversation.nil?
|
head :ok && return if conversation.nil?
|
||||||
|
|
||||||
@ -43,6 +51,6 @@ class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params
|
||||||
params.permit(:id, :typing_status, :website_token, :email)
|
params.permit(:id, :typing_status, :website_token, :email, contact: [:name, :email], message: [:content, :referer_url, :timestamp, :echo_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -39,44 +39,7 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_conversation
|
def set_conversation
|
||||||
@conversation = ::Conversation.create!(conversation_params) if conversation.nil?
|
@conversation = create_conversation if conversation.nil?
|
||||||
end
|
|
||||||
|
|
||||||
def message_params
|
|
||||||
{
|
|
||||||
account_id: conversation.account_id,
|
|
||||||
sender: @contact,
|
|
||||||
content: permitted_params[:message][:content],
|
|
||||||
inbox_id: conversation.inbox_id,
|
|
||||||
echo_id: permitted_params[:message][:echo_id],
|
|
||||||
message_type: :incoming
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def conversation_params
|
|
||||||
# FIXME: typo referrer in additional attributes
|
|
||||||
# will probably require a migration.
|
|
||||||
{
|
|
||||||
account_id: inbox.account_id,
|
|
||||||
inbox_id: inbox.id,
|
|
||||||
contact_id: @contact.id,
|
|
||||||
contact_inbox_id: @contact_inbox.id,
|
|
||||||
additional_attributes: {
|
|
||||||
browser: browser_params,
|
|
||||||
referer: permitted_params[:message][:referer_url],
|
|
||||||
initiated_at: timestamp_params
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def timestamp_params
|
|
||||||
{
|
|
||||||
timestamp: permitted_params[:message][:timestamp]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def inbox
|
|
||||||
@inbox ||= ::Inbox.find_by(id: auth_token_params[:inbox_id])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_finder_params
|
def message_finder_params
|
||||||
@ -90,36 +53,12 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
|
|||||||
@message_finder ||= MessageFinder.new(conversation, message_finder_params)
|
@message_finder ||= MessageFinder.new(conversation, message_finder_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_contact(email)
|
|
||||||
contact_with_email = @current_account.contacts.find_by(email: email)
|
|
||||||
if contact_with_email
|
|
||||||
@contact = ::ContactMergeAction.new(
|
|
||||||
account: @current_account,
|
|
||||||
base_contact: contact_with_email,
|
|
||||||
mergee_contact: @contact
|
|
||||||
).perform
|
|
||||||
else
|
|
||||||
@contact.update!(
|
|
||||||
email: email,
|
|
||||||
name: contact_name
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def contact_email
|
|
||||||
permitted_params[:contact][:email].downcase
|
|
||||||
end
|
|
||||||
|
|
||||||
def contact_name
|
|
||||||
contact_email.split('@')[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
def message_update_params
|
def message_update_params
|
||||||
params.permit(message: [{ submitted_values: [:name, :title, :value] }])
|
params.permit(message: [{ submitted_values: [:name, :title, :value] }])
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params
|
||||||
params.permit(:id, :before, :website_token, contact: [:email], message: [:content, :referer_url, :timestamp, :echo_id])
|
params.permit(:id, :before, :website_token, contact: [:name, :email], message: [:content, :referer_url, :timestamp, :echo_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_message
|
def set_message
|
||||||
|
|||||||
26
app/controllers/concerns/twitter_concern.rb
Normal file
26
app/controllers/concerns/twitter_concern.rb
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
module TwitterConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parsed_body
|
||||||
|
@parsed_body ||= Rack::Utils.parse_nested_query(@response.raw_response.body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def host
|
||||||
|
ENV.fetch('FRONTEND_URL', '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def twitter_client
|
||||||
|
Twitty::Facade.new do |config|
|
||||||
|
config.consumer_key = ENV.fetch('TWITTER_CONSUMER_KEY', nil)
|
||||||
|
config.consumer_secret = ENV.fetch('TWITTER_CONSUMER_SECRET', nil)
|
||||||
|
config.base_url = twitter_api_base_url
|
||||||
|
config.environment = ENV.fetch('TWITTER_ENVIRONMENT', '')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def twitter_api_base_url
|
||||||
|
'https://api.twitter.com'
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -20,7 +20,8 @@ class DashboardController < ActionController::Base
|
|||||||
'TERMS_URL',
|
'TERMS_URL',
|
||||||
'PRIVACY_URL',
|
'PRIVACY_URL',
|
||||||
'DISPLAY_MANIFEST',
|
'DISPLAY_MANIFEST',
|
||||||
'CREATE_NEW_ACCOUNT_FROM_DASHBOARD'
|
'CREATE_NEW_ACCOUNT_FROM_DASHBOARD',
|
||||||
|
'CHATWOOT_INBOX_TOKEN'
|
||||||
).merge(
|
).merge(
|
||||||
APP_VERSION: Chatwoot.config[:version]
|
APP_VERSION: Chatwoot.config[:version]
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
class Twitter::AuthorizationsController < Twitter::BaseController
|
|
||||||
def create
|
|
||||||
@response = twitter_client.request_oauth_token(url: twitter_callback_url)
|
|
||||||
|
|
||||||
if @response.status == '200'
|
|
||||||
::Redis::Alfred.setex(oauth_token, account.id)
|
|
||||||
redirect_to oauth_authorize_endpoint(oauth_token)
|
|
||||||
else
|
|
||||||
redirect_to app_new_twitter_inbox_url(account_id: account.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def oauth_token
|
|
||||||
parsed_body['oauth_token']
|
|
||||||
end
|
|
||||||
|
|
||||||
def user
|
|
||||||
@user ||= User.find_by(id: params[:user_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def account
|
|
||||||
@account ||= user.account
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_authorize_endpoint(oauth_token)
|
|
||||||
"#{twitter_api_base_url}/oauth/authorize?oauth_token=#{oauth_token}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -1,24 +1,3 @@
|
|||||||
class Twitter::BaseController < ApplicationController
|
class Twitter::BaseController < ApplicationController
|
||||||
private
|
include TwitterConcern
|
||||||
|
|
||||||
def parsed_body
|
|
||||||
@parsed_body ||= Rack::Utils.parse_nested_query(@response.raw_response.body)
|
|
||||||
end
|
|
||||||
|
|
||||||
def host
|
|
||||||
ENV.fetch('FRONTEND_URL', '')
|
|
||||||
end
|
|
||||||
|
|
||||||
def twitter_client
|
|
||||||
Twitty::Facade.new do |config|
|
|
||||||
config.consumer_key = ENV.fetch('TWITTER_CONSUMER_KEY', nil)
|
|
||||||
config.consumer_secret = ENV.fetch('TWITTER_CONSUMER_SECRET', nil)
|
|
||||||
config.base_url = twitter_api_base_url
|
|
||||||
config.environment = ENV.fetch('TWITTER_ENVIRONMENT', '')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def twitter_api_base_url
|
|
||||||
'https://api.twitter.com'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
class Twitter::CallbacksController < Twitter::BaseController
|
class Twitter::CallbacksController < Twitter::BaseController
|
||||||
|
include TwitterConcern
|
||||||
|
|
||||||
def show
|
def show
|
||||||
return redirect_to twitter_app_redirect_url if permitted_params[:denied]
|
return redirect_to twitter_app_redirect_url if permitted_params[:denied]
|
||||||
|
|
||||||
|
|||||||
@ -22,10 +22,12 @@ class ConversationFinder
|
|||||||
|
|
||||||
def perform
|
def perform
|
||||||
set_inboxes
|
set_inboxes
|
||||||
|
set_team
|
||||||
set_assignee_type
|
set_assignee_type
|
||||||
|
|
||||||
find_all_conversations
|
find_all_conversations
|
||||||
filter_by_status unless params[:q]
|
filter_by_status unless params[:q]
|
||||||
|
filter_by_team if @team
|
||||||
filter_by_labels if params[:labels]
|
filter_by_labels if params[:labels]
|
||||||
filter_by_query if params[:q]
|
filter_by_query if params[:q]
|
||||||
|
|
||||||
@ -61,6 +63,10 @@ class ConversationFinder
|
|||||||
@assignee_type = params[:assignee_type]
|
@assignee_type = params[:assignee_type]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_team
|
||||||
|
@team = current_account.teams.find(params[:team_id]) if params[:team_id]
|
||||||
|
end
|
||||||
|
|
||||||
def find_all_conversations
|
def find_all_conversations
|
||||||
@conversations = current_account.conversations.where(inbox_id: @inbox_ids)
|
@conversations = current_account.conversations.where(inbox_id: @inbox_ids)
|
||||||
end
|
end
|
||||||
@ -87,6 +93,10 @@ class ConversationFinder
|
|||||||
@conversations = @conversations.where(status: params[:status] || DEFAULT_STATUS)
|
@conversations = @conversations.where(status: params[:status] || DEFAULT_STATUS)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_by_team
|
||||||
|
@conversations = @conversations.where(team: @team)
|
||||||
|
end
|
||||||
|
|
||||||
def filter_by_labels
|
def filter_by_labels
|
||||||
@conversations = @conversations.tagged_with(params[:labels], any: true)
|
@conversations = @conversations.tagged_with(params[:labels], any: true)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -9,6 +9,8 @@ module FileTypeHelper
|
|||||||
'image/bmp'
|
'image/bmp'
|
||||||
].include?(content_type)
|
].include?(content_type)
|
||||||
|
|
||||||
|
return :audio if content_type.include?('audio/')
|
||||||
|
|
||||||
:file
|
:file
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
14
app/javascript/dashboard/api/channel/twitterClient.js
Normal file
14
app/javascript/dashboard/api/channel/twitterClient.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* global axios */
|
||||||
|
import ApiClient from '../ApiClient';
|
||||||
|
|
||||||
|
class TwitterClient extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('twitter', { accountScoped: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
generateAuthorization() {
|
||||||
|
return axios.post(`${this.url}/authorization`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TwitterClient();
|
||||||
@ -6,10 +6,11 @@ class ConversationApi extends ApiClient {
|
|||||||
super('conversations', { accountScoped: true });
|
super('conversations', { accountScoped: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
get({ inboxId, status, assigneeType, page, labels }) {
|
get({ inboxId, status, assigneeType, page, labels, teamId }) {
|
||||||
return axios.get(this.url, {
|
return axios.get(this.url, {
|
||||||
params: {
|
params: {
|
||||||
inbox_id: inboxId,
|
inbox_id: inboxId,
|
||||||
|
team_id: teamId,
|
||||||
status,
|
status,
|
||||||
assignee_type: assigneeType,
|
assignee_type: assigneeType,
|
||||||
page,
|
page,
|
||||||
@ -56,13 +57,14 @@ class ConversationApi extends ApiClient {
|
|||||||
return axios.post(`${this.url}/${conversationId}/unmute`);
|
return axios.post(`${this.url}/${conversationId}/unmute`);
|
||||||
}
|
}
|
||||||
|
|
||||||
meta({ inboxId, status, assigneeType, labels }) {
|
meta({ inboxId, status, assigneeType, labels, teamId }) {
|
||||||
return axios.get(`${this.url}/meta`, {
|
return axios.get(`${this.url}/meta`, {
|
||||||
params: {
|
params: {
|
||||||
inbox_id: inboxId,
|
inbox_id: inboxId,
|
||||||
status,
|
status,
|
||||||
assignee_type: assigneeType,
|
assignee_type: assigneeType,
|
||||||
labels,
|
labels,
|
||||||
|
team_id: teamId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
33
app/javascript/dashboard/api/notifications.js
Normal file
33
app/javascript/dashboard/api/notifications.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* global axios */
|
||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
class NotificationsAPI extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('notifications', { accountScoped: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
get(page) {
|
||||||
|
return axios.get(`${this.url}?page=${page}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotifications(contactId) {
|
||||||
|
return axios.get(`${this.url}/${contactId}/notifications`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnreadCount() {
|
||||||
|
return axios.get(`${this.url}/unread_count`);
|
||||||
|
}
|
||||||
|
|
||||||
|
read(primaryActorType, primaryActorId) {
|
||||||
|
return axios.post(`${this.url}/read_all`, {
|
||||||
|
primary_actor_type: primaryActorType,
|
||||||
|
primary_actor_id: primaryActorId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
readAll() {
|
||||||
|
return axios.post(`${this.url}/read_all`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new NotificationsAPI();
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import TwitterClient from '../../channel/twitterClient';
|
||||||
|
import ApiClient from '../../ApiClient';
|
||||||
|
|
||||||
|
describe('#TwitterClient', () => {
|
||||||
|
it('creates correct instance', () => {
|
||||||
|
expect(TwitterClient).toBeInstanceOf(ApiClient);
|
||||||
|
expect(TwitterClient).toHaveProperty('generateAuthorization');
|
||||||
|
});
|
||||||
|
});
|
||||||
13
app/javascript/dashboard/api/specs/notifications.spec.js
Normal file
13
app/javascript/dashboard/api/specs/notifications.spec.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import notifications from '../notifications';
|
||||||
|
import ApiClient from '../ApiClient';
|
||||||
|
|
||||||
|
describe('#NotificationAPI', () => {
|
||||||
|
it('creates correct instance', () => {
|
||||||
|
expect(notifications).toBeInstanceOf(ApiClient);
|
||||||
|
expect(notifications).toHaveProperty('get');
|
||||||
|
expect(notifications).toHaveProperty('getNotifications');
|
||||||
|
expect(notifications).toHaveProperty('getUnreadCount');
|
||||||
|
expect(notifications).toHaveProperty('read');
|
||||||
|
expect(notifications).toHaveProperty('readAll');
|
||||||
|
});
|
||||||
|
});
|
||||||
15
app/javascript/dashboard/api/specs/teams.spec.js
Normal file
15
app/javascript/dashboard/api/specs/teams.spec.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import teams from '../teams';
|
||||||
|
import ApiClient from '../ApiClient';
|
||||||
|
|
||||||
|
describe('#TeamsAPI', () => {
|
||||||
|
it('creates correct instance', () => {
|
||||||
|
expect(teams).toBeInstanceOf(ApiClient);
|
||||||
|
expect(teams).toHaveProperty('get');
|
||||||
|
expect(teams).toHaveProperty('show');
|
||||||
|
expect(teams).toHaveProperty('create');
|
||||||
|
expect(teams).toHaveProperty('update');
|
||||||
|
expect(teams).toHaveProperty('delete');
|
||||||
|
expect(teams).toHaveProperty('getAgents');
|
||||||
|
expect(teams).toHaveProperty('addAgents');
|
||||||
|
});
|
||||||
|
});
|
||||||
20
app/javascript/dashboard/api/teams.js
Normal file
20
app/javascript/dashboard/api/teams.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* global axios */
|
||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
export class TeamsAPI extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('teams', { accountScoped: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
getAgents({ teamId }) {
|
||||||
|
return axios.get(`${this.url}/${teamId}/team_members`);
|
||||||
|
}
|
||||||
|
|
||||||
|
addAgents({ teamId, agentsList }) {
|
||||||
|
return axios.post(`${this.url}/${teamId}/team_members`, {
|
||||||
|
user_ids: agentsList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TeamsAPI();
|
||||||
@ -8,23 +8,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.reply-box__top {
|
.reply-box__top {
|
||||||
.canned {
|
|
||||||
@include elegant-card;
|
|
||||||
background: $color-white;
|
|
||||||
border-bottom: $space-small solid $color-white;
|
|
||||||
border-top: $space-small solid $color-white;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
max-height: 14rem;
|
|
||||||
overflow: auto;
|
|
||||||
position: absolute;
|
|
||||||
width: 24rem;
|
|
||||||
z-index: 100;
|
|
||||||
|
|
||||||
.active a {
|
|
||||||
background: $color-woot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
color: $medium-gray;
|
color: $medium-gray;
|
||||||
|
|||||||
@ -49,8 +49,10 @@
|
|||||||
margin-top: $space-micro;
|
margin-top: $space-micro;
|
||||||
|
|
||||||
.inbox-icon {
|
.inbox-icon {
|
||||||
|
$icon-top-space: -1px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: $space-micro;
|
margin-right: $space-micro;
|
||||||
|
margin-top: $icon-top-space;
|
||||||
min-width: $space-normal;
|
min-width: $space-normal;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@ -70,9 +72,9 @@
|
|||||||
.dropdown-pane {
|
.dropdown-pane {
|
||||||
@include elegant-card;
|
@include elegant-card;
|
||||||
@include border-light;
|
@include border-light;
|
||||||
|
bottom: 6rem;
|
||||||
display: block;
|
display: block;
|
||||||
left: 18%;
|
left: 5rem;
|
||||||
top: -110px;
|
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
@ -118,42 +120,6 @@
|
|||||||
line-height: $global-lineheight;
|
line-height: $global-lineheight;
|
||||||
}
|
}
|
||||||
|
|
||||||
.current-user {
|
|
||||||
@include flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
.current-user--data {
|
|
||||||
@include flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.current-user--name {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
font-weight: $font-weight-medium;
|
|
||||||
line-height: 1;
|
|
||||||
margin-bottom: $space-smaller;
|
|
||||||
margin-left: $space-one;
|
|
||||||
margin-top: $space-micro;
|
|
||||||
}
|
|
||||||
|
|
||||||
.current-user--role {
|
|
||||||
color: $color-gray;
|
|
||||||
font-size: $font-size-mini;
|
|
||||||
margin-bottom: $zero;
|
|
||||||
margin-left: $space-one;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.current-user--options {
|
|
||||||
font-size: $font-size-big;
|
|
||||||
margin-bottom: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hamburger--menu {
|
.hamburger--menu {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@ -80,6 +80,10 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
activeTeam: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -128,12 +132,16 @@ export default {
|
|||||||
status: this.activeStatus,
|
status: this.activeStatus,
|
||||||
page: this.currentPage + 1,
|
page: this.currentPage + 1,
|
||||||
labels: this.label ? [this.label] : undefined,
|
labels: this.label ? [this.label] : undefined,
|
||||||
|
teamId: this.activeTeam.name ? this.activeTeam.id : undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
pageTitle() {
|
pageTitle() {
|
||||||
if (this.inbox.name) {
|
if (this.inbox.name) {
|
||||||
return this.inbox.name;
|
return this.inbox.name;
|
||||||
}
|
}
|
||||||
|
if (this.activeTeam.name) {
|
||||||
|
return this.activeTeam.name;
|
||||||
|
}
|
||||||
if (this.label) {
|
if (this.label) {
|
||||||
return `#${this.label}`;
|
return `#${this.label}`;
|
||||||
}
|
}
|
||||||
@ -162,6 +170,9 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
activeTeam() {
|
||||||
|
this.resetAndFetchData();
|
||||||
|
},
|
||||||
conversationInbox() {
|
conversationInbox() {
|
||||||
this.resetAndFetchData();
|
this.resetAndFetchData();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<button class="button small button--copy-code" @click="onCopy">
|
<button class="button small button--copy-code" @click="onCopy">
|
||||||
{{ $t('COMPONENTS.CODE.BUTTON_TEXT') }}
|
{{ $t('COMPONENTS.CODE.BUTTON_TEXT') }}
|
||||||
</button>
|
</button>
|
||||||
<highlightjs :language="lang" :code="script" />
|
<highlightjs v-if="script" :language="lang" :code="script" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
script: {
|
script: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
default: '',
|
||||||
},
|
},
|
||||||
lang: {
|
lang: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
@ -13,6 +13,11 @@
|
|||||||
:key="item.toState"
|
:key="item.toState"
|
||||||
:menu-item="item"
|
:menu-item="item"
|
||||||
/>
|
/>
|
||||||
|
<sidebar-item
|
||||||
|
v-if="shouldShowTeams"
|
||||||
|
:key="teamSection.toState"
|
||||||
|
:menu-item="teamSection"
|
||||||
|
/>
|
||||||
<sidebar-item
|
<sidebar-item
|
||||||
v-if="shouldShowInboxes"
|
v-if="shouldShowInboxes"
|
||||||
:key="inboxSection.toState"
|
:key="inboxSection.toState"
|
||||||
@ -30,173 +35,65 @@
|
|||||||
<availability-status />
|
<availability-status />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bottom-nav app-context-menu">
|
<div class="bottom-nav app-context-menu" @click="toggleOptions">
|
||||||
<transition name="menu-slide">
|
<agent-details @show-options="toggleOptions" />
|
||||||
<div
|
<notification-bell />
|
||||||
v-if="showOptionsMenu"
|
<span class="current-user--options icon ion-android-more-vertical" />
|
||||||
v-on-clickaway="showOptions"
|
<options-menu
|
||||||
class="dropdown-pane top"
|
:show="showOptionsMenu"
|
||||||
>
|
@toggle-accounts="toggleAccountModal"
|
||||||
<ul class="vertical dropdown menu">
|
@show-support-chat-window="toggleSupportChatWindow"
|
||||||
<li v-if="showChangeAccountOption">
|
@close="toggleOptions"
|
||||||
<button
|
|
||||||
class="button clear change-accounts--button"
|
|
||||||
@click="changeAccount"
|
|
||||||
>
|
|
||||||
{{ $t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS') }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<router-link :to="`/app/accounts/${accountId}/profile/settings`">
|
|
||||||
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" @click.prevent="logout()">
|
|
||||||
{{ $t('SIDEBAR_ITEMS.LOGOUT') }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<div class="current-user" @click.prevent="showOptions()">
|
|
||||||
<thumbnail
|
|
||||||
:src="currentUser.avatar_url"
|
|
||||||
:username="currentUserAvailableName"
|
|
||||||
/>
|
|
||||||
<div class="current-user--data">
|
|
||||||
<h3 class="current-user--name">
|
|
||||||
{{ currentUserAvailableName }}
|
|
||||||
</h3>
|
|
||||||
<h5 v-if="currentRole" class="current-user--role">
|
|
||||||
{{ $t(`AGENT_MGMT.AGENT_TYPES.${currentRole.toUpperCase()}`) }}
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<span class="current-user--options icon ion-android-more-vertical" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<woot-modal
|
|
||||||
:show="showAccountModal"
|
|
||||||
:on-close="onClose"
|
|
||||||
class="account-selector--modal"
|
|
||||||
>
|
|
||||||
<woot-modal-header
|
|
||||||
:header-title="$t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS')"
|
|
||||||
:header-content="$t('SIDEBAR_ITEMS.SELECTOR_SUBTITLE')"
|
|
||||||
/>
|
/>
|
||||||
<div
|
</div>
|
||||||
v-for="account in currentUser.accounts"
|
|
||||||
:key="account.id"
|
|
||||||
class="account-selector"
|
|
||||||
>
|
|
||||||
<a :href="`/app/accounts/${account.id}/dashboard`">
|
|
||||||
<i v-if="account.id === accountId" class="ion ion-ios-checkmark" />
|
|
||||||
<label :for="account.name" class="account--details">
|
|
||||||
<div class="account--name">{{ account.name }}</div>
|
|
||||||
<div class="account--role">{{ account.role }}</div>
|
|
||||||
</label>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="globalConfig.createNewAccountFromDashboard"
|
|
||||||
class="modal-footer delete-item"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="button success large expanded nice"
|
|
||||||
@click="createAccount"
|
|
||||||
>
|
|
||||||
{{ $t('CREATE_ACCOUNT.NEW_ACCOUNT') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</woot-modal>
|
|
||||||
|
|
||||||
<woot-modal
|
<account-selector
|
||||||
|
:show-account-modal="showAccountModal"
|
||||||
|
@close-account-modal="toggleAccountModal"
|
||||||
|
@show-create-account-modal="openCreateAccountModal"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<add-account-modal
|
||||||
:show="showCreateAccountModal"
|
:show="showCreateAccountModal"
|
||||||
:on-close="onCloseCreate"
|
@close-account-create-modal="closeCreateAccountModal"
|
||||||
class="account-selector--modal"
|
/>
|
||||||
>
|
|
||||||
<div class="column content-box">
|
|
||||||
<woot-modal-header
|
|
||||||
:header-title="$t('CREATE_ACCOUNT.NEW_ACCOUNT')"
|
|
||||||
:header-content="$t('CREATE_ACCOUNT.SELECTOR_SUBTITLE')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<form class="row" @submit.prevent="addAccount()">
|
|
||||||
<div class="medium-12 columns">
|
|
||||||
<label :class="{ error: $v.accountName.$error }">
|
|
||||||
{{ $t('CREATE_ACCOUNT.FORM.NAME.LABEL') }}
|
|
||||||
<input
|
|
||||||
v-model.trim="accountName"
|
|
||||||
type="text"
|
|
||||||
:placeholder="$t('CREATE_ACCOUNT.FORM.NAME.PLACEHOLDER')"
|
|
||||||
@input="$v.accountName.$touch"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer medium-12 columns">
|
|
||||||
<div class="medium-12 columns">
|
|
||||||
<woot-submit-button
|
|
||||||
:disabled="
|
|
||||||
$v.accountName.$invalid ||
|
|
||||||
$v.accountName.$invalid ||
|
|
||||||
uiFlags.isCreating
|
|
||||||
"
|
|
||||||
:button-text="$t('CREATE_ACCOUNT.FORM.SUBMIT')"
|
|
||||||
:loading="uiFlags.isCreating"
|
|
||||||
button-class="large expanded"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</woot-modal>
|
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
|
||||||
|
|
||||||
import adminMixin from '../../mixins/isAdmin';
|
import adminMixin from '../../mixins/isAdmin';
|
||||||
import Auth from '../../api/auth';
|
|
||||||
import SidebarItem from './SidebarItem';
|
import SidebarItem from './SidebarItem';
|
||||||
import AvailabilityStatus from './AvailabilityStatus';
|
import AvailabilityStatus from './AvailabilityStatus';
|
||||||
import { frontendURL } from '../../helper/URLHelper';
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
import Thumbnail from '../widgets/Thumbnail';
|
|
||||||
import { getSidebarItems } from '../../i18n/default-sidebar';
|
import { getSidebarItems } from '../../i18n/default-sidebar';
|
||||||
import { required, minLength } from 'vuelidate/lib/validators';
|
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
import NotificationBell from './sidebarComponents/NotificationBell';
|
||||||
|
import AgentDetails from './sidebarComponents/AgentDetails.vue';
|
||||||
|
import OptionsMenu from './sidebarComponents/OptionsMenu.vue';
|
||||||
|
import AccountSelector from './sidebarComponents/AccountSelector.vue';
|
||||||
|
import AddAccountModal from './sidebarComponents/AddAccountModal.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
AgentDetails,
|
||||||
SidebarItem,
|
SidebarItem,
|
||||||
Thumbnail,
|
|
||||||
AvailabilityStatus,
|
AvailabilityStatus,
|
||||||
|
NotificationBell,
|
||||||
|
OptionsMenu,
|
||||||
|
AccountSelector,
|
||||||
|
AddAccountModal,
|
||||||
},
|
},
|
||||||
mixins: [clickaway, adminMixin, alertMixin],
|
mixins: [adminMixin, alertMixin],
|
||||||
props: {
|
|
||||||
route: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showOptionsMenu: false,
|
showOptionsMenu: false,
|
||||||
showAccountModal: false,
|
showAccountModal: false,
|
||||||
showCreateAccountModal: false,
|
showCreateAccountModal: false,
|
||||||
accountName: '',
|
|
||||||
vertical: 'bottom',
|
|
||||||
horizontal: 'center',
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
validations: {
|
|
||||||
accountName: {
|
|
||||||
required,
|
|
||||||
minLength: minLength(1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentUser: 'getCurrentUser',
|
currentUser: 'getCurrentUser',
|
||||||
@ -204,18 +101,10 @@ export default {
|
|||||||
inboxes: 'inboxes/getInboxes',
|
inboxes: 'inboxes/getInboxes',
|
||||||
accountId: 'getCurrentAccountId',
|
accountId: 'getCurrentAccountId',
|
||||||
currentRole: 'getCurrentRole',
|
currentRole: 'getCurrentRole',
|
||||||
uiFlags: 'agents/getUIFlags',
|
|
||||||
accountLabels: 'labels/getLabelsOnSidebar',
|
accountLabels: 'labels/getLabelsOnSidebar',
|
||||||
|
teams: 'teams/getMyTeams',
|
||||||
}),
|
}),
|
||||||
currentUserAvailableName() {
|
|
||||||
return this.currentUser.name;
|
|
||||||
},
|
|
||||||
showChangeAccountOption() {
|
|
||||||
if (this.globalConfig.createNewAccountFromDashboard) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return this.currentUser.accounts.length > 1;
|
|
||||||
},
|
|
||||||
sidemenuItems() {
|
sidemenuItems() {
|
||||||
return getSidebarItems(this.accountId);
|
return getSidebarItems(this.accountId);
|
||||||
},
|
},
|
||||||
@ -242,6 +131,9 @@ export default {
|
|||||||
shouldShowInboxes() {
|
shouldShowInboxes() {
|
||||||
return this.sidemenuItems.common.routes.includes(this.currentRoute);
|
return this.sidemenuItems.common.routes.includes(this.currentRoute);
|
||||||
},
|
},
|
||||||
|
shouldShowTeams() {
|
||||||
|
return this.shouldShowInboxes && this.teams.length;
|
||||||
|
},
|
||||||
inboxSection() {
|
inboxSection() {
|
||||||
return {
|
return {
|
||||||
icon: 'ion-folder',
|
icon: 'ion-folder',
|
||||||
@ -281,15 +173,57 @@ export default {
|
|||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
teamSection() {
|
||||||
|
return {
|
||||||
|
icon: 'ion-ios-people',
|
||||||
|
label: 'TEAMS',
|
||||||
|
hasSubMenu: true,
|
||||||
|
newLink: true,
|
||||||
|
key: 'team',
|
||||||
|
cssClass: 'menu-title align-justify teams-sidebar-menu',
|
||||||
|
toState: frontendURL(`accounts/${this.accountId}/settings/teams`),
|
||||||
|
toStateName: 'teams_list',
|
||||||
|
children: this.teams.map(team => ({
|
||||||
|
id: team.id,
|
||||||
|
label: team.name,
|
||||||
|
truncateLabel: true,
|
||||||
|
toState: frontendURL(`accounts/${this.accountId}/team/${team.id}`),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
dashboardPath() {
|
dashboardPath() {
|
||||||
return frontendURL(`accounts/${this.accountId}/dashboard`);
|
return frontendURL(`accounts/${this.accountId}/dashboard`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
currentUser(newUserInfo, oldUserInfo) {
|
||||||
|
if (!oldUserInfo.email && newUserInfo.email) {
|
||||||
|
this.setChatwootUser();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch('labels/get');
|
this.$store.dispatch('labels/get');
|
||||||
this.$store.dispatch('inboxes/get');
|
this.$store.dispatch('inboxes/get');
|
||||||
|
this.$store.dispatch('notifications/unReadCount');
|
||||||
|
this.$store.dispatch('teams/get');
|
||||||
|
this.setChatwootUser();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
toggleSupportChatWindow() {
|
||||||
|
window.$chatwoot.toggle();
|
||||||
|
},
|
||||||
|
setChatwootUser() {
|
||||||
|
if (!this.currentUser.email || !this.globalConfig.chatwootInboxToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.$chatwoot.setUser(this.currentUser.email, {
|
||||||
|
name: this.currentUser.name,
|
||||||
|
email: this.currentUser.email,
|
||||||
|
avatar_url: this.currentUser.avatar_url,
|
||||||
|
identifier_hash: this.currentUser.hmac_identifier,
|
||||||
|
});
|
||||||
|
},
|
||||||
filterMenuItemsByRole(menuItems) {
|
filterMenuItemsByRole(menuItems) {
|
||||||
if (!this.currentRole) {
|
if (!this.currentRole) {
|
||||||
return [];
|
return [];
|
||||||
@ -301,41 +235,19 @@ export default {
|
|||||||
) > -1
|
) > -1
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
logout() {
|
toggleOptions() {
|
||||||
Auth.logout();
|
|
||||||
},
|
|
||||||
showOptions() {
|
|
||||||
this.showOptionsMenu = !this.showOptionsMenu;
|
this.showOptionsMenu = !this.showOptionsMenu;
|
||||||
},
|
},
|
||||||
changeAccount() {
|
toggleAccountModal() {
|
||||||
this.showAccountModal = true;
|
this.showAccountModal = !this.showAccountModal;
|
||||||
},
|
},
|
||||||
onClose() {
|
openCreateAccountModal() {
|
||||||
this.showAccountModal = false;
|
|
||||||
},
|
|
||||||
createAccount() {
|
|
||||||
this.showAccountModal = false;
|
this.showAccountModal = false;
|
||||||
this.showCreateAccountModal = true;
|
this.showCreateAccountModal = true;
|
||||||
},
|
},
|
||||||
onCloseCreate() {
|
closeCreateAccountModal() {
|
||||||
this.showCreateAccountModal = false;
|
this.showCreateAccountModal = false;
|
||||||
},
|
},
|
||||||
async addAccount() {
|
|
||||||
try {
|
|
||||||
const account_id = await this.$store.dispatch('accounts/create', {
|
|
||||||
account_name: this.accountName,
|
|
||||||
});
|
|
||||||
this.onClose();
|
|
||||||
this.showAlert(this.$t('CREATE_ACCOUNT.API.SUCCESS_MESSAGE'));
|
|
||||||
window.location = `/app/accounts/${account_id}/dashboard`;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.response.status === 422) {
|
|
||||||
this.showAlert(this.$t('CREATE_ACCOUNT.API.EXIST_MESSAGE'));
|
|
||||||
} else {
|
|
||||||
this.showAlert(this.$t('CREATE_ACCOUNT.API.ERROR_MESSAGE'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -406,7 +318,23 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-context-menu {
|
.app-context-menu {
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
height: 6rem;
|
height: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.current-user--options {
|
||||||
|
font-size: $font-size-big;
|
||||||
|
margin-bottom: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teams-sidebar-menu + .nested.vertical.menu {
|
||||||
|
padding-left: calc(var(--space-medium) - var(--space-one));
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -155,7 +155,6 @@ export default {
|
|||||||
|
|
||||||
.wrap {
|
.wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-color--display {
|
.label-color--display {
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<woot-modal
|
||||||
|
:show="showAccountModal"
|
||||||
|
:on-close="() => $emit('close-account-modal')"
|
||||||
|
class="account-selector--modal"
|
||||||
|
>
|
||||||
|
<woot-modal-header
|
||||||
|
:header-title="$t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS')"
|
||||||
|
:header-content="$t('SIDEBAR_ITEMS.SELECTOR_SUBTITLE')"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-for="account in currentUser.accounts"
|
||||||
|
:key="account.id"
|
||||||
|
class="account-selector"
|
||||||
|
>
|
||||||
|
<a :href="`/app/accounts/${account.id}/dashboard`">
|
||||||
|
<i v-if="account.id === accountId" class="ion ion-ios-checkmark" />
|
||||||
|
<label :for="account.name" class="account--details">
|
||||||
|
<div class="account--name">{{ account.name }}</div>
|
||||||
|
<div class="account--role">{{ account.role }}</div>
|
||||||
|
</label>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="globalConfig.createNewAccountFromDashboard"
|
||||||
|
class="modal-footer delete-item"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="button success large expanded nice"
|
||||||
|
@click="$emit('show-create-account-modal')"
|
||||||
|
>
|
||||||
|
{{ $t('CREATE_ACCOUNT.NEW_ACCOUNT') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</woot-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
showAccountModal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
accountId: 'getCurrentAccountId',
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
|
globalConfig: 'globalConfig/get',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<woot-modal
|
||||||
|
:show="show"
|
||||||
|
:on-close="() => $emit('close-account-create-modal')"
|
||||||
|
class="account-selector--modal"
|
||||||
|
>
|
||||||
|
<div class="column content-box">
|
||||||
|
<woot-modal-header
|
||||||
|
:header-title="$t('CREATE_ACCOUNT.NEW_ACCOUNT')"
|
||||||
|
:header-content="$t('CREATE_ACCOUNT.SELECTOR_SUBTITLE')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<form class="row" @submit.prevent="addAccount">
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<label :class="{ error: $v.accountName.$error }">
|
||||||
|
{{ $t('CREATE_ACCOUNT.FORM.NAME.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model.trim="accountName"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('CREATE_ACCOUNT.FORM.NAME.PLACEHOLDER')"
|
||||||
|
@input="$v.accountName.$touch"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer medium-12 columns">
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<woot-submit-button
|
||||||
|
:disabled="
|
||||||
|
$v.accountName.$invalid ||
|
||||||
|
$v.accountName.$invalid ||
|
||||||
|
uiFlags.isCreating
|
||||||
|
"
|
||||||
|
:button-text="$t('CREATE_ACCOUNT.FORM.SUBMIT')"
|
||||||
|
:loading="uiFlags.isCreating"
|
||||||
|
button-class="large expanded"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</woot-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { required, minLength } from 'vuelidate/lib/validators';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [alertMixin],
|
||||||
|
props: {
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
accountName: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
validations: {
|
||||||
|
accountName: {
|
||||||
|
required,
|
||||||
|
minLength: minLength(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
uiFlags: 'agents/getUIFlags',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async addAccount() {
|
||||||
|
try {
|
||||||
|
const account_id = await this.$store.dispatch('accounts/create', {
|
||||||
|
account_name: this.accountName,
|
||||||
|
});
|
||||||
|
this.$emit('close-account-create-modal');
|
||||||
|
this.showAlert(this.$t('CREATE_ACCOUNT.API.SUCCESS_MESSAGE'));
|
||||||
|
window.location = `/app/accounts/${account_id}/dashboard`;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response.status === 422) {
|
||||||
|
this.showAlert(this.$t('CREATE_ACCOUNT.API.EXIST_MESSAGE'));
|
||||||
|
} else {
|
||||||
|
this.showAlert(this.$t('CREATE_ACCOUNT.API.ERROR_MESSAGE'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="current-user--row">
|
||||||
|
<thumbnail
|
||||||
|
:src="currentUser.avatar_url"
|
||||||
|
:username="currentUserAvailableName"
|
||||||
|
/>
|
||||||
|
<div class="current-user--data">
|
||||||
|
<h3 class="current-user--name text-truncate">
|
||||||
|
{{ currentUserAvailableName }}
|
||||||
|
</h3>
|
||||||
|
<h5 v-if="currentRole" class="current-user--role">
|
||||||
|
{{ $t(`AGENT_MGMT.AGENT_TYPES.${currentRole.toUpperCase()}`) }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import Thumbnail from '../../widgets/Thumbnail';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Thumbnail,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
|
currentRole: 'getCurrentRole',
|
||||||
|
}),
|
||||||
|
currentUserAvailableName() {
|
||||||
|
return this.currentUser.name;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.current-user--row {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-user--data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.current-user--name {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
margin-bottom: var(--space-micro);
|
||||||
|
margin-left: var(--space-one);
|
||||||
|
max-width: 12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-user--role {
|
||||||
|
color: var(--color-gray);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
margin-bottom: var(--zero);
|
||||||
|
margin-left: var(--space-one);
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<span class="notifications icon ion-ios-bell" @click.stop="showNotification">
|
||||||
|
<span v-if="unreadCount" class="unread-badge">{{ unreadCount }}</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
accountId: 'getCurrentAccountId',
|
||||||
|
notificationMetadata: 'notifications/getMeta',
|
||||||
|
}),
|
||||||
|
unreadCount() {
|
||||||
|
if (!this.notificationMetadata.unreadCount) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.notificationMetadata.unreadCount < 100
|
||||||
|
? this.notificationMetadata.unreadCount
|
||||||
|
: '99+';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showNotification() {
|
||||||
|
this.$router.push(`/app/accounts/${this.accountId}/notifications`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.notifications {
|
||||||
|
font-size: var(--font-size-big);
|
||||||
|
margin-bottom: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-top: auto;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.unread-badge {
|
||||||
|
background: var(--r-300);
|
||||||
|
border-radius: var(--space-small);
|
||||||
|
color: var(--white);
|
||||||
|
font-size: var(--font-size-micro);
|
||||||
|
font-weight: var(--font-weight-black);
|
||||||
|
left: var(--space-slab);
|
||||||
|
padding: 0 var(--space-smaller);
|
||||||
|
position: absolute;
|
||||||
|
top: var(--space-smaller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<transition name="menu-slide">
|
||||||
|
<div
|
||||||
|
v-if="show"
|
||||||
|
v-on-clickaway="() => $emit('close')"
|
||||||
|
class="dropdown-pane top"
|
||||||
|
>
|
||||||
|
<ul class="vertical dropdown menu">
|
||||||
|
<li v-if="showChangeAccountOption">
|
||||||
|
<button
|
||||||
|
class="button clear change-accounts--button"
|
||||||
|
@click="$emit('toggle-accounts')"
|
||||||
|
>
|
||||||
|
{{ $t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li v-if="globalConfig.chatwootInboxToken">
|
||||||
|
<button
|
||||||
|
class="button clear change-accounts--button"
|
||||||
|
@click="$emit('show-support-chat-window')"
|
||||||
|
>
|
||||||
|
Contact Support
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<router-link :to="`/app/accounts/${accountId}/profile/settings`">
|
||||||
|
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" @click.prevent="logout">
|
||||||
|
{{ $t('SIDEBAR_ITEMS.LOGOUT') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import Auth from '../../../api/auth';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [clickaway],
|
||||||
|
props: {
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
|
globalConfig: 'globalConfig/get',
|
||||||
|
accountId: 'getCurrentAccountId',
|
||||||
|
}),
|
||||||
|
showChangeAccountOption() {
|
||||||
|
if (this.globalConfig.createNewAccountFromDashboard) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return this.currentUser.accounts.length > 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logout() {
|
||||||
|
Auth.logout();
|
||||||
|
|
||||||
|
if (this.globalConfig.chatwootInboxToken) {
|
||||||
|
window.$chatwoot.reset();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -172,6 +172,7 @@ export default {
|
|||||||
margin-left: -2px;
|
margin-left: -2px;
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
padding: var(--space-small) var(--space-normal);
|
padding: var(--space-small) var(--space-normal);
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
@ -1,55 +1,113 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="editor" class="editor-root"></div>
|
<div class="editor-root">
|
||||||
|
<tag-agents
|
||||||
|
v-if="showUserMentions && isPrivate"
|
||||||
|
:search-key="mentionSearchKey"
|
||||||
|
@click="insertMentionNode"
|
||||||
|
/>
|
||||||
|
<div ref="editor"></div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { EditorState } from 'prosemirror-state';
|
|
||||||
import { EditorView } from 'prosemirror-view';
|
import { EditorView } from 'prosemirror-view';
|
||||||
import {
|
import { defaultMarkdownSerializer } from 'prosemirror-markdown';
|
||||||
schema,
|
|
||||||
defaultMarkdownParser,
|
|
||||||
defaultMarkdownSerializer,
|
|
||||||
} from 'prosemirror-markdown';
|
|
||||||
import { wootWriterSetup } from '@chatwoot/prosemirror-schema';
|
|
||||||
|
|
||||||
const TYPING_INDICATOR_IDLE_TIME = 4000;
|
const TYPING_INDICATOR_IDLE_TIME = 4000;
|
||||||
|
|
||||||
const createState = (content, placeholder) =>
|
import {
|
||||||
EditorState.create({
|
addMentionsToMarkdownSerializer,
|
||||||
doc: defaultMarkdownParser.parse(content),
|
addMentionsToMarkdownParser,
|
||||||
plugins: wootWriterSetup({ schema, placeholder }),
|
schemaWithMentions,
|
||||||
|
} from '@chatwoot/prosemirror-schema/src/mentions/schema';
|
||||||
|
import {
|
||||||
|
suggestionsPlugin,
|
||||||
|
triggerCharacters,
|
||||||
|
} from '@chatwoot/prosemirror-schema/src/mentions/plugin';
|
||||||
|
import TagAgents from '../conversation/TagAgents.vue';
|
||||||
|
import { EditorState } from 'prosemirror-state';
|
||||||
|
import { defaultMarkdownParser } from 'prosemirror-markdown';
|
||||||
|
import { wootWriterSetup } from '@chatwoot/prosemirror-schema';
|
||||||
|
|
||||||
|
const createState = (content, placeholder, plugins = []) => {
|
||||||
|
return EditorState.create({
|
||||||
|
doc: addMentionsToMarkdownParser(defaultMarkdownParser).parse(content),
|
||||||
|
plugins: wootWriterSetup({
|
||||||
|
schema: schemaWithMentions,
|
||||||
|
placeholder,
|
||||||
|
plugins,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WootMessageEditor',
|
name: 'WootMessageEditor',
|
||||||
|
components: { TagAgents },
|
||||||
props: {
|
props: {
|
||||||
value: { type: String, default: '' },
|
value: { type: String, default: '' },
|
||||||
placeholder: { type: String, default: '' },
|
placeholder: { type: String, default: '' },
|
||||||
|
isPrivate: { type: Boolean, default: false },
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
lastValue: null,
|
lastValue: null,
|
||||||
|
showUserMentions: false,
|
||||||
|
mentionSearchKey: '',
|
||||||
|
editorView: null,
|
||||||
|
range: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
plugins() {
|
||||||
|
return [
|
||||||
|
suggestionsPlugin({
|
||||||
|
matcher: triggerCharacters('@'),
|
||||||
|
onEnter: args => {
|
||||||
|
this.showUserMentions = true;
|
||||||
|
this.range = args.range;
|
||||||
|
this.editorView = args.view;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onChange: args => {
|
||||||
|
this.editorView = args.view;
|
||||||
|
this.range = args.range;
|
||||||
|
|
||||||
|
this.mentionSearchKey = args.text.replace('@', '');
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onExit: () => {
|
||||||
|
this.mentionSearchKey = '';
|
||||||
|
this.showUserMentions = false;
|
||||||
|
this.editorView = null;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onKeyDown: ({ event }) => {
|
||||||
|
return event.keyCode === 13 && this.showUserMentions;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
showUserMentions(updatedValue) {
|
||||||
|
this.$emit('toggle-user-mention', this.isPrivate && updatedValue);
|
||||||
|
},
|
||||||
value(newValue) {
|
value(newValue) {
|
||||||
if (newValue !== this.lastValue) {
|
if (newValue !== this.lastValue) {
|
||||||
this.state = createState(newValue, this.placeholder);
|
this.state = createState(newValue, this.placeholder, this.plugins);
|
||||||
this.view.updateState(this.state);
|
this.view.updateState(this.state);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.state = createState(this.value, this.placeholder);
|
this.state = createState(this.value, this.placeholder, this.plugins);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.view = new EditorView(this.$refs.editor, {
|
this.view = new EditorView(this.$refs.editor, {
|
||||||
state: this.state,
|
state: this.state,
|
||||||
dispatchTransaction: tx => {
|
dispatchTransaction: tx => {
|
||||||
this.state = this.state.apply(tx);
|
this.state = this.state.apply(tx);
|
||||||
this.view.updateState(this.state);
|
this.emitOnChange();
|
||||||
this.lastValue = defaultMarkdownSerializer.serialize(this.state.doc);
|
|
||||||
this.$emit('input', this.lastValue);
|
|
||||||
},
|
},
|
||||||
handleDOMEvents: {
|
handleDOMEvents: {
|
||||||
keyup: () => {
|
keyup: () => {
|
||||||
@ -65,6 +123,33 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
insertMentionNode(mentionItem) {
|
||||||
|
if (!this.view) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const node = this.view.state.schema.nodes.mention.create({
|
||||||
|
userId: mentionItem.key,
|
||||||
|
userFullName: mentionItem.label,
|
||||||
|
});
|
||||||
|
|
||||||
|
const tr = this.view.state.tr.replaceWith(
|
||||||
|
this.range.from,
|
||||||
|
this.range.to,
|
||||||
|
node
|
||||||
|
);
|
||||||
|
this.state = this.view.state.apply(tr);
|
||||||
|
return this.emitOnChange();
|
||||||
|
},
|
||||||
|
emitOnChange() {
|
||||||
|
this.view.updateState(this.state);
|
||||||
|
this.lastValue = addMentionsToMarkdownSerializer(
|
||||||
|
defaultMarkdownSerializer
|
||||||
|
).serialize(this.state.doc);
|
||||||
|
this.$emit('input', this.lastValue);
|
||||||
|
},
|
||||||
|
hideMentions() {
|
||||||
|
this.showUserMentions = false;
|
||||||
|
},
|
||||||
resetTyping() {
|
resetTyping() {
|
||||||
this.$emit('typing-off');
|
this.$emit('typing-off');
|
||||||
this.idleTimer = null;
|
this.idleTimer = null;
|
||||||
@ -115,4 +200,14 @@ export default {
|
|||||||
max-height: 12rem;
|
max-height: 12rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.is-private {
|
||||||
|
.prosemirror-mention-node {
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
background: var(--s-300);
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
padding: 1px 4px;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
</file-upload>
|
</file-upload>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="enableRichEditor"
|
v-if="enableRichEditor && !isOnPrivateNote"
|
||||||
class="button clear button--emoji"
|
class="button clear button--emoji"
|
||||||
:title="$t('CONVERSATION.REPLYBOX.TIP_FORMAT_ICON')"
|
:title="$t('CONVERSATION.REPLYBOX.TIP_FORMAT_ICON')"
|
||||||
@click="toggleFormatMode"
|
@click="toggleFormatMode"
|
||||||
@ -31,6 +31,17 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-wrap">
|
<div class="right-wrap">
|
||||||
|
<div v-if="isFormatMode" class="enter-to-send--checkbox">
|
||||||
|
<input
|
||||||
|
:checked="enterToSendEnabled"
|
||||||
|
type="checkbox"
|
||||||
|
value="enterToSend"
|
||||||
|
@input="toggleEnterToSend"
|
||||||
|
/>
|
||||||
|
<label for="enterToSend">
|
||||||
|
{{ $t('CONVERSATION.REPLYBOX.ENTER_TO_SEND') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
class="button nice primary button--send"
|
class="button nice primary button--send"
|
||||||
:class="buttonClass"
|
:class="buttonClass"
|
||||||
@ -91,10 +102,18 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
isOnPrivateNote: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
enableRichEditor: {
|
enableRichEditor: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
enterToSendEnabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isNote() {
|
isNote() {
|
||||||
@ -119,6 +138,9 @@ export default {
|
|||||||
toggleFormatMode() {
|
toggleFormatMode() {
|
||||||
this.setFormatMode(!this.isFormatMode);
|
this.setFormatMode(!this.isFormatMode);
|
||||||
},
|
},
|
||||||
|
toggleEnterToSend() {
|
||||||
|
this.$emit('toggleEnterToSend', !this.enterToSendEnabled);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -173,8 +195,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.left-wrap {
|
.left-wrap {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--reply {
|
.button--reply {
|
||||||
@ -185,4 +207,21 @@ export default {
|
|||||||
color: var(--s-600);
|
color: var(--s-600);
|
||||||
font-size: var(--font-size-default);
|
font-size: var(--font-size-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-wrap {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.enter-to-send--checkbox {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: var(--s-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,88 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul
|
<mention-box :items="items" @mention-select="handleMentionClick" />
|
||||||
v-if="cannedMessages.length"
|
|
||||||
id="canned-list"
|
|
||||||
class="vertical dropdown menu canned"
|
|
||||||
:style="{ top: getTopPadding() + 'rem' }"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
v-for="(item, index) in cannedMessages"
|
|
||||||
:id="`canned-${index}`"
|
|
||||||
:key="item.short_code"
|
|
||||||
:class="{ active: index === selectedIndex }"
|
|
||||||
@click="onListItemSelection(index)"
|
|
||||||
@mouseover="onHover(index)"
|
|
||||||
>
|
|
||||||
<a class="text-truncate">
|
|
||||||
<strong>{{ item.short_code }}</strong> - {{ item.content }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
import MentionBox from '../mentions/MentionBox.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['onKeyenter', 'onClick'],
|
components: { MentionBox },
|
||||||
data() {
|
props: {
|
||||||
return {
|
searchKey: {
|
||||||
selectedIndex: 0,
|
type: String,
|
||||||
};
|
default: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
cannedMessages: 'getCannedResponses',
|
cannedMessages: 'getCannedResponses',
|
||||||
}),
|
}),
|
||||||
|
items() {
|
||||||
|
return this.cannedMessages.map(cannedMessage => ({
|
||||||
|
label: cannedMessage.short_code,
|
||||||
|
key: cannedMessage.short_code,
|
||||||
|
description: cannedMessage.content,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
searchKey() {
|
||||||
|
this.fetchCannedResponses();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
document.addEventListener('keydown', this.keyListener);
|
this.fetchCannedResponses();
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
document.removeEventListener('keydown', this.keyListener);
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getTopPadding() {
|
fetchCannedResponses() {
|
||||||
if (this.cannedMessages.length <= 4) {
|
this.$store.dispatch('getCannedResponse', { searchKey: this.searchKey });
|
||||||
return -this.cannedMessages.length * 3.5;
|
|
||||||
}
|
|
||||||
return -14;
|
|
||||||
},
|
},
|
||||||
isUp(e) {
|
handleMentionClick(item = {}) {
|
||||||
return e.keyCode === 38 || (e.ctrlKey && e.keyCode === 80); // UP, Ctrl-P
|
this.$emit('click', item.description);
|
||||||
},
|
|
||||||
isDown(e) {
|
|
||||||
return e.keyCode === 40 || (e.ctrlKey && e.keyCode === 78); // DOWN, Ctrl-N
|
|
||||||
},
|
|
||||||
isEnter(e) {
|
|
||||||
return e.keyCode === 13;
|
|
||||||
},
|
|
||||||
keyListener(e) {
|
|
||||||
if (this.isUp(e)) {
|
|
||||||
if (!this.selectedIndex) {
|
|
||||||
this.selectedIndex = this.cannedMessages.length - 1;
|
|
||||||
} else {
|
|
||||||
this.selectedIndex -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.isDown(e)) {
|
|
||||||
if (this.selectedIndex === this.cannedMessages.length - 1) {
|
|
||||||
this.selectedIndex = 0;
|
|
||||||
} else {
|
|
||||||
this.selectedIndex += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.isEnter(e)) {
|
|
||||||
this.onKeyenter(this.cannedMessages[this.selectedIndex].content);
|
|
||||||
}
|
|
||||||
this.$el.scrollTop = 34 * this.selectedIndex;
|
|
||||||
},
|
|
||||||
onHover(index) {
|
|
||||||
this.selectedIndex = index;
|
|
||||||
},
|
|
||||||
onListItemSelection(index) {
|
|
||||||
this.selectedIndex = index;
|
|
||||||
this.onClick(this.cannedMessages[this.selectedIndex].content);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,6 +35,12 @@
|
|||||||
{{ this.$t(`${attachmentIconKey}.CONTENT`) }}
|
{{ this.$t(`${attachmentIconKey}.CONTENT`) }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
<p v-else class="conversation--message">
|
||||||
|
<i class="ion-android-alert"></i>
|
||||||
|
<span>
|
||||||
|
{{ this.$t(`CHAT_LIST.NO_MESSAGES`) }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
<div class="conversation--meta">
|
<div class="conversation--meta">
|
||||||
<span class="timestamp">
|
<span class="timestamp">
|
||||||
{{ dynamicTime(chat.timestamp) }}
|
{{ dynamicTime(chat.timestamp) }}
|
||||||
|
|||||||
@ -21,8 +21,11 @@
|
|||||||
:url="attachment.data_url"
|
:url="attachment.data_url"
|
||||||
:readable-time="readableTime"
|
:readable-time="readableTime"
|
||||||
/>
|
/>
|
||||||
|
<audio v-else-if="attachment.file_type === 'audio'" controls>
|
||||||
|
<source :src="attachment.data_url" />
|
||||||
|
</audio>
|
||||||
<bubble-file
|
<bubble-file
|
||||||
v-if="attachment.file_type !== 'image'"
|
v-else
|
||||||
:url="attachment.data_url"
|
:url="attachment.data_url"
|
||||||
:readable-time="readableTime"
|
:readable-time="readableTime"
|
||||||
/>
|
/>
|
||||||
@ -72,7 +75,7 @@ import Spinner from 'shared/components/Spinner';
|
|||||||
import contentTypeMixin from 'shared/mixins/contentTypeMixin';
|
import contentTypeMixin from 'shared/mixins/contentTypeMixin';
|
||||||
import BubbleActions from './bubble/Actions';
|
import BubbleActions from './bubble/Actions';
|
||||||
import { MESSAGE_TYPE, MESSAGE_STATUS } from 'shared/constants/messages';
|
import { MESSAGE_TYPE, MESSAGE_STATUS } from 'shared/constants/messages';
|
||||||
|
import { generateBotMessageContent } from './helpers/botMessageContentHelper';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BubbleActions,
|
BubbleActions,
|
||||||
@ -99,7 +102,19 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
message() {
|
message() {
|
||||||
return this.formatMessage(this.data.content, this.isATweet);
|
const botMessageContent = generateBotMessageContent(
|
||||||
|
this.contentType,
|
||||||
|
this.contentAttributes,
|
||||||
|
this.$t('CONVERSATION.NO_RESPONSE')
|
||||||
|
);
|
||||||
|
let messageContent =
|
||||||
|
this.formatMessage(this.data.content, this.isATweet) +
|
||||||
|
botMessageContent;
|
||||||
|
|
||||||
|
return messageContent;
|
||||||
|
},
|
||||||
|
contentAttributes() {
|
||||||
|
return this.data.content_attributes || {};
|
||||||
},
|
},
|
||||||
sender() {
|
sender() {
|
||||||
return this.data.sender || {};
|
return this.data.sender || {};
|
||||||
@ -119,7 +134,7 @@ export default {
|
|||||||
return !this.data.message_type ? 'left' : 'right';
|
return !this.data.message_type ? 'left' : 'right';
|
||||||
},
|
},
|
||||||
readableTime() {
|
readableTime() {
|
||||||
return this.messageStamp(this.data.created_at);
|
return this.messageStamp(this.data.created_at, 'LLL d, h:mm a');
|
||||||
},
|
},
|
||||||
isBubble() {
|
isBubble() {
|
||||||
return [0, 1, 3].includes(this.data.message_type);
|
return [0, 1, 3].includes(this.data.message_type);
|
||||||
@ -193,6 +208,22 @@ export default {
|
|||||||
max-width: 32rem;
|
max-width: 32rem;
|
||||||
padding: var(--space-small) var(--space-normal);
|
padding: var(--space-small) var(--space-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-private .file.message-text__wrap {
|
||||||
|
.ion-document-text {
|
||||||
|
color: var(--w-400);
|
||||||
|
}
|
||||||
|
.text-block-title {
|
||||||
|
color: #3c4858;
|
||||||
|
}
|
||||||
|
.download.button {
|
||||||
|
color: var(--w-400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-private.is-text > .message-text__wrap .link {
|
||||||
|
color: var(--w-700);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-pending {
|
&.is-pending {
|
||||||
|
|||||||
@ -8,11 +8,10 @@
|
|||||||
/>
|
/>
|
||||||
<div class="reply-box__top">
|
<div class="reply-box__top">
|
||||||
<canned-response
|
<canned-response
|
||||||
v-if="showCannedResponsesList"
|
v-if="showMentions && hasSlashCommand"
|
||||||
v-on-clickaway="hideCannedResponse"
|
v-on-clickaway="hideMentions"
|
||||||
data-dropdown-menu
|
:search-key="mentionSearchKey"
|
||||||
:on-keyenter="replaceText"
|
@click="replaceText"
|
||||||
:on-click="replaceText"
|
|
||||||
/>
|
/>
|
||||||
<emoji-input
|
<emoji-input
|
||||||
v-if="showEmojiPicker"
|
v-if="showEmojiPicker"
|
||||||
@ -20,7 +19,7 @@
|
|||||||
:on-click="emojiOnClick"
|
:on-click="emojiOnClick"
|
||||||
/>
|
/>
|
||||||
<resizable-text-area
|
<resizable-text-area
|
||||||
v-if="!isFormatMode"
|
v-if="!showRichContentEditor"
|
||||||
ref="messageInput"
|
ref="messageInput"
|
||||||
v-model="message"
|
v-model="message"
|
||||||
class="input"
|
class="input"
|
||||||
@ -35,12 +34,14 @@
|
|||||||
v-else
|
v-else
|
||||||
v-model="message"
|
v-model="message"
|
||||||
class="input"
|
class="input"
|
||||||
|
:is-private="isOnPrivateNote"
|
||||||
:placeholder="messagePlaceHolder"
|
:placeholder="messagePlaceHolder"
|
||||||
:min-height="4"
|
:min-height="4"
|
||||||
@typing-off="onTypingOff"
|
@typing-off="onTypingOff"
|
||||||
@typing-on="onTypingOn"
|
@typing-on="onTypingOn"
|
||||||
@focus="onFocus"
|
@focus="onFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
|
@toggle-user-mention="toggleUserMention"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasAttachments" class="attachment-preview-box">
|
<div v-if="hasAttachments" class="attachment-preview-box">
|
||||||
@ -59,8 +60,11 @@
|
|||||||
:on-send="sendMessage"
|
:on-send="sendMessage"
|
||||||
:is-send-disabled="isReplyButtonDisabled"
|
:is-send-disabled="isReplyButtonDisabled"
|
||||||
:set-format-mode="setFormatMode"
|
:set-format-mode="setFormatMode"
|
||||||
:is-format-mode="isFormatMode"
|
:is-on-private-note="isOnPrivateNote"
|
||||||
|
:is-format-mode="showRichContentEditor"
|
||||||
:enable-rich-editor="isRichEditorEnabled"
|
:enable-rich-editor="isRichEditorEnabled"
|
||||||
|
:enter-to-send-enabled="enterToSendEnabled"
|
||||||
|
@toggleEnterToSend="toggleEnterToSend"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -84,6 +88,7 @@ import {
|
|||||||
} from 'shared/helpers/KeyboardHelpers';
|
} from 'shared/helpers/KeyboardHelpers';
|
||||||
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
||||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||||
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -95,7 +100,7 @@ export default {
|
|||||||
ReplyBottomPanel,
|
ReplyBottomPanel,
|
||||||
WootMessageEditor,
|
WootMessageEditor,
|
||||||
},
|
},
|
||||||
mixins: [clickaway, inboxMixin],
|
mixins: [clickaway, inboxMixin, uiSettingsMixin],
|
||||||
props: {
|
props: {
|
||||||
inReplyTo: {
|
inReplyTo: {
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
@ -107,18 +112,29 @@ export default {
|
|||||||
message: '',
|
message: '',
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
showEmojiPicker: false,
|
showEmojiPicker: false,
|
||||||
showCannedResponsesList: false,
|
showMentions: false,
|
||||||
attachedFiles: [],
|
attachedFiles: [],
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
replyType: REPLY_EDITOR_MODES.REPLY,
|
replyType: REPLY_EDITOR_MODES.REPLY,
|
||||||
isFormatMode: false,
|
mentionSearchKey: '',
|
||||||
|
hasUserMention: false,
|
||||||
|
hasSlashCommand: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
showRichContentEditor() {
|
||||||
|
const {
|
||||||
|
display_rich_content_editor: displayRichContentEditor,
|
||||||
|
} = this.uiSettings;
|
||||||
|
return this.isOnPrivateNote || displayRichContentEditor;
|
||||||
|
},
|
||||||
...mapGetters({ currentChat: 'getSelectedChat' }),
|
...mapGetters({ currentChat: 'getSelectedChat' }),
|
||||||
|
enterToSendEnabled() {
|
||||||
|
return !!this.uiSettings.enter_to_send_enabled;
|
||||||
|
},
|
||||||
isPrivate() {
|
isPrivate() {
|
||||||
if (this.currentChat.can_reply) {
|
if (this.currentChat.can_reply) {
|
||||||
return this.replyType === REPLY_EDITOR_MODES.NOTE;
|
return this.isOnPrivateNote;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -200,15 +216,20 @@ export default {
|
|||||||
},
|
},
|
||||||
isRichEditorEnabled() {
|
isRichEditorEnabled() {
|
||||||
return (
|
return (
|
||||||
this.isAWebWidgetInbox ||
|
this.isAWebWidgetInbox || this.isAnEmailChannel || this.isOnPrivateNote
|
||||||
this.isAnEmailChannel ||
|
|
||||||
this.replyType === REPLY_EDITOR_MODES.NOTE
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
isOnPrivateNote() {
|
||||||
|
return this.replyType === REPLY_EDITOR_MODES.NOTE;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
currentChat(conversation) {
|
currentChat(conversation) {
|
||||||
const { can_reply: canReply } = conversation;
|
const { can_reply: canReply } = conversation;
|
||||||
|
if (this.isOnPrivateNote) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (canReply) {
|
if (canReply) {
|
||||||
this.replyType = REPLY_EDITOR_MODES.REPLY;
|
this.replyType = REPLY_EDITOR_MODES.REPLY;
|
||||||
} else {
|
} else {
|
||||||
@ -216,22 +237,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
message(updatedMessage) {
|
message(updatedMessage) {
|
||||||
if (this.isPrivate) {
|
this.hasSlashCommand = updatedMessage[0] === '/';
|
||||||
return;
|
|
||||||
}
|
|
||||||
const isSlashCommand = updatedMessage[0] === '/';
|
|
||||||
const hasNextWord = updatedMessage.includes(' ');
|
const hasNextWord = updatedMessage.includes(' ');
|
||||||
const isShortCodeActive = isSlashCommand && !hasNextWord;
|
const isShortCodeActive = this.hasSlashCommand && !hasNextWord;
|
||||||
if (isShortCodeActive) {
|
if (isShortCodeActive) {
|
||||||
this.showCannedResponsesList = true;
|
this.mentionSearchKey = updatedMessage.substr(1, updatedMessage.length);
|
||||||
if (updatedMessage.length > 1) {
|
this.showMentions = true;
|
||||||
const searchKey = updatedMessage.substr(1, updatedMessage.length);
|
|
||||||
this.$store.dispatch('getCannedResponse', { searchKey });
|
|
||||||
} else {
|
|
||||||
this.$store.dispatch('getCannedResponse');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.showCannedResponsesList = false;
|
this.mentionSearchKey = '';
|
||||||
|
this.showMentions = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -242,24 +256,35 @@ export default {
|
|||||||
document.removeEventListener('keydown', this.handleKeyEvents);
|
document.removeEventListener('keydown', this.handleKeyEvents);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
toggleUserMention(currentMentionState) {
|
||||||
|
this.hasUserMention = currentMentionState;
|
||||||
|
},
|
||||||
handleKeyEvents(e) {
|
handleKeyEvents(e) {
|
||||||
if (isEscape(e)) {
|
if (isEscape(e)) {
|
||||||
this.hideEmojiPicker();
|
this.hideEmojiPicker();
|
||||||
this.hideCannedResponse();
|
this.hideMentions();
|
||||||
} else if (isEnter(e)) {
|
} else if (isEnter(e)) {
|
||||||
|
const hasSendOnEnterEnabled =
|
||||||
|
(this.showRichContentEditor &&
|
||||||
|
this.enterToSendEnabled &&
|
||||||
|
!this.hasUserMention) ||
|
||||||
|
!this.showRichContentEditor;
|
||||||
const shouldSendMessage =
|
const shouldSendMessage =
|
||||||
!this.isFormatMode && !hasPressedShift(e) && this.isFocused;
|
hasSendOnEnterEnabled && !hasPressedShift(e) && this.isFocused;
|
||||||
if (shouldSendMessage) {
|
if (shouldSendMessage) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.sendMessage();
|
this.sendMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
toggleEnterToSend(enterToSendEnabled) {
|
||||||
|
this.updateUISettings({ enter_to_send_enabled: enterToSendEnabled });
|
||||||
|
},
|
||||||
async sendMessage() {
|
async sendMessage() {
|
||||||
if (this.isReplyButtonDisabled) {
|
if (this.isReplyButtonDisabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.showCannedResponsesList) {
|
if (!this.showMentions) {
|
||||||
const newMessage = this.message;
|
const newMessage = this.message;
|
||||||
const messagePayload = this.getMessagePayload(newMessage);
|
const messagePayload = this.getMessagePayload(newMessage);
|
||||||
this.clearMessage();
|
this.clearMessage();
|
||||||
@ -281,7 +306,10 @@ export default {
|
|||||||
const { can_reply: canReply } = this.currentChat;
|
const { can_reply: canReply } = this.currentChat;
|
||||||
|
|
||||||
if (canReply) this.replyType = mode;
|
if (canReply) this.replyType = mode;
|
||||||
this.$refs.messageInput.focus();
|
if (this.showRichContentEditor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$nextTick(() => this.$refs.messageInput.focus());
|
||||||
},
|
},
|
||||||
emojiOnClick(emoji) {
|
emojiOnClick(emoji) {
|
||||||
this.message = `${this.message}${emoji} `;
|
this.message = `${this.message}${emoji} `;
|
||||||
@ -298,8 +326,8 @@ export default {
|
|||||||
this.toggleEmojiPicker();
|
this.toggleEmojiPicker();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideCannedResponse() {
|
hideMentions() {
|
||||||
this.showCannedResponsesList = false;
|
this.showMentions = false;
|
||||||
},
|
},
|
||||||
onTypingOn() {
|
onTypingOn() {
|
||||||
this.toggleTyping('on');
|
this.toggleTyping('on');
|
||||||
@ -363,7 +391,7 @@ export default {
|
|||||||
return messagePayload;
|
return messagePayload;
|
||||||
},
|
},
|
||||||
setFormatMode(value) {
|
setFormatMode(value) {
|
||||||
this.isFormatMode = value;
|
this.updateUISettings({ display_rich_content_editor: value });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<mention-box :items="items" @mention-select="handleMentionClick" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import MentionBox from '../mentions/MentionBox.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { MentionBox },
|
||||||
|
props: {
|
||||||
|
searchKey: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
agents: 'agents/getVerifiedAgents',
|
||||||
|
}),
|
||||||
|
items() {
|
||||||
|
if (!this.searchKey) {
|
||||||
|
return this.agents.map(agent => ({
|
||||||
|
label: agent.name,
|
||||||
|
key: agent.id,
|
||||||
|
description: agent.email,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.agents
|
||||||
|
.filter(agent =>
|
||||||
|
agent.name
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.includes(this.searchKey.toLocaleLowerCase())
|
||||||
|
)
|
||||||
|
.map(agent => ({
|
||||||
|
label: agent.name,
|
||||||
|
key: agent.id,
|
||||||
|
description: agent.email,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleMentionClick(item = {}) {
|
||||||
|
this.$emit('click', item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
const generateInputSelectContent = (contentType, contentAttributes) => {
|
||||||
|
const { submitted_values: submittedValues = [] } = contentAttributes;
|
||||||
|
const [selectedOption] = submittedValues;
|
||||||
|
|
||||||
|
if (selectedOption && selectedOption.title) {
|
||||||
|
return `<strong>${selectedOption.title}</strong>`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateInputEmailContent = (contentType, contentAttributes) => {
|
||||||
|
const { submitted_email: submittedEmail = '' } = contentAttributes;
|
||||||
|
if (submittedEmail) {
|
||||||
|
return `<strong>${submittedEmail}</strong>`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateFormContent = (
|
||||||
|
contentType,
|
||||||
|
contentAttributes,
|
||||||
|
noResponseText
|
||||||
|
) => {
|
||||||
|
const { items, submitted_values: submittedValues = [] } = contentAttributes;
|
||||||
|
if (submittedValues.length) {
|
||||||
|
const submittedObject = submittedValues.reduce((acc, keyValuePair) => {
|
||||||
|
acc[keyValuePair.name] = keyValuePair.value;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
let formMessageContent = '';
|
||||||
|
items.forEach(item => {
|
||||||
|
formMessageContent += `<div>${item.label}</div>`;
|
||||||
|
const response = submittedObject[item.name] || noResponseText;
|
||||||
|
formMessageContent += `<strong>${response}</strong><br/><br/>`;
|
||||||
|
});
|
||||||
|
return formMessageContent;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateBotMessageContent = (
|
||||||
|
contentType,
|
||||||
|
contentAttributes,
|
||||||
|
noResponseText = 'No response'
|
||||||
|
) => {
|
||||||
|
const contentTypeMethods = {
|
||||||
|
input_select: generateInputSelectContent,
|
||||||
|
input_email: generateInputEmailContent,
|
||||||
|
form: generateFormContent,
|
||||||
|
};
|
||||||
|
|
||||||
|
const contentTypeMethod = contentTypeMethods[contentType];
|
||||||
|
if (contentTypeMethod && typeof contentTypeMethod === 'function') {
|
||||||
|
return contentTypeMethod(contentType, contentAttributes, noResponseText);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { generateBotMessageContent } from '../botMessageContentHelper';
|
||||||
|
|
||||||
|
describe('#generateBotMessageContent', () => {
|
||||||
|
it('return correct input_select content', () => {
|
||||||
|
expect(
|
||||||
|
generateBotMessageContent('input_select', {
|
||||||
|
submitted_values: [{ value: 'salad', title: 'Salad' }],
|
||||||
|
})
|
||||||
|
).toEqual('<strong>Salad</strong>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('return correct input_email content', () => {
|
||||||
|
expect(
|
||||||
|
generateBotMessageContent('input_email', {
|
||||||
|
submitted_email: 'hello@chatwoot.com',
|
||||||
|
})
|
||||||
|
).toEqual('<strong>hello@chatwoot.com</strong>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('return correct form content', () => {
|
||||||
|
expect(
|
||||||
|
generateBotMessageContent('form', {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'large_text',
|
||||||
|
label: 'This a large text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'email',
|
||||||
|
label: 'Email',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
submitted_values: [{ name: 'large_text', value: 'Large Text Content' }],
|
||||||
|
})
|
||||||
|
).toEqual(
|
||||||
|
'<div>This a large text</div><strong>Large Text Content</strong><br/><br/><div>Email</div><strong>No response</strong><br/><br/>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<ul
|
||||||
|
v-if="items.length"
|
||||||
|
class="vertical dropdown menu mention--box"
|
||||||
|
:style="{ top: getTopPadding() + 'rem' }"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="(item, index) in items"
|
||||||
|
:id="`mention-item-${index}`"
|
||||||
|
:key="item.key"
|
||||||
|
:class="{ active: index === selectedIndex }"
|
||||||
|
@click="onListItemSelection(index)"
|
||||||
|
@mouseover="onHover(index)"
|
||||||
|
>
|
||||||
|
<a class="text-truncate">
|
||||||
|
<strong>{{ item.label }}</strong> - {{ item.description }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedIndex: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
items(newItems) {
|
||||||
|
if (newItems.length < this.selectedIndex + 1) {
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.addEventListener('keydown', this.keyListener);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
document.removeEventListener('keydown', this.keyListener);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTopPadding() {
|
||||||
|
if (this.items.length <= 4) {
|
||||||
|
return -(this.items.length * 2.8 + 1.7);
|
||||||
|
}
|
||||||
|
return -14;
|
||||||
|
},
|
||||||
|
isUp(e) {
|
||||||
|
return e.keyCode === 38 || (e.ctrlKey && e.keyCode === 80); // UP, Ctrl-P
|
||||||
|
},
|
||||||
|
isDown(e) {
|
||||||
|
return e.keyCode === 40 || (e.ctrlKey && e.keyCode === 78); // DOWN, Ctrl-N
|
||||||
|
},
|
||||||
|
isEnter(e) {
|
||||||
|
return e.keyCode === 13;
|
||||||
|
},
|
||||||
|
keyListener(e) {
|
||||||
|
if (this.isUp(e)) {
|
||||||
|
if (!this.selectedIndex) {
|
||||||
|
this.selectedIndex = this.items.length - 1;
|
||||||
|
} else {
|
||||||
|
this.selectedIndex -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.isDown(e)) {
|
||||||
|
if (this.selectedIndex === this.items.length - 1) {
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
} else {
|
||||||
|
this.selectedIndex += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.isEnter(e)) {
|
||||||
|
this.onMentionSelect();
|
||||||
|
}
|
||||||
|
this.$el.scrollTop = 28 * this.selectedIndex;
|
||||||
|
},
|
||||||
|
onHover(index) {
|
||||||
|
this.selectedIndex = index;
|
||||||
|
},
|
||||||
|
onListItemSelection(index) {
|
||||||
|
this.selectedIndex = index;
|
||||||
|
this.onMentionSelect();
|
||||||
|
},
|
||||||
|
onMentionSelect() {
|
||||||
|
this.$emit('mention-select', this.items[this.selectedIndex]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mention--box {
|
||||||
|
background: var(--white);
|
||||||
|
border-bottom: var(--space-small) solid var(--white);
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
left: 0;
|
||||||
|
max-height: 14rem;
|
||||||
|
overflow: auto;
|
||||||
|
padding-top: var(--space-small);
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
.active a {
|
||||||
|
background: var(--w-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -8,11 +8,15 @@ export const getSidebarItems = accountId => ({
|
|||||||
'inbox_conversation',
|
'inbox_conversation',
|
||||||
'conversation_through_inbox',
|
'conversation_through_inbox',
|
||||||
'contacts_dashboard',
|
'contacts_dashboard',
|
||||||
|
'notifications_dashboard',
|
||||||
'settings_account_reports',
|
'settings_account_reports',
|
||||||
'profile_settings',
|
'profile_settings',
|
||||||
'profile_settings_index',
|
'profile_settings_index',
|
||||||
'label_conversations',
|
'label_conversations',
|
||||||
'conversations_through_label',
|
'conversations_through_label',
|
||||||
|
'team_conversations',
|
||||||
|
'conversations_through_team',
|
||||||
|
'notifications_index',
|
||||||
],
|
],
|
||||||
menuItems: {
|
menuItems: {
|
||||||
assignedToMe: {
|
assignedToMe: {
|
||||||
@ -31,6 +35,13 @@ export const getSidebarItems = accountId => ({
|
|||||||
toState: frontendURL(`accounts/${accountId}/contacts`),
|
toState: frontendURL(`accounts/${accountId}/contacts`),
|
||||||
toStateName: 'contacts_dashboard',
|
toStateName: 'contacts_dashboard',
|
||||||
},
|
},
|
||||||
|
notifications: {
|
||||||
|
icon: 'ion-ios-bell',
|
||||||
|
label: 'NOTIFICATIONS',
|
||||||
|
hasSubMenu: false,
|
||||||
|
toState: frontendURL(`accounts/${accountId}/notifications`),
|
||||||
|
toStateName: 'notifications_dashboard',
|
||||||
|
},
|
||||||
report: {
|
report: {
|
||||||
icon: 'ion-arrow-graph-up-right',
|
icon: 'ion-arrow-graph-up-right',
|
||||||
label: 'REPORTS',
|
label: 'REPORTS',
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "تم تلقيه عبر البريد الإلكتروني",
|
"RECEIVED_VIA_EMAIL": "تم تلقيه عبر البريد الإلكتروني",
|
||||||
"VIEW_TWEET_IN_TWITTER": "عرض التغريدة في تويتر",
|
"VIEW_TWEET_IN_TWITTER": "عرض التغريدة في تويتر",
|
||||||
"REPLY_TO_TWEET": "الرد على هذه التغريدة"
|
"REPLY_TO_TWEET": "الرد على هذه التغريدة",
|
||||||
|
"NO_MESSAGES": "No Messages"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "إزالة التحديد",
|
"REMOVE_SELECTION": "إزالة التحديد",
|
||||||
"DOWNLOAD": "تنزيل",
|
"DOWNLOAD": "تنزيل",
|
||||||
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
||||||
|
"NO_RESPONSE": "No response",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "إغلاق المحادثة",
|
"RESOLVE_ACTION": "إغلاق المحادثة",
|
||||||
"REOPEN_ACTION": "إعادة فتح",
|
"REOPEN_ACTION": "إعادة فتح",
|
||||||
@ -44,7 +45,8 @@
|
|||||||
"TWEET": "تغريد",
|
"TWEET": "تغريد",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Show emoji selector",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Attach files",
|
||||||
|
"ENTER_TO_SEND": "Enter to send"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "ملاحظة خاصة: مرئية فقط لأعضاء فريق العمل والموظفين",
|
"VISIBLE_TO_AGENTS": "ملاحظة خاصة: مرئية فقط لأعضاء فريق العمل والموظفين",
|
||||||
"CHANGE_STATUS": "تم تغيير حالة المحادثة",
|
"CHANGE_STATUS": "تم تغيير حالة المحادثة",
|
||||||
|
|||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Press enter to remove",
|
"ENTER_TO_REMOVE": "Press enter to remove",
|
||||||
"SELECT_ONE": "Select one"
|
"SELECT_ONE": "Select one"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "الإشعارات",
|
||||||
|
"MARK_ALL_DONE": "Mark All Done",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Loading notifications...",
|
||||||
|
"404": "No Notifications",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"الاسم",
|
||||||
|
"رقم الهاتف",
|
||||||
|
"المحادثات",
|
||||||
|
"Last Contacted"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "New conversation",
|
||||||
|
"conversation_assignment": "Conversation Assigned",
|
||||||
|
"assigned_conversation_new_message": "New Message",
|
||||||
|
"conversation_mention": "Mention"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "قم بتحديث تفضيلات إشعار البريد الإلكتروني الخاص بك من هنا",
|
"NOTE": "قم بتحديث تفضيلات إشعار البريد الإلكتروني الخاص بك من هنا",
|
||||||
"CONVERSATION_ASSIGNMENT": "إرسال إشعارات البريد الإلكتروني عند إسناد محادثة لي",
|
"CONVERSATION_ASSIGNMENT": "إرسال إشعارات البريد الإلكتروني عند إسناد محادثة لي",
|
||||||
"CONVERSATION_CREATION": "إرسال إشعارات للبريد الإلكتروني عند ورود محادثة جديدة",
|
"CONVERSATION_CREATION": "إرسال إشعارات للبريد الإلكتروني عند ورود محادثة جديدة",
|
||||||
|
"CONVERSATION_MENTION": "Send email notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "قم بتحديث تفضيلات إشعارات المتصفح من هنا",
|
"NOTE": "قم بتحديث تفضيلات إشعارات المتصفح من هنا",
|
||||||
"CONVERSATION_ASSIGNMENT": "إرسال إشعارات على المتصفح عند إسناد محادثة لي",
|
"CONVERSATION_ASSIGNMENT": "إرسال إشعارات على المتصفح عند إسناد محادثة لي",
|
||||||
"CONVERSATION_CREATION": "إرسال إشعارات المتصفح عند ورود محادثة جديدة",
|
"CONVERSATION_CREATION": "إرسال إشعارات المتصفح عند ورود محادثة جديدة",
|
||||||
|
"CONVERSATION_MENTION": "Send push notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
||||||
"HAS_ENABLED_PUSH": "لقد قمت بتمكين الإشعارات لهذا المتصفح.",
|
"HAS_ENABLED_PUSH": "لقد قمت بتمكين الإشعارات لهذا المتصفح.",
|
||||||
"REQUEST_PUSH": "تفعيل إشعارات المتصفح"
|
"REQUEST_PUSH": "تفعيل إشعارات المتصفح"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "الرئيسية",
|
"HOME": "الرئيسية",
|
||||||
"AGENTS": "موظف الدعم",
|
"AGENTS": "موظف الدعم",
|
||||||
"INBOXES": "قنوات التواصل",
|
"INBOXES": "قنوات التواصل",
|
||||||
|
"NOTIFICATIONS": "الإشعارات",
|
||||||
"CANNED_RESPONSES": "الردود السريعة",
|
"CANNED_RESPONSES": "الردود السريعة",
|
||||||
"INTEGRATIONS": "خيارات الربط",
|
"INTEGRATIONS": "خيارات الربط",
|
||||||
"ACCOUNT_SETTINGS": "إعدادات الحساب",
|
"ACCOUNT_SETTINGS": "إعدادات الحساب",
|
||||||
"LABELS": "الوسوم"
|
"LABELS": "الوسوم",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "حساب جديد",
|
"NEW_ACCOUNT": "حساب جديد",
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Rebut per correu electrònic",
|
"RECEIVED_VIA_EMAIL": "Rebut per correu electrònic",
|
||||||
"VIEW_TWEET_IN_TWITTER": "Veure el tuit a Twitter",
|
"VIEW_TWEET_IN_TWITTER": "Veure el tuit a Twitter",
|
||||||
"REPLY_TO_TWEET": "Respon a aquest tuit"
|
"REPLY_TO_TWEET": "Respon a aquest tuit",
|
||||||
|
"NO_MESSAGES": "Cap Missatge"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "Elimina la selecció",
|
"REMOVE_SELECTION": "Elimina la selecció",
|
||||||
"DOWNLOAD": "Descarrega",
|
"DOWNLOAD": "Descarrega",
|
||||||
"UPLOADING_ATTACHMENTS": "Pujant fitxers adjunts...",
|
"UPLOADING_ATTACHMENTS": "Pujant fitxers adjunts...",
|
||||||
|
"NO_RESPONSE": "Sense resposta",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Resoldre",
|
"RESOLVE_ACTION": "Resoldre",
|
||||||
"REOPEN_ACTION": "Tornar a obrir",
|
"REOPEN_ACTION": "Tornar a obrir",
|
||||||
@ -42,9 +43,10 @@
|
|||||||
"SEND": "Envia",
|
"SEND": "Envia",
|
||||||
"CREATE": "Afegeix una nota",
|
"CREATE": "Afegeix una nota",
|
||||||
"TWEET": "Tuit",
|
"TWEET": "Tuit",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Mostra l'editor de text enriquit",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Mostra la selecció d'emoticones",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Ajuntar fitxers",
|
||||||
|
"ENTER_TO_SEND": "Intro per enviar"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "Nota privada: Només és visible per tu i el vostre equip",
|
"VISIBLE_TO_AGENTS": "Nota privada: Només és visible per tu i el vostre equip",
|
||||||
"CHANGE_STATUS": "Estat de la conversa canviat",
|
"CHANGE_STATUS": "Estat de la conversa canviat",
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Ara podeu rebre correus electrònics al vostre domini personalitzat."
|
"CUSTOM_EMAIL_DOMAIN_ENABLED": "Ara podeu rebre correus electrònics al vostre domini personalitzat."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"UPDATE_CHATWOOT": "An update %{latestChatwootVersion} for Chatwoot is available. Please update your instance."
|
"UPDATE_CHATWOOT": "L'actualització %{latestChatwootVersion} per Chatwoot està disponible. Si us plau, actualitza l'instancia."
|
||||||
},
|
},
|
||||||
"FORMS": {
|
"FORMS": {
|
||||||
"MULTISELECT": {
|
"MULTISELECT": {
|
||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Presiona retorn (tecla enter) per eliminar",
|
"ENTER_TO_REMOVE": "Presiona retorn (tecla enter) per eliminar",
|
||||||
"SELECT_ONE": "Selecciona un"
|
"SELECT_ONE": "Selecciona un"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "Notificacions",
|
||||||
|
"MARK_ALL_DONE": "Marca Tot Fet",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Carregant notificacions...",
|
||||||
|
"404": "Cap Notificació",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"Nom",
|
||||||
|
"Número de telèfon",
|
||||||
|
"Converses",
|
||||||
|
"Darrer contacte"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "Nova conversació",
|
||||||
|
"conversation_assignment": "Conversació Assignada",
|
||||||
|
"assigned_conversation_new_message": "Missatge Nou",
|
||||||
|
"conversation_mention": "Menció"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -242,7 +242,7 @@
|
|||||||
"INBOX_UPDATE_TITLE": "Configuració de la safata d'entrada",
|
"INBOX_UPDATE_TITLE": "Configuració de la safata d'entrada",
|
||||||
"INBOX_UPDATE_SUB_TEXT": "Actualitza la configuració de la safata d'entrada",
|
"INBOX_UPDATE_SUB_TEXT": "Actualitza la configuració de la safata d'entrada",
|
||||||
"AUTO_ASSIGNMENT_SUB_TEXT": "Activa o desactiva l'assignació automàtica d'agents disponibles a les noves converses",
|
"AUTO_ASSIGNMENT_SUB_TEXT": "Activa o desactiva l'assignació automàtica d'agents disponibles a les noves converses",
|
||||||
"HMAC_VERIFICATION": "User Identity Validation",
|
"HMAC_VERIFICATION": "Validació de la Identitat del Usuari",
|
||||||
"HMAC_DESCRIPTION": "Inorder validate the users identity, the SDK allows you to pass an `identity_hash` for each user. You can generate HMAC using 'sha256' with the key shown here."
|
"HMAC_DESCRIPTION": "Inorder validate the users identity, the SDK allows you to pass an `identity_hash` for each user. You can generate HMAC using 'sha256' with the key shown here."
|
||||||
},
|
},
|
||||||
"FACEBOOK_REAUTHORIZE": {
|
"FACEBOOK_REAUTHORIZE": {
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "Actualitza aqui les preferències de les notificacions per correu electrònic",
|
"NOTE": "Actualitza aqui les preferències de les notificacions per correu electrònic",
|
||||||
"CONVERSATION_ASSIGNMENT": "Envieu notificacions per correu electrònic quan se m’assigni una conversa",
|
"CONVERSATION_ASSIGNMENT": "Envieu notificacions per correu electrònic quan se m’assigni una conversa",
|
||||||
"CONVERSATION_CREATION": "Envieu notificacions per correu electrònic quan es crea una nova conversa",
|
"CONVERSATION_CREATION": "Envieu notificacions per correu electrònic quan es crea una nova conversa",
|
||||||
|
"CONVERSATION_MENTION": "Enviar notificacions per mail quan siguis esmentat en una conversació",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Envia notificacions per correu electrònic quan es creï un missatge nou en una conversa assignada"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Envia notificacions per correu electrònic quan es creï un missatge nou en una conversa assignada"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "Actualitzeu les preferències de notificació aquí",
|
"NOTE": "Actualitzeu les preferències de notificació aquí",
|
||||||
"CONVERSATION_ASSIGNMENT": "Envia notificacions automàtiques quan se m'assigna una conversa",
|
"CONVERSATION_ASSIGNMENT": "Envia notificacions automàtiques quan se m'assigna una conversa",
|
||||||
"CONVERSATION_CREATION": "Envia notificacions automàtiques quan es creï una conversa nova",
|
"CONVERSATION_CREATION": "Envia notificacions automàtiques quan es creï una conversa nova",
|
||||||
|
"CONVERSATION_MENTION": "Enviar notificacions push quan siguis esmentat en una conversació",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Envia notificacions automàtiques quan es creï un missatge nou en una conversa assignada",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Envia notificacions automàtiques quan es creï un missatge nou en una conversa assignada",
|
||||||
"HAS_ENABLED_PUSH": "Heu activat les notificacions per a aquest navegador.",
|
"HAS_ENABLED_PUSH": "Heu activat les notificacions per a aquest navegador.",
|
||||||
"REQUEST_PUSH": "Activa les notificacions"
|
"REQUEST_PUSH": "Activa les notificacions"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "Inici",
|
"HOME": "Inici",
|
||||||
"AGENTS": "Agents",
|
"AGENTS": "Agents",
|
||||||
"INBOXES": "Safates d'entrada",
|
"INBOXES": "Safates d'entrada",
|
||||||
|
"NOTIFICATIONS": "Notificacions",
|
||||||
"CANNED_RESPONSES": "Respostes predeterminades",
|
"CANNED_RESPONSES": "Respostes predeterminades",
|
||||||
"INTEGRATIONS": "Integracions",
|
"INTEGRATIONS": "Integracions",
|
||||||
"ACCOUNT_SETTINGS": "Configuració del compte",
|
"ACCOUNT_SETTINGS": "Configuració del compte",
|
||||||
"LABELS": "Etiquetes"
|
"LABELS": "Etiquetes",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "Compte nou",
|
"NEW_ACCOUNT": "Compte nou",
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Obdrženo e-mailem",
|
"RECEIVED_VIA_EMAIL": "Obdrženo e-mailem",
|
||||||
"VIEW_TWEET_IN_TWITTER": "Zobrazit tweet na Twitteru",
|
"VIEW_TWEET_IN_TWITTER": "Zobrazit tweet na Twitteru",
|
||||||
"REPLY_TO_TWEET": "Odpovědět na tento tweet"
|
"REPLY_TO_TWEET": "Odpovědět na tento tweet",
|
||||||
|
"NO_MESSAGES": "No Messages"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "Odstranit výběr",
|
"REMOVE_SELECTION": "Odstranit výběr",
|
||||||
"DOWNLOAD": "Stáhnout",
|
"DOWNLOAD": "Stáhnout",
|
||||||
"UPLOADING_ATTACHMENTS": "Nahrávání příloh...",
|
"UPLOADING_ATTACHMENTS": "Nahrávání příloh...",
|
||||||
|
"NO_RESPONSE": "No response",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Vyřešit",
|
"RESOLVE_ACTION": "Vyřešit",
|
||||||
"REOPEN_ACTION": "Znovu otevřít",
|
"REOPEN_ACTION": "Znovu otevřít",
|
||||||
@ -44,7 +45,8 @@
|
|||||||
"TWEET": "Tweet",
|
"TWEET": "Tweet",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Show emoji selector",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Attach files",
|
||||||
|
"ENTER_TO_SEND": "Enter to send"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "Soukromá poznámka: Viditelné pouze pro vás a váš tým",
|
"VISIBLE_TO_AGENTS": "Soukromá poznámka: Viditelné pouze pro vás a váš tým",
|
||||||
"CHANGE_STATUS": "Stav konverzace byl změněn",
|
"CHANGE_STATUS": "Stav konverzace byl změněn",
|
||||||
|
|||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Stiskněte Enter pro odebrání",
|
"ENTER_TO_REMOVE": "Stiskněte Enter pro odebrání",
|
||||||
"SELECT_ONE": "Vyberte jeden"
|
"SELECT_ONE": "Vyberte jeden"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "Notifications",
|
||||||
|
"MARK_ALL_DONE": "Mark All Done",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Loading notifications...",
|
||||||
|
"404": "No Notifications",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"Název",
|
||||||
|
"Telefonní číslo",
|
||||||
|
"Konverzace",
|
||||||
|
"Naposledy kontaktováno"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "New conversation",
|
||||||
|
"conversation_assignment": "Conversation Assigned",
|
||||||
|
"assigned_conversation_new_message": "New Message",
|
||||||
|
"conversation_mention": "Mention"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "Zde aktualizujte nastavení e-mailových oznámení",
|
"NOTE": "Zde aktualizujte nastavení e-mailových oznámení",
|
||||||
"CONVERSATION_ASSIGNMENT": "Odeslat e-mailová oznámení, když je mi přiřazena konverzace",
|
"CONVERSATION_ASSIGNMENT": "Odeslat e-mailová oznámení, když je mi přiřazena konverzace",
|
||||||
"CONVERSATION_CREATION": "Odeslat oznámení e-mailem při vytváření nové konverzace",
|
"CONVERSATION_CREATION": "Odeslat oznámení e-mailem při vytváření nové konverzace",
|
||||||
|
"CONVERSATION_MENTION": "Send email notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "Update your push notification preferences here",
|
"NOTE": "Update your push notification preferences here",
|
||||||
"CONVERSATION_ASSIGNMENT": "Send push notifications when a conversation is assigned to me",
|
"CONVERSATION_ASSIGNMENT": "Send push notifications when a conversation is assigned to me",
|
||||||
"CONVERSATION_CREATION": "Send push notifications when a new conversation is created",
|
"CONVERSATION_CREATION": "Send push notifications when a new conversation is created",
|
||||||
|
"CONVERSATION_MENTION": "Send push notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
||||||
"HAS_ENABLED_PUSH": "You have enabled push for this browser.",
|
"HAS_ENABLED_PUSH": "You have enabled push for this browser.",
|
||||||
"REQUEST_PUSH": "Enable push notifications"
|
"REQUEST_PUSH": "Enable push notifications"
|
||||||
@ -98,7 +100,7 @@
|
|||||||
},
|
},
|
||||||
"FILE_BUBBLE": {
|
"FILE_BUBBLE": {
|
||||||
"DOWNLOAD": "Stáhnout",
|
"DOWNLOAD": "Stáhnout",
|
||||||
"UPLOADING": "Uploading..."
|
"UPLOADING": "Nahrávání..."
|
||||||
},
|
},
|
||||||
"FORM_BUBBLE": {
|
"FORM_BUBBLE": {
|
||||||
"SUBMIT": "Odeslat"
|
"SUBMIT": "Odeslat"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "Home",
|
"HOME": "Home",
|
||||||
"AGENTS": "Agenti",
|
"AGENTS": "Agenti",
|
||||||
"INBOXES": "Krabice",
|
"INBOXES": "Krabice",
|
||||||
|
"NOTIFICATIONS": "Notifications",
|
||||||
"CANNED_RESPONSES": "Konzervované odpovědi",
|
"CANNED_RESPONSES": "Konzervované odpovědi",
|
||||||
"INTEGRATIONS": "Integrace",
|
"INTEGRATIONS": "Integrace",
|
||||||
"ACCOUNT_SETTINGS": "Account Settings",
|
"ACCOUNT_SETTINGS": "Account Settings",
|
||||||
"LABELS": "Labels"
|
"LABELS": "Labels",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "New Account",
|
"NEW_ACCOUNT": "New Account",
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Modtaget via e-mail",
|
"RECEIVED_VIA_EMAIL": "Modtaget via e-mail",
|
||||||
"VIEW_TWEET_IN_TWITTER": "Se tweet på Twitter",
|
"VIEW_TWEET_IN_TWITTER": "Se tweet på Twitter",
|
||||||
"REPLY_TO_TWEET": "Svar på dette tweet"
|
"REPLY_TO_TWEET": "Svar på dette tweet",
|
||||||
|
"NO_MESSAGES": "No Messages"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "Fjern Markering",
|
"REMOVE_SELECTION": "Fjern Markering",
|
||||||
"DOWNLOAD": "Download",
|
"DOWNLOAD": "Download",
|
||||||
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
||||||
|
"NO_RESPONSE": "No response",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Løs",
|
"RESOLVE_ACTION": "Løs",
|
||||||
"REOPEN_ACTION": "Genåben",
|
"REOPEN_ACTION": "Genåben",
|
||||||
@ -44,7 +45,8 @@
|
|||||||
"TWEET": "Tweet",
|
"TWEET": "Tweet",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Show emoji selector",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Attach files",
|
||||||
|
"ENTER_TO_SEND": "Enter to send"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "Privat Note: Kun synlig for dig og dit team",
|
"VISIBLE_TO_AGENTS": "Privat Note: Kun synlig for dig og dit team",
|
||||||
"CHANGE_STATUS": "Samtalestatus ændret",
|
"CHANGE_STATUS": "Samtalestatus ændret",
|
||||||
|
|||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Press enter to remove",
|
"ENTER_TO_REMOVE": "Press enter to remove",
|
||||||
"SELECT_ONE": "Select one"
|
"SELECT_ONE": "Select one"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "Notifications",
|
||||||
|
"MARK_ALL_DONE": "Mark All Done",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Loading notifications...",
|
||||||
|
"404": "No Notifications",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"Navn",
|
||||||
|
"Telefonnummer",
|
||||||
|
"Samtaler",
|
||||||
|
"Sidst Kontaktet"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "New conversation",
|
||||||
|
"conversation_assignment": "Conversation Assigned",
|
||||||
|
"assigned_conversation_new_message": "New Message",
|
||||||
|
"conversation_mention": "Mention"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "Opdater dine e-mail notifikationspræferencer her",
|
"NOTE": "Opdater dine e-mail notifikationspræferencer her",
|
||||||
"CONVERSATION_ASSIGNMENT": "Send e-mail notifikationer når en samtale er tildelt mig",
|
"CONVERSATION_ASSIGNMENT": "Send e-mail notifikationer når en samtale er tildelt mig",
|
||||||
"CONVERSATION_CREATION": "Send e-mail notifikationer når en ny samtale er oprettet",
|
"CONVERSATION_CREATION": "Send e-mail notifikationer når en ny samtale er oprettet",
|
||||||
|
"CONVERSATION_MENTION": "Send email notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send e-mail notifikationer når en ny besked er oprettet i en tildelt samtale"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send e-mail notifikationer når en ny besked er oprettet i en tildelt samtale"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "Opdater dine push-notifikationspræferencer her",
|
"NOTE": "Opdater dine push-notifikationspræferencer her",
|
||||||
"CONVERSATION_ASSIGNMENT": "Send push-notifikationer, når en samtale er tildelt mig",
|
"CONVERSATION_ASSIGNMENT": "Send push-notifikationer, når en samtale er tildelt mig",
|
||||||
"CONVERSATION_CREATION": "Send push-notifikationer, når en ny samtale er oprettet",
|
"CONVERSATION_CREATION": "Send push-notifikationer, når en ny samtale er oprettet",
|
||||||
|
"CONVERSATION_MENTION": "Send push notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push-notifikationer, når en ny besked oprettes i en tildelt samtale",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push-notifikationer, når en ny besked oprettes i en tildelt samtale",
|
||||||
"HAS_ENABLED_PUSH": "Du har aktiveret push for denne browser.",
|
"HAS_ENABLED_PUSH": "Du har aktiveret push for denne browser.",
|
||||||
"REQUEST_PUSH": "Aktivér push-notifikationer"
|
"REQUEST_PUSH": "Aktivér push-notifikationer"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "Hjem",
|
"HOME": "Hjem",
|
||||||
"AGENTS": "Agenter",
|
"AGENTS": "Agenter",
|
||||||
"INBOXES": "Indbakker",
|
"INBOXES": "Indbakker",
|
||||||
|
"NOTIFICATIONS": "Notifications",
|
||||||
"CANNED_RESPONSES": "Standardsvar Svar",
|
"CANNED_RESPONSES": "Standardsvar Svar",
|
||||||
"INTEGRATIONS": "Integrationer",
|
"INTEGRATIONS": "Integrationer",
|
||||||
"ACCOUNT_SETTINGS": "Kontoindstillinger",
|
"ACCOUNT_SETTINGS": "Kontoindstillinger",
|
||||||
"LABELS": "Etiketter"
|
"LABELS": "Etiketter",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "Ny Konto",
|
"NEW_ACCOUNT": "Ny Konto",
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Per E-Mail empfangen",
|
"RECEIVED_VIA_EMAIL": "Per E-Mail empfangen",
|
||||||
"VIEW_TWEET_IN_TWITTER": "Tweet auf Twitter anzeigen",
|
"VIEW_TWEET_IN_TWITTER": "Tweet auf Twitter anzeigen",
|
||||||
"REPLY_TO_TWEET": "Auf diesen Tweet antworten"
|
"REPLY_TO_TWEET": "Auf diesen Tweet antworten",
|
||||||
|
"NO_MESSAGES": "No Messages"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "Auswahl entfernen",
|
"REMOVE_SELECTION": "Auswahl entfernen",
|
||||||
"DOWNLOAD": "Herunterladen",
|
"DOWNLOAD": "Herunterladen",
|
||||||
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
||||||
|
"NO_RESPONSE": "No response",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Fall schließen",
|
"RESOLVE_ACTION": "Fall schließen",
|
||||||
"REOPEN_ACTION": "Wieder öffnen",
|
"REOPEN_ACTION": "Wieder öffnen",
|
||||||
@ -44,7 +45,8 @@
|
|||||||
"TWEET": "Tweet",
|
"TWEET": "Tweet",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Show emoji selector",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Attach files",
|
||||||
|
"ENTER_TO_SEND": "Enter to send"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "Privater Hinweis: Nur für Sie und Ihr Team sichtbar",
|
"VISIBLE_TO_AGENTS": "Privater Hinweis: Nur für Sie und Ihr Team sichtbar",
|
||||||
"CHANGE_STATUS": "Gesprächsstatus geändert",
|
"CHANGE_STATUS": "Gesprächsstatus geändert",
|
||||||
|
|||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Drücke Enter zum Entfernen",
|
"ENTER_TO_REMOVE": "Drücke Enter zum Entfernen",
|
||||||
"SELECT_ONE": "Eine Option wählen"
|
"SELECT_ONE": "Eine Option wählen"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "Notifications",
|
||||||
|
"MARK_ALL_DONE": "Mark All Done",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Loading notifications...",
|
||||||
|
"404": "No Notifications",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"Name",
|
||||||
|
"Telefonnummer",
|
||||||
|
"Gespräche",
|
||||||
|
"Letzter Kontakt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "New conversation",
|
||||||
|
"conversation_assignment": "Conversation Assigned",
|
||||||
|
"assigned_conversation_new_message": "New Message",
|
||||||
|
"conversation_mention": "Mention"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "Aktualisieren Sie hier Ihre E-Mail-Benachrichtigungseinstellungen",
|
"NOTE": "Aktualisieren Sie hier Ihre E-Mail-Benachrichtigungseinstellungen",
|
||||||
"CONVERSATION_ASSIGNMENT": "Senden Sie E-Mail-Benachrichtigungen, wenn mir ein Gespräch zugewiesen wurde",
|
"CONVERSATION_ASSIGNMENT": "Senden Sie E-Mail-Benachrichtigungen, wenn mir ein Gespräch zugewiesen wurde",
|
||||||
"CONVERSATION_CREATION": "Senden Sie E-Mail-Benachrichtigungen, wenn eine neue Konversation erstellt wird",
|
"CONVERSATION_CREATION": "Senden Sie E-Mail-Benachrichtigungen, wenn eine neue Konversation erstellt wird",
|
||||||
|
"CONVERSATION_MENTION": "Send email notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "Aktualisieren Sie hier Ihre Push-Benachrichtigungseinstellungen",
|
"NOTE": "Aktualisieren Sie hier Ihre Push-Benachrichtigungseinstellungen",
|
||||||
"CONVERSATION_ASSIGNMENT": "Push-Benachrichtigungen senden, wenn mir ein Gespräch zugewiesen wird",
|
"CONVERSATION_ASSIGNMENT": "Push-Benachrichtigungen senden, wenn mir ein Gespräch zugewiesen wird",
|
||||||
"CONVERSATION_CREATION": "Push-Benachrichtigungen senden, wenn eine neue Konversation startet",
|
"CONVERSATION_CREATION": "Push-Benachrichtigungen senden, wenn eine neue Konversation startet",
|
||||||
|
"CONVERSATION_MENTION": "Send push notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
||||||
"HAS_ENABLED_PUSH": "Sie haben die Push-Benachrichtigung für diesen Browser aktiviert.",
|
"HAS_ENABLED_PUSH": "Sie haben die Push-Benachrichtigung für diesen Browser aktiviert.",
|
||||||
"REQUEST_PUSH": "Push-Benachrichtigungen aktivieren"
|
"REQUEST_PUSH": "Push-Benachrichtigungen aktivieren"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "Hauptseite",
|
"HOME": "Hauptseite",
|
||||||
"AGENTS": "Agenten",
|
"AGENTS": "Agenten",
|
||||||
"INBOXES": "Posteingänge",
|
"INBOXES": "Posteingänge",
|
||||||
|
"NOTIFICATIONS": "Notifications",
|
||||||
"CANNED_RESPONSES": "Vorgefertigte Antworten",
|
"CANNED_RESPONSES": "Vorgefertigte Antworten",
|
||||||
"INTEGRATIONS": "Integrationen",
|
"INTEGRATIONS": "Integrationen",
|
||||||
"ACCOUNT_SETTINGS": "Kontoeinstellungen",
|
"ACCOUNT_SETTINGS": "Kontoeinstellungen",
|
||||||
"LABELS": "Labels"
|
"LABELS": "Labels",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "New Account",
|
"NEW_ACCOUNT": "New Account",
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Παραλήφθηκε από email",
|
"RECEIVED_VIA_EMAIL": "Παραλήφθηκε από email",
|
||||||
"VIEW_TWEET_IN_TWITTER": "Προβολή του tweet στο Twitter",
|
"VIEW_TWEET_IN_TWITTER": "Προβολή του tweet στο Twitter",
|
||||||
"REPLY_TO_TWEET": "Απάντηση στο tweet"
|
"REPLY_TO_TWEET": "Απάντηση στο tweet",
|
||||||
|
"NO_MESSAGES": "Κανένα Μήνυμα"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "Διαγραφή Επιλογής",
|
"REMOVE_SELECTION": "Διαγραφή Επιλογής",
|
||||||
"DOWNLOAD": "Κατέβασμα",
|
"DOWNLOAD": "Κατέβασμα",
|
||||||
"UPLOADING_ATTACHMENTS": "Ανέβασμα επισυναπτόμενων...",
|
"UPLOADING_ATTACHMENTS": "Ανέβασμα επισυναπτόμενων...",
|
||||||
|
"NO_RESPONSE": "Καμία ανταπόκριση",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Επίλυση",
|
"RESOLVE_ACTION": "Επίλυση",
|
||||||
"REOPEN_ACTION": "Επαναφορά",
|
"REOPEN_ACTION": "Επαναφορά",
|
||||||
@ -42,9 +43,10 @@
|
|||||||
"SEND": "Αποστολή",
|
"SEND": "Αποστολή",
|
||||||
"CREATE": "Σημείωση (Add Note)",
|
"CREATE": "Σημείωση (Add Note)",
|
||||||
"TWEET": "Tweet",
|
"TWEET": "Tweet",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Προβολή επεξεργαστή εμπλουτισμένου κειμένου",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Προβολή επιλογέα emoji",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Επισύναψη αρχείων",
|
||||||
|
"ENTER_TO_SEND": "Εισαγωγή για αποστολή"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "Ιδιωτική Σημείωση: Ορατή μόνο σε σας και την ομάδα σας",
|
"VISIBLE_TO_AGENTS": "Ιδιωτική Σημείωση: Ορατή μόνο σε σας και την ομάδα σας",
|
||||||
"CHANGE_STATUS": "Η κατάσταση της συνομιλίας άλλαξε",
|
"CHANGE_STATUS": "Η κατάσταση της συνομιλίας άλλαξε",
|
||||||
|
|||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Πάτησε enter για αφαίρεση",
|
"ENTER_TO_REMOVE": "Πάτησε enter για αφαίρεση",
|
||||||
"SELECT_ONE": "Επιλέξτε ένα"
|
"SELECT_ONE": "Επιλέξτε ένα"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "Ειδοποιήσεις",
|
||||||
|
"MARK_ALL_DONE": "Όλες ολοκληρωμένες",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Φόρτωση ειδοποιήσεων...",
|
||||||
|
"404": "Καμία Ειδοποίηση",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"Όνομα",
|
||||||
|
"Αριθμός Τηλεφώνου",
|
||||||
|
"Συζητήσεις",
|
||||||
|
"Τελευταία επικοινωνία"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "Νέα συνομιλία",
|
||||||
|
"conversation_assignment": "Η Συνομιλία Ανατέθηκε",
|
||||||
|
"assigned_conversation_new_message": "Νέο Μήνυμα",
|
||||||
|
"conversation_mention": "Αναφορά"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "Ενημέρωστε την προτίμηση για ειδοποιήσεις με email εδώ",
|
"NOTE": "Ενημέρωστε την προτίμηση για ειδοποιήσεις με email εδώ",
|
||||||
"CONVERSATION_ASSIGNMENT": "Να στέλνεται ειδοποίηση email όταν μια συνομιλία αντιστοιχίζεται σε μένα",
|
"CONVERSATION_ASSIGNMENT": "Να στέλνεται ειδοποίηση email όταν μια συνομιλία αντιστοιχίζεται σε μένα",
|
||||||
"CONVERSATION_CREATION": "Να στέλνεται ειδοποίηση όταν δημιουργείται μια νέα συνομιλία",
|
"CONVERSATION_CREATION": "Να στέλνεται ειδοποίηση όταν δημιουργείται μια νέα συνομιλία",
|
||||||
|
"CONVERSATION_MENTION": "Αποστολή ειδοποιήσεων email όταν αναφέρεστε σε μια συνομιλία",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Αποστολή ειδοποίησης email όταν ένα νέο μήνυμα δημιουργείται σε συνομιλία που έχει αναληφθεί"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Αποστολή ειδοποίησης email όταν ένα νέο μήνυμα δημιουργείται σε συνομιλία που έχει αναληφθεί"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "Ενημερώστε τις ρυθμίσεις για τις push ειδοποιήσεις εδώ",
|
"NOTE": "Ενημερώστε τις ρυθμίσεις για τις push ειδοποιήσεις εδώ",
|
||||||
"CONVERSATION_ASSIGNMENT": "Να ειδοποιούμαι με push όταν μια συνομιλία μου ανατείθεται",
|
"CONVERSATION_ASSIGNMENT": "Να ειδοποιούμαι με push όταν μια συνομιλία μου ανατείθεται",
|
||||||
"CONVERSATION_CREATION": "Να ειδοποιούμαι με push όταν δημιουργείται μια νέα συνομιλία",
|
"CONVERSATION_CREATION": "Να ειδοποιούμαι με push όταν δημιουργείται μια νέα συνομιλία",
|
||||||
|
"CONVERSATION_MENTION": "Αποστολή ειδοποιήσεων push όταν αναφέρεστε σε μια συνομιλία",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Αποστολή ειδοποίησης push όταν ένα νέο μήνυμα δημιουργείται σε συνομιλία που έχει αναληφθεί",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Αποστολή ειδοποίησης push όταν ένα νέο μήνυμα δημιουργείται σε συνομιλία που έχει αναληφθεί",
|
||||||
"HAS_ENABLED_PUSH": "Έχετε ενεργοποιήσει τις ειδοποιήσεις push για αυτόν τον browser.",
|
"HAS_ENABLED_PUSH": "Έχετε ενεργοποιήσει τις ειδοποιήσεις push για αυτόν τον browser.",
|
||||||
"REQUEST_PUSH": "Ενεργοποποίηση των ειδοποιήσεων push"
|
"REQUEST_PUSH": "Ενεργοποποίηση των ειδοποιήσεων push"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "Αρχική",
|
"HOME": "Αρχική",
|
||||||
"AGENTS": "Πράκτορες",
|
"AGENTS": "Πράκτορες",
|
||||||
"INBOXES": "Κιβώτια Εισερχομένων",
|
"INBOXES": "Κιβώτια Εισερχομένων",
|
||||||
|
"NOTIFICATIONS": "Ειδοποιήσεις",
|
||||||
"CANNED_RESPONSES": "Έτοιμες Απαντήσεις",
|
"CANNED_RESPONSES": "Έτοιμες Απαντήσεις",
|
||||||
"INTEGRATIONS": "Ενοποιήσεις",
|
"INTEGRATIONS": "Ενοποιήσεις",
|
||||||
"ACCOUNT_SETTINGS": "Ρυθμίσεις Λογαριασμού",
|
"ACCOUNT_SETTINGS": "Ρυθμίσεις Λογαριασμού",
|
||||||
"LABELS": "Ετικέτες"
|
"LABELS": "Ετικέτες",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "Νέος Λογαριασμός",
|
"NEW_ACCOUNT": "Νέος Λογαριασμός",
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Received via email",
|
"RECEIVED_VIA_EMAIL": "Received via email",
|
||||||
"VIEW_TWEET_IN_TWITTER": "View tweet in Twitter",
|
"VIEW_TWEET_IN_TWITTER": "View tweet in Twitter",
|
||||||
"REPLY_TO_TWEET": "Reply to this tweet"
|
"REPLY_TO_TWEET": "Reply to this tweet",
|
||||||
|
"NO_MESSAGES": "No Messages"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "Remove Selection",
|
"REMOVE_SELECTION": "Remove Selection",
|
||||||
"DOWNLOAD": "Download",
|
"DOWNLOAD": "Download",
|
||||||
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
||||||
|
"NO_RESPONSE": "No response",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Resolve",
|
"RESOLVE_ACTION": "Resolve",
|
||||||
"REOPEN_ACTION": "Reopen",
|
"REOPEN_ACTION": "Reopen",
|
||||||
@ -44,7 +45,8 @@
|
|||||||
"TWEET": "Tweet",
|
"TWEET": "Tweet",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Show emoji selector",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Attach files",
|
||||||
|
"ENTER_TO_SEND": "Enter to send"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "Private Note: Only visible to you and your team",
|
"VISIBLE_TO_AGENTS": "Private Note: Only visible to you and your team",
|
||||||
"CHANGE_STATUS": "Conversation status changed",
|
"CHANGE_STATUS": "Conversation status changed",
|
||||||
|
|||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Press enter to remove",
|
"ENTER_TO_REMOVE": "Press enter to remove",
|
||||||
"SELECT_ONE": "Select one"
|
"SELECT_ONE": "Select one"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "Notifications",
|
||||||
|
"MARK_ALL_DONE": "Mark All Done",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Loading notifications...",
|
||||||
|
"404": "No Notifications",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"Name",
|
||||||
|
"Phone Number",
|
||||||
|
"Conversations",
|
||||||
|
"Last Contacted"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "New conversation",
|
||||||
|
"conversation_assignment": "Conversation Assigned",
|
||||||
|
"assigned_conversation_new_message": "New Message",
|
||||||
|
"conversation_mention": "Mention"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,8 @@
|
|||||||
"PICK_A_VALUE": "Pick a value"
|
"PICK_A_VALUE": "Pick a value"
|
||||||
},
|
},
|
||||||
"TWITTER": {
|
"TWITTER": {
|
||||||
"HELP": "To add your Twitter profile as a channel, you need to authenticate your Twitter Profile by clicking on 'Sign in with Twitter' "
|
"HELP": "To add your Twitter profile as a channel, you need to authenticate your Twitter Profile by clicking on 'Sign in with Twitter' ",
|
||||||
|
"ERROR_MESSAGE": "There was an error connecting to Twitter, please try again"
|
||||||
},
|
},
|
||||||
"WEBSITE_CHANNEL": {
|
"WEBSITE_CHANNEL": {
|
||||||
"TITLE": "Website channel",
|
"TITLE": "Website channel",
|
||||||
@ -224,7 +225,8 @@
|
|||||||
"TABS": {
|
"TABS": {
|
||||||
"SETTINGS": "Settings",
|
"SETTINGS": "Settings",
|
||||||
"COLLABORATORS": "Collaborators",
|
"COLLABORATORS": "Collaborators",
|
||||||
"CONFIGURATION": "Configuration"
|
"CONFIGURATION": "Configuration",
|
||||||
|
"PRE_CHAT_FORM": "Pre Chat Form"
|
||||||
},
|
},
|
||||||
"SETTINGS": "Settings",
|
"SETTINGS": "Settings",
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
@ -250,6 +252,23 @@
|
|||||||
"SUBTITLE": "Your Facebook connection has expired, please reconnect your Facebook page to continue services",
|
"SUBTITLE": "Your Facebook connection has expired, please reconnect your Facebook page to continue services",
|
||||||
"MESSAGE_SUCCESS": "Reconnection successful",
|
"MESSAGE_SUCCESS": "Reconnection successful",
|
||||||
"MESSAGE_ERROR": "There was an error, please try again"
|
"MESSAGE_ERROR": "There was an error, please try again"
|
||||||
|
},
|
||||||
|
"PRE_CHAT_FORM": {
|
||||||
|
"DESCRIPTION": "Pre chat forms enable you to capture user information before they start conversation with you.",
|
||||||
|
"ENABLE": {
|
||||||
|
"LABEL": "Enable pre chat form",
|
||||||
|
"OPTIONS": {
|
||||||
|
"ENABLED": "Yes",
|
||||||
|
"DISABLED": "No"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PRE_CHAT_MESSAGE": {
|
||||||
|
"LABEL": "Pre Chat Message",
|
||||||
|
"PLACEHOLDER": "This message would be visible to the users along with the form"
|
||||||
|
},
|
||||||
|
"REQUIRE_EMAIL": {
|
||||||
|
"LABEL": "Visitors should provide their name and email address before starting the chat"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "Update your email notification preferences here",
|
"NOTE": "Update your email notification preferences here",
|
||||||
"CONVERSATION_ASSIGNMENT": "Send email notifications when a conversation is assigned to me",
|
"CONVERSATION_ASSIGNMENT": "Send email notifications when a conversation is assigned to me",
|
||||||
"CONVERSATION_CREATION": "Send email notifications when a new conversation is created",
|
"CONVERSATION_CREATION": "Send email notifications when a new conversation is created",
|
||||||
|
"CONVERSATION_MENTION": "Send email notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "Update your push notification preferences here",
|
"NOTE": "Update your push notification preferences here",
|
||||||
"CONVERSATION_ASSIGNMENT": "Send push notifications when a conversation is assigned to me",
|
"CONVERSATION_ASSIGNMENT": "Send push notifications when a conversation is assigned to me",
|
||||||
"CONVERSATION_CREATION": "Send push notifications when a new conversation is created",
|
"CONVERSATION_CREATION": "Send push notifications when a new conversation is created",
|
||||||
|
"CONVERSATION_MENTION": "Send push notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
||||||
"HAS_ENABLED_PUSH": "You have enabled push for this browser.",
|
"HAS_ENABLED_PUSH": "You have enabled push for this browser.",
|
||||||
"REQUEST_PUSH": "Enable push notifications"
|
"REQUEST_PUSH": "Enable push notifications"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "Home",
|
"HOME": "Home",
|
||||||
"AGENTS": "Agents",
|
"AGENTS": "Agents",
|
||||||
"INBOXES": "Inboxes",
|
"INBOXES": "Inboxes",
|
||||||
|
"NOTIFICATIONS": "Notifications",
|
||||||
"CANNED_RESPONSES": "Canned Responses",
|
"CANNED_RESPONSES": "Canned Responses",
|
||||||
"INTEGRATIONS": "Integrations",
|
"INTEGRATIONS": "Integrations",
|
||||||
"ACCOUNT_SETTINGS": "Account Settings",
|
"ACCOUNT_SETTINGS": "Account Settings",
|
||||||
"LABELS": "Labels"
|
"LABELS": "Labels",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "New Account",
|
"NEW_ACCOUNT": "New Account",
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Recibido por email",
|
"RECEIVED_VIA_EMAIL": "Recibido por email",
|
||||||
"VIEW_TWEET_IN_TWITTER": "Ver tweet en Twitter",
|
"VIEW_TWEET_IN_TWITTER": "Ver tweet en Twitter",
|
||||||
"REPLY_TO_TWEET": "Responder a este tweet"
|
"REPLY_TO_TWEET": "Responder a este tweet",
|
||||||
|
"NO_MESSAGES": "No Messages"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "Eliminar selección",
|
"REMOVE_SELECTION": "Eliminar selección",
|
||||||
"DOWNLOAD": "Descargar",
|
"DOWNLOAD": "Descargar",
|
||||||
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
||||||
|
"NO_RESPONSE": "No response",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Resolver",
|
"RESOLVE_ACTION": "Resolver",
|
||||||
"REOPEN_ACTION": "Reabrir",
|
"REOPEN_ACTION": "Reabrir",
|
||||||
@ -44,7 +45,8 @@
|
|||||||
"TWEET": "Tweet",
|
"TWEET": "Tweet",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Show emoji selector",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Attach files",
|
||||||
|
"ENTER_TO_SEND": "Enter to send"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "Nota privada: solo visible para ti y tu equipo",
|
"VISIBLE_TO_AGENTS": "Nota privada: solo visible para ti y tu equipo",
|
||||||
"CHANGE_STATUS": "Estado de la conversación cambiado",
|
"CHANGE_STATUS": "Estado de la conversación cambiado",
|
||||||
|
|||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Press enter to remove",
|
"ENTER_TO_REMOVE": "Press enter to remove",
|
||||||
"SELECT_ONE": "Select one"
|
"SELECT_ONE": "Select one"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "Notificaciones",
|
||||||
|
"MARK_ALL_DONE": "Mark All Done",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Loading notifications...",
|
||||||
|
"404": "No Notifications",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"Nombre",
|
||||||
|
"Número telefónico",
|
||||||
|
"Conversaciones",
|
||||||
|
"Last Contacted"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "New conversation",
|
||||||
|
"conversation_assignment": "Conversation Assigned",
|
||||||
|
"assigned_conversation_new_message": "New Message",
|
||||||
|
"conversation_mention": "Mention"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "Actualiza tus preferencias de notificación por correo electrónico aquí",
|
"NOTE": "Actualiza tus preferencias de notificación por correo electrónico aquí",
|
||||||
"CONVERSATION_ASSIGNMENT": "Enviar notificaciones por correo electrónico cuando se me ha asignado una conversación",
|
"CONVERSATION_ASSIGNMENT": "Enviar notificaciones por correo electrónico cuando se me ha asignado una conversación",
|
||||||
"CONVERSATION_CREATION": "Enviar notificaciones por correo electrónico cuando se crea una nueva conversación",
|
"CONVERSATION_CREATION": "Enviar notificaciones por correo electrónico cuando se crea una nueva conversación",
|
||||||
|
"CONVERSATION_MENTION": "Send email notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Envirar notificaciones por correo electrónico cuando un nuevo mensaje es creado en una conversación asignada"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Envirar notificaciones por correo electrónico cuando un nuevo mensaje es creado en una conversación asignada"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "Actualize sus preferencias de notificaciones push aquí",
|
"NOTE": "Actualize sus preferencias de notificaciones push aquí",
|
||||||
"CONVERSATION_ASSIGNMENT": "Enviar notificaciones push cuando se me ha asignado una conversación",
|
"CONVERSATION_ASSIGNMENT": "Enviar notificaciones push cuando se me ha asignado una conversación",
|
||||||
"CONVERSATION_CREATION": "Enviar notificaciones push cuando se crea una nueva conversación",
|
"CONVERSATION_CREATION": "Enviar notificaciones push cuando se crea una nueva conversación",
|
||||||
|
"CONVERSATION_MENTION": "Send push notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Enviar notificaciones push cuando un nuevo mensaja es creadao en una conversación asignada",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Enviar notificaciones push cuando un nuevo mensaja es creadao en una conversación asignada",
|
||||||
"HAS_ENABLED_PUSH": "Has habilitado notificaciones push para este navegador.",
|
"HAS_ENABLED_PUSH": "Has habilitado notificaciones push para este navegador.",
|
||||||
"REQUEST_PUSH": "Habilitar notificaciones push"
|
"REQUEST_PUSH": "Habilitar notificaciones push"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "Inicio",
|
"HOME": "Inicio",
|
||||||
"AGENTS": "Agentes",
|
"AGENTS": "Agentes",
|
||||||
"INBOXES": "Entradas",
|
"INBOXES": "Entradas",
|
||||||
|
"NOTIFICATIONS": "Notificaciones",
|
||||||
"CANNED_RESPONSES": "Respuestas predefinidas",
|
"CANNED_RESPONSES": "Respuestas predefinidas",
|
||||||
"INTEGRATIONS": "Integraciones",
|
"INTEGRATIONS": "Integraciones",
|
||||||
"ACCOUNT_SETTINGS": "Configuración de la cuenta",
|
"ACCOUNT_SETTINGS": "Configuración de la cuenta",
|
||||||
"LABELS": "Etiquetas"
|
"LABELS": "Etiquetas",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "Nueva cuenta",
|
"NEW_ACCOUNT": "Nueva cuenta",
|
||||||
|
|||||||
@ -14,9 +14,9 @@
|
|||||||
"ERROR": "Full name is too short"
|
"ERROR": "Full name is too short"
|
||||||
},
|
},
|
||||||
"EMAIL": {
|
"EMAIL": {
|
||||||
"LABEL": "Work email",
|
"LABEL": "Email de trabajo",
|
||||||
"PLACEHOLDER": "Enter your work email address. eg: bruce@wayne.enterprises",
|
"PLACEHOLDER": "Introduzca su dirección de correo electrónico de trabajo. Ejemplo: bruce@wayne.enterprises",
|
||||||
"ERROR": "Email address is invalid"
|
"ERROR": "Dirección de correo no válida"
|
||||||
},
|
},
|
||||||
"PASSWORD": {
|
"PASSWORD": {
|
||||||
"LABEL": "Contraseña",
|
"LABEL": "Contraseña",
|
||||||
@ -33,6 +33,6 @@
|
|||||||
"ERROR_MESSAGE": "No se pudo conectar al servidor Woot, por favor inténtalo de nuevo más tarde"
|
"ERROR_MESSAGE": "No se pudo conectar al servidor Woot, por favor inténtalo de nuevo más tarde"
|
||||||
},
|
},
|
||||||
"SUBMIT": "Enviar",
|
"SUBMIT": "Enviar",
|
||||||
"HAVE_AN_ACCOUNT": "Already have an account?"
|
"HAVE_AN_ACCOUNT": "¿Ya tienes una cuenta?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "از طریق ایمیل دریافت شد",
|
"RECEIVED_VIA_EMAIL": "از طریق ایمیل دریافت شد",
|
||||||
"VIEW_TWEET_IN_TWITTER": "مشاهده توییت در توییتر",
|
"VIEW_TWEET_IN_TWITTER": "مشاهده توییت در توییتر",
|
||||||
"REPLY_TO_TWEET": "پاسخ به این توییت"
|
"REPLY_TO_TWEET": "پاسخ به این توییت",
|
||||||
|
"NO_MESSAGES": "هیچ پیامی وجود ندارد"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,9 +34,9 @@
|
|||||||
"NO_AVAILABLE_LABELS": "هیچ برچسبی به این گفتگو اضافه نشده است."
|
"NO_AVAILABLE_LABELS": "هیچ برچسبی به این گفتگو اضافه نشده است."
|
||||||
},
|
},
|
||||||
"MUTE_CONTACT": "بیصدا کردن گفتگو",
|
"MUTE_CONTACT": "بیصدا کردن گفتگو",
|
||||||
"UNMUTE_CONTACT": "Unmute Conversation",
|
"UNMUTE_CONTACT": "خارج کردن از حالت بی صدا",
|
||||||
"MUTED_SUCCESS": "این گفتگو به مدت ۶ ساعت بیصدا است",
|
"MUTED_SUCCESS": "این گفتگو به مدت ۶ ساعت بیصدا است",
|
||||||
"UNMUTED_SUCCESS": "This conversation is unmuted",
|
"UNMUTED_SUCCESS": "این گفتگو از حالت بی صدا خارج شده است",
|
||||||
"SEND_TRANSCRIPT": "ارسال متن",
|
"SEND_TRANSCRIPT": "ارسال متن",
|
||||||
"EDIT_LABEL": "ویرایش"
|
"EDIT_LABEL": "ویرایش"
|
||||||
},
|
},
|
||||||
@ -108,7 +108,7 @@
|
|||||||
"نام",
|
"نام",
|
||||||
"شماره تلفن",
|
"شماره تلفن",
|
||||||
"گفتگوها",
|
"گفتگوها",
|
||||||
"Last Contacted"
|
"آخرین تماس"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"CONVERSATION": {
|
"CONVERSATION": {
|
||||||
"404": "لطفا یک گفتگو را از پنجره سمت چپ انتخاب کنید",
|
"404": "لطفا یک گفتگو را از پنجره گفتگوها انتخاب کنید",
|
||||||
"NO_MESSAGE_1": "اوه اوه! به نظر میرسد هیچ پیامی از طرف مشتری در صندوق ورودی شما وجود ندارد.",
|
"NO_MESSAGE_1": "اوه اوه! به نظر میرسد هیچ پیامی از طرف مشتری در صندوق ورودی شما وجود ندارد.",
|
||||||
"NO_MESSAGE_2": " برای ارسال پیام به صفحه خود بروید!",
|
"NO_MESSAGE_2": " برای ارسال پیام به صفحه خود بروید!",
|
||||||
"NO_INBOX_1": "سلام! به نظر میرسد هنوز صندوق ورودی اضافه نکردهاید.",
|
"NO_INBOX_1": "سلام! به نظر میرسد هنوز صندوق ورودی اضافه نکردهاید.",
|
||||||
@ -11,7 +11,7 @@
|
|||||||
"TITLE": "جستجو پیامها",
|
"TITLE": "جستجو پیامها",
|
||||||
"LOADING_MESSAGE": "درحال پردازش داده...",
|
"LOADING_MESSAGE": "درحال پردازش داده...",
|
||||||
"PLACEHOLDER": "متنی برای جستجو پیام تایپ کنید",
|
"PLACEHOLDER": "متنی برای جستجو پیام تایپ کنید",
|
||||||
"NO_MATCHING_RESULTS": "There are no messages matching the search parameters."
|
"NO_MATCHING_RESULTS": "گفتگویی با مشخصات ورودی یافت نشد."
|
||||||
},
|
},
|
||||||
"UNREAD_MESSAGES": "پیامهای خوانده نشده",
|
"UNREAD_MESSAGES": "پیامهای خوانده نشده",
|
||||||
"UNREAD_MESSAGE": "پیام خوانده نشده",
|
"UNREAD_MESSAGE": "پیام خوانده نشده",
|
||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "حذف انتخابشدهها",
|
"REMOVE_SELECTION": "حذف انتخابشدهها",
|
||||||
"DOWNLOAD": "دانلود",
|
"DOWNLOAD": "دانلود",
|
||||||
"UPLOADING_ATTACHMENTS": "در حال بارگذاری پیوستها...",
|
"UPLOADING_ATTACHMENTS": "در حال بارگذاری پیوستها...",
|
||||||
|
"NO_RESPONSE": "بدون پاسخ",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "حل شد",
|
"RESOLVE_ACTION": "حل شد",
|
||||||
"REOPEN_ACTION": "دوباره باز کنید",
|
"REOPEN_ACTION": "دوباره باز کنید",
|
||||||
@ -33,8 +34,8 @@
|
|||||||
"DETAILS": "جزئیات"
|
"DETAILS": "جزئیات"
|
||||||
},
|
},
|
||||||
"FOOTER": {
|
"FOOTER": {
|
||||||
"MSG_INPUT": "Shift + enter for new line. Start with '/' to select a Canned Response.",
|
"MSG_INPUT": "برای رفتن به سرخط shift+enter و برای استفاده از پیام های ذخیره شده / را بزنید.",
|
||||||
"PRIVATE_MSG_INPUT": "Shift + enter for new line. This will be visible only to Agents"
|
"PRIVATE_MSG_INPUT": "برای رفتن به سرخط shift+enter را بزنید. این پیام فقط به ایجنت ها نمایش داده میشود"
|
||||||
},
|
},
|
||||||
"REPLYBOX": {
|
"REPLYBOX": {
|
||||||
"REPLY": "پاسخ",
|
"REPLY": "پاسخ",
|
||||||
@ -42,18 +43,19 @@
|
|||||||
"SEND": "ارسال",
|
"SEND": "ارسال",
|
||||||
"CREATE": "افزودن یادداشت",
|
"CREATE": "افزودن یادداشت",
|
||||||
"TWEET": "توییت",
|
"TWEET": "توییت",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "نمایش ویرایشگر متنی پیشرفته",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "انتخاب ایموجی",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "ضمیمه فایل",
|
||||||
|
"ENTER_TO_SEND": "برای ارسال Enter را بزنید"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "یادداشت خصوصی: فقط برای شما و تیم شما قابل مشاهده است",
|
"VISIBLE_TO_AGENTS": "یادداشت خصوصی: فقط برای شما و تیم شما قابل مشاهده است",
|
||||||
"CHANGE_STATUS": "وضعیت گفتگو تغییر کرد",
|
"CHANGE_STATUS": "وضعیت گفتگو تغییر کرد",
|
||||||
"CHANGE_AGENT": "Conversation Assignee changed",
|
"CHANGE_AGENT": "مسول گفتگو تغییر کرد",
|
||||||
"SENT_BY": "Sent by:",
|
"SENT_BY": "ارسال شده توسط:",
|
||||||
"ASSIGNMENT": {
|
"ASSIGNMENT": {
|
||||||
"SELECT_AGENT": "Select Agent",
|
"SELECT_AGENT": "انتخاب ایجنت",
|
||||||
"REMOVE": "حذف",
|
"REMOVE": "حذف",
|
||||||
"ASSIGN": "Assign"
|
"ASSIGN": "اختصاص دادن"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EMAIL_TRANSCRIPT": {
|
"EMAIL_TRANSCRIPT": {
|
||||||
@ -65,7 +67,7 @@
|
|||||||
"SEND_EMAIL_ERROR": "خطایی پیش آمد. لطفا دوباره امتحان کنید",
|
"SEND_EMAIL_ERROR": "خطایی پیش آمد. لطفا دوباره امتحان کنید",
|
||||||
"FORM": {
|
"FORM": {
|
||||||
"SEND_TO_CONTACT": "متن گفتگو را برای مشتری ارسال کنید",
|
"SEND_TO_CONTACT": "متن گفتگو را برای مشتری ارسال کنید",
|
||||||
"SEND_TO_AGENT": "Send the transcript to the assigned agent",
|
"SEND_TO_AGENT": "متن گفتگو را به ایجنت اختصاص یافته ارسال کنید",
|
||||||
"SEND_TO_OTHER_EMAIL_ADDRESS": "متن گفتگو را به آدرس ایمیل دیگری ارسال کنید",
|
"SEND_TO_OTHER_EMAIL_ADDRESS": "متن گفتگو را به آدرس ایمیل دیگری ارسال کنید",
|
||||||
"EMAIL": {
|
"EMAIL": {
|
||||||
"PLACEHOLDER": "یک آدرس ایمیل وارد کنید",
|
"PLACEHOLDER": "یک آدرس ایمیل وارد کنید",
|
||||||
|
|||||||
@ -34,22 +34,42 @@
|
|||||||
"ERROR": ""
|
"ERROR": ""
|
||||||
},
|
},
|
||||||
"AUTO_RESOLVE_DURATION": {
|
"AUTO_RESOLVE_DURATION": {
|
||||||
"LABEL": "Number of days after a ticket should auto resolve if there is no activity",
|
"LABEL": "تعداد روزهایی که اگر فعالیتی وجود نداشته باشد، گفتگو به صورت خودکار بسته شود",
|
||||||
"PLACEHOLDER": "30",
|
"PLACEHOLDER": "30",
|
||||||
"ERROR": "Please enter a valid auto resolve duration (minimum 1 day)"
|
"ERROR": "لطفاً یک مدت زمان حل خودکار معتبر (حداقل 1 روز) وارد کنید"
|
||||||
},
|
},
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"INBOUND_EMAIL_ENABLED": "Conversation continuity with emails is enabled for your account.",
|
"INBOUND_EMAIL_ENABLED": "تداوم مکالمه با ایمیل برای حساب شما فعال است.",
|
||||||
"CUSTOM_EMAIL_DOMAIN_ENABLED": "اکنون میتوانید ایمیلها را در دامنه سفارشی خود دریافت کنید."
|
"CUSTOM_EMAIL_DOMAIN_ENABLED": "اکنون میتوانید ایمیلها را در دامنه سفارشی خود دریافت کنید."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"UPDATE_CHATWOOT": "An update %{latestChatwootVersion} for Chatwoot is available. Please update your instance."
|
"UPDATE_CHATWOOT": "به روزرسانی%{latestChatwootVersion} برای Chatwoot در دسترس است. لطفا نمونه خود را به روز کنید."
|
||||||
},
|
},
|
||||||
"FORMS": {
|
"FORMS": {
|
||||||
"MULTISELECT": {
|
"MULTISELECT": {
|
||||||
"ENTER_TO_SELECT": "Press enter to select",
|
"ENTER_TO_SELECT": "برای انتخاب Enter را فشار دهید",
|
||||||
"ENTER_TO_REMOVE": "Press enter to remove",
|
"ENTER_TO_REMOVE": "برای حذف دکمه enter را فشار دهید",
|
||||||
"SELECT_ONE": "Select one"
|
"SELECT_ONE": "یکی را انتخاب کن"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "اعلان ها",
|
||||||
|
"MARK_ALL_DONE": "همه را به انجام شده تغییر بده",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "در حال بارگیری اعلان ها...",
|
||||||
|
"404": "اعلانی وجود ندارد",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"نام",
|
||||||
|
"شماره تلفن",
|
||||||
|
"گفتگوها",
|
||||||
|
"آخرین تماس"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "گفتگوی جدید",
|
||||||
|
"conversation_assignment": "مکالمه اختصاص داده شد",
|
||||||
|
"assigned_conversation_new_message": "پیام جدید",
|
||||||
|
"conversation_mention": "اشاره"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,10 +75,10 @@
|
|||||||
},
|
},
|
||||||
"REPLY_TIME": {
|
"REPLY_TIME": {
|
||||||
"TITLE": "تنظیم زمان پاسخ",
|
"TITLE": "تنظیم زمان پاسخ",
|
||||||
"IN_A_FEW_MINUTES": "In a few minutes",
|
"IN_A_FEW_MINUTES": "در چند دقیقه",
|
||||||
"IN_A_FEW_HOURS": "In a few hours",
|
"IN_A_FEW_HOURS": "در چند ساعت",
|
||||||
"IN_A_DAY": "In a day",
|
"IN_A_DAY": "در یک روز",
|
||||||
"HELP_TEXT": "This reply time will be displayed on the live chat widget"
|
"HELP_TEXT": "این زمان پاسخ در ویجت چت زنده نمایش داده می شود"
|
||||||
},
|
},
|
||||||
"WIDGET_COLOR": {
|
"WIDGET_COLOR": {
|
||||||
"LABEL": "رنگ ویجت",
|
"LABEL": "رنگ ویجت",
|
||||||
@ -124,7 +124,7 @@
|
|||||||
},
|
},
|
||||||
"API_CHANNEL": {
|
"API_CHANNEL": {
|
||||||
"TITLE": "کانال API",
|
"TITLE": "کانال API",
|
||||||
"DESC": "Integrate with API channel and start supporting your customers.",
|
"DESC": "با کانال API ادغام شده و از مشتریان خود پشتیبانی کنید.",
|
||||||
"CHANNEL_NAME": {
|
"CHANNEL_NAME": {
|
||||||
"LABEL": "عنوان کانال",
|
"LABEL": "عنوان کانال",
|
||||||
"PLACEHOLDER": "لطفا اسم یک کانال را وارد کنید",
|
"PLACEHOLDER": "لطفا اسم یک کانال را وارد کنید",
|
||||||
@ -132,12 +132,12 @@
|
|||||||
},
|
},
|
||||||
"WEBHOOK_URL": {
|
"WEBHOOK_URL": {
|
||||||
"LABEL": "آدرس URL وب هوک",
|
"LABEL": "آدرس URL وب هوک",
|
||||||
"SUBTITLE": "Configure the URL where you want to recieve callbacks on events.",
|
"SUBTITLE": "آدرس کال بک خود را جهت دریافت اطلاعات رویداد های وارد کنید.",
|
||||||
"PLACEHOLDER": "آدرس URL وب هوک"
|
"PLACEHOLDER": "آدرس URL وب هوک"
|
||||||
},
|
},
|
||||||
"SUBMIT_BUTTON": "ایجاد کانال API",
|
"SUBMIT_BUTTON": "ایجاد کانال API",
|
||||||
"API": {
|
"API": {
|
||||||
"ERROR_MESSAGE": "We were not able to save the api channel"
|
"ERROR_MESSAGE": "کانال API ذخیره نشد"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EMAIL_CHANNEL": {
|
"EMAIL_CHANNEL": {
|
||||||
@ -150,14 +150,14 @@
|
|||||||
},
|
},
|
||||||
"EMAIL": {
|
"EMAIL": {
|
||||||
"LABEL": "ایمیل",
|
"LABEL": "ایمیل",
|
||||||
"SUBTITLE": "Email where your customers sends you support tickets",
|
"SUBTITLE": "آدرس ایمیلی که مشتریان درخواست پشتیبانی را برای شما ارسال کنند",
|
||||||
"PLACEHOLDER": "ایمیل"
|
"PLACEHOLDER": "ایمیل"
|
||||||
},
|
},
|
||||||
"SUBMIT_BUTTON": "ایجاد کانال ایمیل",
|
"SUBMIT_BUTTON": "ایجاد کانال ایمیل",
|
||||||
"API": {
|
"API": {
|
||||||
"ERROR_MESSAGE": "We were not able to save the email channel"
|
"ERROR_MESSAGE": "کانال ایمیل ذخیره نشد"
|
||||||
},
|
},
|
||||||
"FINISH_MESSAGE": "Start forwarding your emails to the following email address."
|
"FINISH_MESSAGE": "ایمیل های خود را به این آدرس ها فوروارد کنید."
|
||||||
},
|
},
|
||||||
"AUTH": {
|
"AUTH": {
|
||||||
"TITLE": "کانالها",
|
"TITLE": "کانالها",
|
||||||
@ -166,8 +166,8 @@
|
|||||||
"AGENTS": {
|
"AGENTS": {
|
||||||
"TITLE": "اپراتورها",
|
"TITLE": "اپراتورها",
|
||||||
"DESC": "در اینجا میتوانید اپراتورها را به صندوق ورودی خود اختصاص دهید. توجه داشته باشید که فقط اپراتورهایی که در اینجا معرفی شده باشند میتوانند به پیامهای این صندوق پاسخ بدهند.دیگر اپراتورها نخواهند توانست پیامهای این صندوق را ببینید یا به آنها پاسخی بدهند. <br> <b>پانویس:</b>به عنوان مدیر اگر میخواهید به همه صندوقهای ورودی دسترسی داشته باشید میبایست خود را به عنوان اپراتور به همه صندوقها اضافه کنید.",
|
"DESC": "در اینجا میتوانید اپراتورها را به صندوق ورودی خود اختصاص دهید. توجه داشته باشید که فقط اپراتورهایی که در اینجا معرفی شده باشند میتوانند به پیامهای این صندوق پاسخ بدهند.دیگر اپراتورها نخواهند توانست پیامهای این صندوق را ببینید یا به آنها پاسخی بدهند. <br> <b>پانویس:</b>به عنوان مدیر اگر میخواهید به همه صندوقهای ورودی دسترسی داشته باشید میبایست خود را به عنوان اپراتور به همه صندوقها اضافه کنید.",
|
||||||
"VALIDATION_ERROR": "Add atleast one agent to your new Inbox",
|
"VALIDATION_ERROR": "حداقل یک ایجنت به اینباکس جدید اضافه کنید",
|
||||||
"PICK_AGENTS": "Pick agents for the inbox"
|
"PICK_AGENTS": "انتخاب ایجنت ها برای این صندوق ورودی"
|
||||||
},
|
},
|
||||||
"DETAILS": {
|
"DETAILS": {
|
||||||
"TITLE": "جزئیات صندوق ورودی",
|
"TITLE": "جزئیات صندوق ورودی",
|
||||||
@ -229,8 +229,8 @@
|
|||||||
"SETTINGS": "تنظیمات",
|
"SETTINGS": "تنظیمات",
|
||||||
"FEATURES": {
|
"FEATURES": {
|
||||||
"LABEL": "امکانات",
|
"LABEL": "امکانات",
|
||||||
"DISPLAY_FILE_PICKER": "Display file picker on the widget",
|
"DISPLAY_FILE_PICKER": "نمایش گزینه ضمیه فایل در ویجت",
|
||||||
"DISPLAY_EMOJI_PICKER": "Display emoji picker on the widget"
|
"DISPLAY_EMOJI_PICKER": "نمایش گزینه انتخاب ایموجی در ویجت"
|
||||||
},
|
},
|
||||||
"SETTINGS_POPUP": {
|
"SETTINGS_POPUP": {
|
||||||
"MESSENGER_HEADING": "اسکریپت ویجت",
|
"MESSENGER_HEADING": "اسکریپت ویجت",
|
||||||
@ -242,13 +242,13 @@
|
|||||||
"INBOX_UPDATE_TITLE": "تنظیمات صندوق ورودی",
|
"INBOX_UPDATE_TITLE": "تنظیمات صندوق ورودی",
|
||||||
"INBOX_UPDATE_SUB_TEXT": "تغییر پارامترهای صندوق ورودی",
|
"INBOX_UPDATE_SUB_TEXT": "تغییر پارامترهای صندوق ورودی",
|
||||||
"AUTO_ASSIGNMENT_SUB_TEXT": "فعال کردن یا غیرفعال کردن واگذاری خودکار گفتگوها به اپراتورهای عضو این صندوق ورودی.",
|
"AUTO_ASSIGNMENT_SUB_TEXT": "فعال کردن یا غیرفعال کردن واگذاری خودکار گفتگوها به اپراتورهای عضو این صندوق ورودی.",
|
||||||
"HMAC_VERIFICATION": "User Identity Validation",
|
"HMAC_VERIFICATION": "اعتبار سنجی کاربر",
|
||||||
"HMAC_DESCRIPTION": "Inorder validate the users identity, the SDK allows you to pass an `identity_hash` for each user. You can generate HMAC using 'sha256' with the key shown here."
|
"HMAC_DESCRIPTION": "برای اعتبار سنجی کاربران با استفاده از این کلید یک هش HMAC sha256 تولید کنید."
|
||||||
},
|
},
|
||||||
"FACEBOOK_REAUTHORIZE": {
|
"FACEBOOK_REAUTHORIZE": {
|
||||||
"TITLE": "احراز هویت مجدد",
|
"TITLE": "احراز هویت مجدد",
|
||||||
"SUBTITLE": "Your Facebook connection has expired, please reconnect your Facebook page to continue services",
|
"SUBTITLE": "اتصال فیس بوک شما منقضی شده است ، لطفاً برای ادامه خدمات دوباره صفحه فیس بوک خود را متصل کنید",
|
||||||
"MESSAGE_SUCCESS": "Reconnection successful",
|
"MESSAGE_SUCCESS": "اتصال مجدد موفقیت آمیز بود",
|
||||||
"MESSAGE_ERROR": "خطایی پیش آمد. لطفا دوباره امتحان کنید"
|
"MESSAGE_ERROR": "خطایی پیش آمد. لطفا دوباره امتحان کنید"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"HEADER_BTN_TXT": "افزودن برچسب",
|
"HEADER_BTN_TXT": "افزودن برچسب",
|
||||||
"LOADING": "درحال گرفتن برچسبها",
|
"LOADING": "درحال گرفتن برچسبها",
|
||||||
"SEARCH_404": "هیچ آیتمی با این مشخصات یافت نشد",
|
"SEARCH_404": "هیچ آیتمی با این مشخصات یافت نشد",
|
||||||
"SIDEBAR_TXT": "<p><b>Labels</b> <p>Labels help you to categorize conversations and prioritize them. You can assign label to a conversation from the sidepanel. <br /><br />Labels are tied to the account and can be used to create custom workflows in your organization. You can assign custom color to a label, it makes it easier to identify the label. You will be able to display the label on the sidebar to filter the conversations easily.</p>",
|
"SIDEBAR_TXT": "<p>\n <b> برچسب ها</b>\n<p> برچسب ها به شما در دسته بندی مکالمات و اولویت بندی آنها کمک می کند. می توانید برچسب را به یک مکالمه از نوار کناری اختصاص دهید.\n <br/>\n <br/>\n برچسب ها به حساب گره خورده اند و می توانند برای ایجاد گردش کار سفارشی در سازمان شما استفاده شوند. می توانید رنگ سفارشی را به برچسب اختصاص دهید ، شناسایی برچسب آسان تر می شود. شما می توانید برچسب را در نوار کناری نمایش دهید تا مکالمه ها به راحتی فیلتر شود.\n</p>",
|
||||||
"LIST": {
|
"LIST": {
|
||||||
"404": "هیچ برچسبی در این حسابکاربری وجود ندارد.",
|
"404": "هیچ برچسبی در این حسابکاربری وجود ندارد.",
|
||||||
"TITLE": "مدیریت برچسبها",
|
"TITLE": "مدیریت برچسبها",
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"HEADER": "گزارشات",
|
"HEADER": "گزارشات",
|
||||||
"LOADING_CHART": "در حال دریافت اطلاعات...",
|
"LOADING_CHART": "در حال دریافت اطلاعات...",
|
||||||
"NO_ENOUGH_DATA": "متاسفانه اطلاعات کافی دریافت نشد، لطفا بعدا دوباره امتحان کنید",
|
"NO_ENOUGH_DATA": "متاسفانه اطلاعات کافی دریافت نشد، لطفا بعدا دوباره امتحان کنید",
|
||||||
"DOWNLOAD_AGENT_REPORTS": "Download agent reports",
|
"DOWNLOAD_AGENT_REPORTS": "دانلود گزارش ایجنت",
|
||||||
"METRICS": {
|
"METRICS": {
|
||||||
"CONVERSATIONS": {
|
"CONVERSATIONS": {
|
||||||
"NAME": "گفتگوها",
|
"NAME": "گفتگوها",
|
||||||
|
|||||||
@ -27,7 +27,8 @@
|
|||||||
"NOTE": "اینجا میتوانید تنظیمات اعلامیههایی که به ایمیل ارسال میشود تغییر دهید",
|
"NOTE": "اینجا میتوانید تنظیمات اعلامیههایی که به ایمیل ارسال میشود تغییر دهید",
|
||||||
"CONVERSATION_ASSIGNMENT": "هر وقت گفتگویی به من اختصاص داده شد، ایمیل بفرست",
|
"CONVERSATION_ASSIGNMENT": "هر وقت گفتگویی به من اختصاص داده شد، ایمیل بفرست",
|
||||||
"CONVERSATION_CREATION": "هر وقت گفتگوی جدیدی شروع شد برای من ایمیل بفرست",
|
"CONVERSATION_CREATION": "هر وقت گفتگوی جدیدی شروع شد برای من ایمیل بفرست",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send email notifications when a new message is created in an assigned conversation"
|
"CONVERSATION_MENTION": "هنگامی که از شما در گفتگو نام برده میشود، از طریق ایمیل آگاهسازی ارسال کن",
|
||||||
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "با ایجاد پیام جدید در مکالمه اختصاصی ، اعلان های ایمیل را ارسال کنید"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
"UPDATE_SUCCESS": "تغییرات تنظیمات اعلامیهها با موفقیت ثبت شد",
|
"UPDATE_SUCCESS": "تغییرات تنظیمات اعلامیهها با موفقیت ثبت شد",
|
||||||
@ -38,7 +39,8 @@
|
|||||||
"NOTE": "اینجا میتوانید تنظیمات پوش نوتیفیکیشن را تغییر دهید",
|
"NOTE": "اینجا میتوانید تنظیمات پوش نوتیفیکیشن را تغییر دهید",
|
||||||
"CONVERSATION_ASSIGNMENT": "هر وقت گفتگویی به من اختصاص داده شد، برای من پوش نوتیفیکیشن بفرست",
|
"CONVERSATION_ASSIGNMENT": "هر وقت گفتگویی به من اختصاص داده شد، برای من پوش نوتیفیکیشن بفرست",
|
||||||
"CONVERSATION_CREATION": "هر وقت گفتگوی جدیدی شروع شد برای من پوش نوتیفیکیشن بفرست",
|
"CONVERSATION_CREATION": "هر وقت گفتگوی جدیدی شروع شد برای من پوش نوتیفیکیشن بفرست",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Send push notifications when a new message is created in an assigned conversation",
|
"CONVERSATION_MENTION": "هنگامی که از شما در مکالمه نام برده می شود پوش نوتیفیکیشن ارسال کنید",
|
||||||
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "وقتی پیام جدیدی در مکالمه اختصاص داده شده ایجاد می شود ، پوش نوتیفیکیشن را ارسال کنید",
|
||||||
"HAS_ENABLED_PUSH": "در این مرورگر پوش نوتیفیکیشن را فعال کردهاید",
|
"HAS_ENABLED_PUSH": "در این مرورگر پوش نوتیفیکیشن را فعال کردهاید",
|
||||||
"REQUEST_PUSH": "فعال کردن پوش نوتیفیکیشن"
|
"REQUEST_PUSH": "فعال کردن پوش نوتیفیکیشن"
|
||||||
},
|
},
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "صفحه اصلی",
|
"HOME": "صفحه اصلی",
|
||||||
"AGENTS": "اپراتورها",
|
"AGENTS": "اپراتورها",
|
||||||
"INBOXES": "صندوقهای ورودی",
|
"INBOXES": "صندوقهای ورودی",
|
||||||
|
"NOTIFICATIONS": "اعلان ها",
|
||||||
"CANNED_RESPONSES": "پاسخهای آماده",
|
"CANNED_RESPONSES": "پاسخهای آماده",
|
||||||
"INTEGRATIONS": "برنامههای تلفیق شده",
|
"INTEGRATIONS": "برنامههای تلفیق شده",
|
||||||
"ACCOUNT_SETTINGS": "تنظیمات حساب",
|
"ACCOUNT_SETTINGS": "تنظیمات حساب",
|
||||||
"LABELS": "برچسبها"
|
"LABELS": "برچسبها",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "حسابکاربری جدید",
|
"NEW_ACCOUNT": "حسابکاربری جدید",
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
"REGISTER": {
|
"REGISTER": {
|
||||||
"TRY_WOOT": "ثبت حساب",
|
"TRY_WOOT": "ثبت حساب",
|
||||||
"TITLE": "ثبت نام",
|
"TITLE": "ثبت نام",
|
||||||
"TERMS_ACCEPT": "با ثبت نام، اعلام میدارید که <a href=\"https://www.chatwoot.com/terms\">قوانین</a> و <a href=\"https://www.chatwoot.com/privacy-policy\">شرایط استفاده</a> از ما را تایید کرده و میپذیرید.",
|
"TERMS_ACCEPT": "با ثبت نام، اعلام میدارید که <a href=\"https://www.chatwoot.com/terms\">قوانین</a> و <a href=\"https://www.chatwoot.com/privacy-policy\">شرایط استفاده</a> از این نرم افزار را تایید کرده و میپذیرید",
|
||||||
"ACCOUNT_NAME": {
|
"ACCOUNT_NAME": {
|
||||||
"LABEL": "عنوان حساب",
|
"LABEL": "عنوان حساب",
|
||||||
"PLACEHOLDER": "نام حسابکاربری را وارد کنید. به عنوان مثال: شرکت وین",
|
"PLACEHOLDER": "نام حساب کاربری را وارد کنید. به عنوان مثال: شرکت وین",
|
||||||
"ERROR": "نام حسابکاربری خیلی کوتاه است"
|
"ERROR": "نام حساب کاربری خیلی کوتاه است"
|
||||||
},
|
},
|
||||||
"FULL_NAME": {
|
"FULL_NAME": {
|
||||||
"LABEL": "نام کامل",
|
"LABEL": "نام کامل",
|
||||||
@ -33,6 +33,6 @@
|
|||||||
"ERROR_MESSAGE": "ارتباط با سرور برقرار نشد، لطفا بعدا امتحان کنید"
|
"ERROR_MESSAGE": "ارتباط با سرور برقرار نشد، لطفا بعدا امتحان کنید"
|
||||||
},
|
},
|
||||||
"SUBMIT": "ثبت",
|
"SUBMIT": "ثبت",
|
||||||
"HAVE_AN_ACCOUNT": "از قبل حسابکاربری دارید؟"
|
"HAVE_AN_ACCOUNT": "از قبل حساب کاربری دارید؟"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Vastaanotettu sähköpostitse",
|
"RECEIVED_VIA_EMAIL": "Vastaanotettu sähköpostitse",
|
||||||
"VIEW_TWEET_IN_TWITTER": "Näytä twiitti Twitterissä",
|
"VIEW_TWEET_IN_TWITTER": "Näytä twiitti Twitterissä",
|
||||||
"REPLY_TO_TWEET": "Vastaa tähän twiittiin"
|
"REPLY_TO_TWEET": "Vastaa tähän twiittiin",
|
||||||
|
"NO_MESSAGES": "No Messages"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"REMOVE_SELECTION": "Poista valinnat",
|
"REMOVE_SELECTION": "Poista valinnat",
|
||||||
"DOWNLOAD": "Lataa",
|
"DOWNLOAD": "Lataa",
|
||||||
"UPLOADING_ATTACHMENTS": "Ladataan liitteitä...",
|
"UPLOADING_ATTACHMENTS": "Ladataan liitteitä...",
|
||||||
|
"NO_RESPONSE": "No response",
|
||||||
"HEADER": {
|
"HEADER": {
|
||||||
"RESOLVE_ACTION": "Ratkaise",
|
"RESOLVE_ACTION": "Ratkaise",
|
||||||
"REOPEN_ACTION": "Uudelleenavaa",
|
"REOPEN_ACTION": "Uudelleenavaa",
|
||||||
@ -44,7 +45,8 @@
|
|||||||
"TWEET": "Twiittaa",
|
"TWEET": "Twiittaa",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
"TIP_EMOJI_ICON": "Show emoji selector",
|
"TIP_EMOJI_ICON": "Show emoji selector",
|
||||||
"TIP_ATTACH_ICON": "Attach files"
|
"TIP_ATTACH_ICON": "Attach files",
|
||||||
|
"ENTER_TO_SEND": "Enter to send"
|
||||||
},
|
},
|
||||||
"VISIBLE_TO_AGENTS": "Yksityinen huomautus: Näkyy vain sinulle ja tiimillesi",
|
"VISIBLE_TO_AGENTS": "Yksityinen huomautus: Näkyy vain sinulle ja tiimillesi",
|
||||||
"CHANGE_STATUS": "Keskustelun tila muutettu",
|
"CHANGE_STATUS": "Keskustelun tila muutettu",
|
||||||
|
|||||||
@ -51,5 +51,25 @@
|
|||||||
"ENTER_TO_REMOVE": "Paina enter poistaaksesi",
|
"ENTER_TO_REMOVE": "Paina enter poistaaksesi",
|
||||||
"SELECT_ONE": "Valitse yksi"
|
"SELECT_ONE": "Valitse yksi"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NOTIFICATIONS_PAGE": {
|
||||||
|
"HEADER": "Ilmoitukset",
|
||||||
|
"MARK_ALL_DONE": "Mark All Done",
|
||||||
|
"LIST": {
|
||||||
|
"LOADING_MESSAGE": "Loading notifications...",
|
||||||
|
"404": "No Notifications",
|
||||||
|
"TABLE_HEADER": [
|
||||||
|
"Nimi",
|
||||||
|
"Puhelinnumero",
|
||||||
|
"Keskustelut",
|
||||||
|
"Viimeksi otettu yhteyttä"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TYPE_LABEL": {
|
||||||
|
"conversation_creation": "New conversation",
|
||||||
|
"conversation_assignment": "Conversation Assigned",
|
||||||
|
"assigned_conversation_new_message": "New Message",
|
||||||
|
"conversation_mention": "Mention"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"NOTE": "Päivitä sähköposti-ilmoitusasetukset täällä",
|
"NOTE": "Päivitä sähköposti-ilmoitusasetukset täällä",
|
||||||
"CONVERSATION_ASSIGNMENT": "Lähetä sähköposti-ilmoitus, kun keskustelu on määritetty minulle",
|
"CONVERSATION_ASSIGNMENT": "Lähetä sähköposti-ilmoitus, kun keskustelu on määritetty minulle",
|
||||||
"CONVERSATION_CREATION": "Lähetä sähköposti-ilmoitus, kun keskustelu on määritetty minulle",
|
"CONVERSATION_CREATION": "Lähetä sähköposti-ilmoitus, kun keskustelu on määritetty minulle",
|
||||||
|
"CONVERSATION_MENTION": "Send email notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Lähetä sähköposti-ilmoitus, kun uusi viesti saapuu minulle osoitettuun keskusteluun"
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Lähetä sähköposti-ilmoitus, kun uusi viesti saapuu minulle osoitettuun keskusteluun"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"NOTE": "Päivitä push-ilmoitusasetukset täällä",
|
"NOTE": "Päivitä push-ilmoitusasetukset täällä",
|
||||||
"CONVERSATION_ASSIGNMENT": "Lähetä push-ilmoitus, kun keskustelu on määritetty minulle",
|
"CONVERSATION_ASSIGNMENT": "Lähetä push-ilmoitus, kun keskustelu on määritetty minulle",
|
||||||
"CONVERSATION_CREATION": "Lähetä push-ilmoitus, kun keskustelu on määritetty minulle",
|
"CONVERSATION_CREATION": "Lähetä push-ilmoitus, kun keskustelu on määritetty minulle",
|
||||||
|
"CONVERSATION_MENTION": "Send push notifications when you are mentioned in a conversation",
|
||||||
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Lähetä push-ilmoitus, kun uusi viesti saapuu minulle osoitettuun keskusteluun",
|
"ASSIGNED_CONVERSATION_NEW_MESSAGE": "Lähetä push-ilmoitus, kun uusi viesti saapuu minulle osoitettuun keskusteluun",
|
||||||
"HAS_ENABLED_PUSH": "Tämän selaimen push-ilmoitukset on otettu käyttöön.",
|
"HAS_ENABLED_PUSH": "Tämän selaimen push-ilmoitukset on otettu käyttöön.",
|
||||||
"REQUEST_PUSH": "Ota push-ilmoitukset käyttöön"
|
"REQUEST_PUSH": "Ota push-ilmoitukset käyttöön"
|
||||||
@ -118,10 +120,12 @@
|
|||||||
"HOME": "Koti",
|
"HOME": "Koti",
|
||||||
"AGENTS": "Edustajat",
|
"AGENTS": "Edustajat",
|
||||||
"INBOXES": "Kansiot",
|
"INBOXES": "Kansiot",
|
||||||
|
"NOTIFICATIONS": "Ilmoitukset",
|
||||||
"CANNED_RESPONSES": "Tallennetut vastaukset",
|
"CANNED_RESPONSES": "Tallennetut vastaukset",
|
||||||
"INTEGRATIONS": "Integraatiot",
|
"INTEGRATIONS": "Integraatiot",
|
||||||
"ACCOUNT_SETTINGS": "Tilin asetukset",
|
"ACCOUNT_SETTINGS": "Tilin asetukset",
|
||||||
"LABELS": "Tunnisteet"
|
"LABELS": "Tunnisteet",
|
||||||
|
"TEAMS": "Teams"
|
||||||
},
|
},
|
||||||
"CREATE_ACCOUNT": {
|
"CREATE_ACCOUNT": {
|
||||||
"NEW_ACCOUNT": "Uusi tili",
|
"NEW_ACCOUNT": "Uusi tili",
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
"RECEIVED_VIA_EMAIL": "Reçu par courriel",
|
"RECEIVED_VIA_EMAIL": "Reçu par courriel",
|
||||||
"VIEW_TWEET_IN_TWITTER": "Voir le tweet sur Twitter",
|
"VIEW_TWEET_IN_TWITTER": "Voir le tweet sur Twitter",
|
||||||
"REPLY_TO_TWEET": "Répondre à ce tweet"
|
"REPLY_TO_TWEET": "Répondre à ce tweet",
|
||||||
|
"NO_MESSAGES": "No Messages"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user