Merge branch 'develop' into chore/chat-list-design

This commit is contained in:
Muhsin Keloth 2022-02-08 14:39:39 +05:30 committed by GitHub
commit 6012b90c78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
726 changed files with 19046 additions and 2773 deletions

View file

@ -22,6 +22,9 @@ checks:
enabled: true enabled: true
config: config:
threshold: 300 threshold: 300
method-lines:
config:
threshold: 50
exclude_patterns: exclude_patterns:
- 'spec/' - 'spec/'
- '**/specs/' - '**/specs/'
@ -44,3 +47,5 @@ exclude_patterns:
- 'app/javascript/shared/constants/countries.js' - 'app/javascript/shared/constants/countries.js'
- 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js' - 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js'
- 'app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js' - 'app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js'
- 'app/javascript/dashboard/routes/dashboard/settings/automation/constants.js'
- 'app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js'

View file

@ -102,7 +102,7 @@ gem 'sentry-ruby'
gem 'sentry-sidekiq' gem 'sentry-sidekiq'
##-- background job processing --## ##-- background job processing --##
gem 'sidekiq' gem 'sidekiq', '~> 6.4.0'
# We want cron jobs # We want cron jobs
gem 'sidekiq-cron' gem 'sidekiq-cron'

View file

@ -447,7 +447,7 @@ GEM
rb-fsevent (0.11.0) rb-fsevent (0.11.0)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
redis (4.4.0) redis (4.5.1)
redis-namespace (1.8.1) redis-namespace (1.8.1)
redis (>= 3.0.4) redis (>= 3.0.4)
regexp_parser (2.1.1) regexp_parser (2.1.1)
@ -546,7 +546,7 @@ GEM
sexp_processor (4.15.3) sexp_processor (4.15.3)
shoulda-matchers (5.0.0) shoulda-matchers (5.0.0)
activesupport (>= 5.2.0) activesupport (>= 5.2.0)
sidekiq (6.2.2) sidekiq (6.4.0)
connection_pool (>= 2.2.2) connection_pool (>= 2.2.2)
rack (~> 2.0) rack (~> 2.0)
redis (>= 4.2.0) redis (>= 4.2.0)
@ -726,7 +726,7 @@ DEPENDENCIES
sentry-ruby sentry-ruby
sentry-sidekiq sentry-sidekiq
shoulda-matchers shoulda-matchers
sidekiq sidekiq (~> 6.4.0)
sidekiq-cron sidekiq-cron
simplecov (= 0.17.1) simplecov (= 0.17.1)
slack-ruby-client slack-ruby-client

View file

@ -2,7 +2,7 @@
class AccountBuilder class AccountBuilder
include CustomExceptions::Account include CustomExceptions::Account
pattr_initialize [:account_name!, :email!, :confirmed, :user, :user_full_name, :user_password] pattr_initialize [:account_name!, :email!, :confirmed, :user, :user_full_name, :user_password, :super_admin]
def perform def perform
if @user.nil? if @user.nil?
@ -65,6 +65,7 @@ class AccountBuilder
password: user_password, password: user_password,
password_confirmation: user_password, password_confirmation: user_password,
name: @user_full_name) name: @user_full_name)
@user.type = 'SuperAdmin' if @super_admin
@user.confirm if @confirmed @user.confirm if @confirmed
@user.save! @user.save!
end end

View file

@ -4,7 +4,7 @@ class ContactInboxBuilder
def perform def perform
@contact = Contact.find(contact_id) @contact = Contact.find(contact_id)
@inbox = @contact.account.inboxes.find(inbox_id) @inbox = @contact.account.inboxes.find(inbox_id)
return unless ['Channel::TwilioSms', 'Channel::Email', 'Channel::Api', 'Channel::Whatsapp'].include? @inbox.channel_type return unless ['Channel::TwilioSms', 'Channel::Sms', 'Channel::Email', 'Channel::Api', 'Channel::Whatsapp'].include? @inbox.channel_type
source_id = @source_id || generate_source_id source_id = @source_id || generate_source_id
create_contact_inbox(source_id) if source_id.present? create_contact_inbox(source_id) if source_id.present?
@ -13,12 +13,18 @@ class ContactInboxBuilder
private private
def generate_source_id def generate_source_id
return twilio_source_id if @inbox.channel_type == 'Channel::TwilioSms' case @inbox.channel_type
return wa_source_id if @inbox.channel_type == 'Channel::Whatsapp' when 'Channel::TwilioSms'
return @contact.email if @inbox.channel_type == 'Channel::Email' twilio_source_id
return SecureRandom.uuid if @inbox.channel_type == 'Channel::Api' when 'Channel::Whatsapp'
wa_source_id
nil when 'Channel::Email'
@contact.email
when 'Channel::Sms'
@contact.phone_number
when 'Channel::Api'
SecureRandom.uuid
end
end end
def wa_source_id def wa_source_id

View file

@ -31,7 +31,6 @@ class Messages::MessageBuilder
@attachments.each do |uploaded_attachment| @attachments.each do |uploaded_attachment|
@message.attachments.build( @message.attachments.build(
account_id: @message.account_id, account_id: @message.account_id,
file_type: file_type(uploaded_attachment&.content_type),
file: uploaded_attachment file: uploaded_attachment
) )
end end

View file

@ -1,5 +1,6 @@
class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseController class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseController
before_action :check_authorization before_action :check_authorization
before_action :fetch_automation_rule, only: [:show, :update, :destroy, :clone]
def index def index
@automation_rules = Current.account.automation_rules.active @automation_rules = Current.account.automation_rules.active
@ -9,13 +10,35 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
@automation_rule = Current.account.automation_rules.create(automation_rules_permit) @automation_rule = Current.account.automation_rules.create(automation_rules_permit)
end end
def show; end
def update
@automation_rule.update(automation_rules_permit)
end
def destroy
@automation_rule.destroy!
head :ok
end
def clone
automation_rule = Current.account.automation_rules.find_by(id: params[:automation_rule_id])
new_rule = automation_rule.dup
new_rule.save
@automation_rule = new_rule
end
private private
def automation_rules_permit def automation_rules_permit
params.permit( params.permit(
:name, :description, :event_name, :account_id, :name, :description, :event_name, :account_id,
conditions: [:attribute_key, :filter_operator, :query_operator, { values: [] }], conditions: [:attribute_key, :filter_operator, :query_operator, { values: [] }],
actions: [:action_name, { action_params: [:intiated_at] }] actions: [:action_name, { action_params: [] }]
) )
end end
def fetch_automation_rule
@automation_rule = Current.account.automation_rules.find_by(id: params[:id])
end
end end

View file

@ -18,7 +18,7 @@ class Api::V1::Accounts::CampaignsController < Api::V1::Accounts::BaseController
def show; end def show; end
def update def update
@campaign.update(campaign_params) @campaign.update!(campaign_params)
end end
private private

View file

@ -91,20 +91,9 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
end end
def create_channel def create_channel
case permitted_params[:channel][:type] return unless %w[web_widget api email line telegram whatsapp sms].include?(permitted_params[:channel][:type])
when 'web_widget'
Current.account.web_widgets.create!(permitted_params(Channel::WebWidget::EDITABLE_ATTRS)[:channel].except(:type)) account_channels_method.create!(permitted_params(channel_type_from_params::EDITABLE_ATTRS)[:channel].except(:type))
when 'api'
Current.account.api_channels.create!(permitted_params(Channel::Api::EDITABLE_ATTRS)[:channel].except(:type))
when 'email'
Current.account.email_channels.create!(permitted_params(Channel::Email::EDITABLE_ATTRS)[:channel].except(:type))
when 'line'
Current.account.line_channels.create!(permitted_params(Channel::Line::EDITABLE_ATTRS)[:channel].except(:type))
when 'telegram'
Current.account.telegram_channels.create!(permitted_params(Channel::Telegram::EDITABLE_ATTRS)[:channel].except(:type))
when 'whatsapp'
Current.account.whatsapp_channels.create!(permitted_params(Channel::Whatsapp::EDITABLE_ATTRS)[:channel].except(:type))
end
end end
def update_channel_feature_flags def update_channel_feature_flags
@ -123,6 +112,30 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
) )
end end
def channel_type_from_params
{
'web_widget' => Channel::WebWidget,
'api' => Channel::Api,
'email' => Channel::Email,
'line' => Channel::Line,
'telegram' => Channel::Telegram,
'whatsapp' => Channel::Whatsapp,
'sms' => Channel::Sms
}[permitted_params[:channel][:type]]
end
def account_channels_method
{
'web_widget' => Current.account.web_widgets,
'api' => Current.account.api_channels,
'email' => Current.account.email_channels,
'line' => Current.account.line_channels,
'telegram' => Current.account.telegram_channels,
'whatsapp' => Current.account.whatsapp_channels,
'sms' => Current.account.sms_channels
}[permitted_params[:channel][:type]]
end
def get_channel_attributes(channel_type) def get_channel_attributes(channel_type)
if channel_type.constantize.const_defined?('EDITABLE_ATTRS') if channel_type.constantize.const_defined?('EDITABLE_ATTRS')
channel_type.constantize::EDITABLE_ATTRS.presence channel_type.constantize::EDITABLE_ATTRS.presence

View file

@ -38,6 +38,8 @@ class Api::V1::ProfilesController < Api::BaseController
:name, :name,
:display_name, :display_name,
:avatar, :avatar,
:message_signature,
:message_signature_enabled,
ui_settings: {} ui_settings: {}
) )
end end

View file

@ -31,7 +31,6 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
params[:message][:attachments].each do |uploaded_attachment| params[:message][:attachments].each do |uploaded_attachment|
@message.attachments.new( @message.attachments.new(
account_id: @message.account_id, account_id: @message.account_id,
file_type: helpers.file_type(uploaded_attachment&.content_type),
file: uploaded_attachment file: uploaded_attachment
) )
end end

View file

@ -10,6 +10,7 @@ class Installation::OnboardingController < ApplicationController
user_full_name: onboarding_params.dig(:user, :name), user_full_name: onboarding_params.dig(:user, :name),
email: onboarding_params.dig(:user, :email), email: onboarding_params.dig(:user, :email),
user_password: params.dig(:user, :password), user_password: params.dig(:user, :password),
super_admin: true,
confirmed: true confirmed: true
).perform ).perform
rescue StandardError => e rescue StandardError => e

View file

@ -13,7 +13,8 @@ class Platform::Api::V1::UsersController < PlatformController
end end
def login def login
render json: { url: "#{ENV['FRONTEND_URL']}/app/login?email=#{@resource.email}&sso_auth_token=#{@resource.generate_sso_auth_token}" } encoded_email = ERB::Util.url_encode(@resource.email)
render json: { url: "#{ENV['FRONTEND_URL']}/app/login?email=#{encoded_email}&sso_auth_token=#{@resource.generate_sso_auth_token}" }
end end
def show; end def show; end

View file

@ -8,7 +8,7 @@ class SuperAdmin::Devise::SessionsController < Devise::SessionsController
def create def create
redirect_to(super_admin_session_path, flash: { error: @error_message }) && return unless valid_credentials? redirect_to(super_admin_session_path, flash: { error: @error_message }) && return unless valid_credentials?
sign_in(@super_admin, scope: :super_admin) sign_in(:super_admin, @super_admin)
flash.discard flash.discard
redirect_to super_admin_users_path redirect_to super_admin_users_path
end end

View file

@ -1,44 +0,0 @@
class SuperAdmin::SuperAdminsController < SuperAdmin::ApplicationController
# Overwrite any of the RESTful controller actions to implement custom behavior
# For example, you may want to send an email after a foo is updated.
#
# def update
# super
# send_foo_updated_email(requested_resource)
# end
# Override this method to specify custom lookup behavior.
# This will be used to set the resource for the `show`, `edit`, and `update`
# actions.
#
# def find_resource(param)
# Foo.find_by!(slug: param)
# end
# The result of this lookup will be available as `requested_resource`
# Override this if you have certain roles that require a subset
# this will be used to set the records shown on the `index` action.
#
# def scoped_resource
# if current_user.super_admin?
# resource_class
# else
# resource_class.with_less_stuff
# end
# end
# Override `resource_params` if you want to transform the submitted
# data before it's persisted. For example, the following would turn all
# empty values into nil values. It uses other APIs such as `resource_class`
# and `dashboard`:
#
# def resource_params
# params.require(resource_class.model_name.param_key).
# permit(dashboard.permitted_attributes).
# transform_values { |value| value == "" ? nil : value }
# end
# See https://administrate-prototype.herokuapp.com/customizing_controller_actions
# for more information
end

View file

@ -33,12 +33,15 @@ class SuperAdmin::UsersController < SuperAdmin::ApplicationController
# empty values into nil values. It uses other APIs such as `resource_class` # empty values into nil values. It uses other APIs such as `resource_class`
# and `dashboard`: # and `dashboard`:
# #
# def resource_params def resource_params
# params.require(resource_class.model_name.param_key). permitted_params = super
# permit(dashboard.permitted_attributes). permitted_params.delete(:password) if permitted_params[:password].blank?
# transform_values { |value| value == "" ? nil : value } permitted_params
# end end
# See https://administrate-prototype.herokuapp.com/customizing_controller_actions # See https://administrate-prototype.herokuapp.com/customizing_controller_actions
# for more information # for more information
def find_resource(param)
super.becomes(User)
end
end end

View file

@ -0,0 +1,6 @@
class Webhooks::SmsController < ActionController::API
def process_payload
Webhooks::SmsEventsJob.perform_later(params['_json']&.first&.to_unsafe_hash)
head :ok
end
end

View file

@ -1,75 +0,0 @@
require 'administrate/base_dashboard'
class SuperAdminDashboard < Administrate::BaseDashboard
# ATTRIBUTE_TYPES
# a hash that describes the type of each of the model's fields.
#
# Each different type represents an Administrate::Field object,
# which determines how the attribute is displayed
# on pages throughout the dashboard.
ATTRIBUTE_TYPES = {
id: Field::Number,
email: Field::String,
password: Field::Password,
remember_created_at: Field::DateTime,
sign_in_count: Field::Number,
current_sign_in_at: Field::DateTime,
last_sign_in_at: Field::DateTime,
current_sign_in_ip: Field::String.with_options(searchable: false),
last_sign_in_ip: Field::String.with_options(searchable: false),
created_at: Field::DateTime,
updated_at: Field::DateTime
}.freeze
# COLLECTION_ATTRIBUTES
# an array of attributes that will be displayed on the model's index page.
#
# By default, it's limited to four items to reduce clutter on index pages.
# Feel free to add, remove, or rearrange items.
COLLECTION_ATTRIBUTES = %i[
id
email
].freeze
# SHOW_PAGE_ATTRIBUTES
# an array of attributes that will be displayed on the model's show page.
SHOW_PAGE_ATTRIBUTES = %i[
id
email
remember_created_at
sign_in_count
current_sign_in_at
last_sign_in_at
current_sign_in_ip
last_sign_in_ip
created_at
updated_at
].freeze
# FORM_ATTRIBUTES
# an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages.
FORM_ATTRIBUTES = %i[
email
password
].freeze
# COLLECTION_FILTERS
# a hash that defines filters that can be used while searching via the search
# field of the dashboard.
#
# For example to add an option to search for open resources by typing "open:"
# in the search field:
#
# COLLECTION_FILTERS = {
# open: ->(resources) { resources.where(open: true) }
# }.freeze
COLLECTION_FILTERS = {}.freeze
# Overwrite this method to customize how super admins are displayed
# across all pages of the admin dashboard.
#
# def display_resource(super_admin)
# "SuperAdmin ##{super_admin.id}"
# end
end

View file

@ -30,6 +30,7 @@ class UserDashboard < Administrate::BaseDashboard
created_at: Field::DateTime, created_at: Field::DateTime,
updated_at: Field::DateTime, updated_at: Field::DateTime,
pubsub_token: Field::String, pubsub_token: Field::String,
type: Field::Select.with_options(collection: [nil, 'SuperAdmin']),
accounts: CountField accounts: CountField
}.freeze }.freeze
@ -44,6 +45,7 @@ class UserDashboard < Administrate::BaseDashboard
name name
email email
accounts accounts
type
].freeze ].freeze
# SHOW_PAGE_ATTRIBUTES # SHOW_PAGE_ATTRIBUTES
@ -53,10 +55,12 @@ class UserDashboard < Administrate::BaseDashboard
avatar_url avatar_url
unconfirmed_email unconfirmed_email
name name
type
display_name display_name
email email
created_at created_at
updated_at updated_at
confirmed_at
account_users account_users
].freeze ].freeze
@ -68,6 +72,8 @@ class UserDashboard < Administrate::BaseDashboard
display_name display_name
email email
password password
confirmed_at
type
].freeze ].freeze
# COLLECTION_FILTERS # COLLECTION_FILTERS

View file

@ -55,7 +55,7 @@ class ConversationFinder
def set_inboxes def set_inboxes
@inbox_ids = if params[:inbox_id] @inbox_ids = if params[:inbox_id]
current_account.inboxes.where(id: params[:inbox_id]) @current_user.assigned_inboxes.where(id: params[:inbox_id])
else else
@current_user.assigned_inboxes.pluck(:id) @current_user.assigned_inboxes.pluck(:id)
end end

View file

@ -0,0 +1,14 @@
/* global axios */
import ApiClient from './ApiClient';
class AutomationsAPI extends ApiClient {
constructor() {
super('automation_rules', { accountScoped: true });
}
clone(automationId) {
return axios.post(`${this.url}/${automationId}/clone`);
}
}
export default new AutomationsAPI();

View file

@ -6,8 +6,12 @@ class CustomViewsAPI extends ApiClient {
super('custom_filters', { accountScoped: true }); super('custom_filters', { accountScoped: true });
} }
getCustomViews() { getCustomViewsByFilterType(type) {
return axios.get(this.url); return axios.get(`${this.url}?filter_type=${type}`);
}
deleteCustomViews(id, type) {
return axios.delete(`${this.url}/${id}?filter_type=${type}`);
} }
} }

View file

@ -7,17 +7,19 @@ export const buildCreatePayload = ({
isPrivate, isPrivate,
contentAttributes, contentAttributes,
echoId, echoId,
file, files,
ccEmails = '', ccEmails = '',
bccEmails = '', bccEmails = '',
}) => { }) => {
let payload; let payload;
if (file) { if (files && files.length !== 0) {
payload = new FormData(); payload = new FormData();
payload.append('attachments[]', file, file.name);
if (message) { if (message) {
payload.append('content', message); payload.append('content', message);
} }
files.forEach(file => {
payload.append('attachments[]', file);
});
payload.append('private', isPrivate); payload.append('private', isPrivate);
payload.append('echo_id', echoId); payload.append('echo_id', echoId);
payload.append('cc_emails', ccEmails); payload.append('cc_emails', ccEmails);
@ -46,7 +48,7 @@ class MessageApi extends ApiClient {
private: isPrivate, private: isPrivate,
contentAttributes, contentAttributes,
echo_id: echoId, echo_id: echoId,
file, files,
ccEmails = '', ccEmails = '',
bccEmails = '', bccEmails = '',
}) { }) {
@ -58,7 +60,7 @@ class MessageApi extends ApiClient {
isPrivate, isPrivate,
contentAttributes, contentAttributes,
echoId, echoId,
file, files,
ccEmails, ccEmails,
bccEmails, bccEmails,
}), }),

View file

@ -0,0 +1,15 @@
import automations from '../automation';
import ApiClient from '../ApiClient';
describe('#AutomationsAPI', () => {
it('creates correct instance', () => {
expect(automations).toBeInstanceOf(ApiClient);
expect(automations).toHaveProperty('get');
expect(automations).toHaveProperty('show');
expect(automations).toHaveProperty('create');
expect(automations).toHaveProperty('update');
expect(automations).toHaveProperty('delete');
expect(automations).toHaveProperty('clone');
expect(automations.url).toBe('/api/v1/automation_rules');
});
});

View file

@ -36,7 +36,7 @@ describe('#ConversationAPI', () => {
echoId: 12, echoId: 12,
isPrivate: true, isPrivate: true,
file: new Blob(['test-content'], { type: 'application/pdf' }), files: [new Blob(['test-content'], { type: 'application/pdf' })],
}); });
expect(formPayload).toBeInstanceOf(FormData); expect(formPayload).toBeInstanceOf(FormData);
expect(formPayload.get('content')).toEqual('test content'); expect(formPayload.get('content')).toEqual('test content');

View file

@ -223,8 +223,14 @@
@include flex-align(right, null); @include flex-align(right, null);
.wrap { .wrap {
align-items: flex-end;
display: flex;
margin-right: $space-normal; margin-right: $space-normal;
text-align: right; text-align: right;
.sender--info {
padding: var(--space-small) 0 var(--space-smaller) var(--space-small);
}
} }
.bubble { .bubble {

View file

@ -3,7 +3,7 @@
<slot></slot> <slot></slot>
<div <div
class="chat-list__top" class="chat-list__top"
:class="{ filter__applied: hasAppliedFiltersOrActiveCustomViews }" :class="{ filter__applied: hasAppliedFiltersOrActiveFolders }"
> >
<h1 class="page-sub-title text-truncate" :title="pageTitle"> <h1 class="page-sub-title text-truncate" :title="pageTitle">
{{ pageTitle }} {{ pageTitle }}
@ -11,17 +11,17 @@
<div class="filter--actions"> <div class="filter--actions">
<chat-filter <chat-filter
v-if="!hasAppliedFiltersOrActiveCustomViews" v-if="!hasAppliedFiltersOrActiveFolders"
@statusFilterChange="updateStatusType" @statusFilterChange="updateStatusType"
/> />
<div v-if="hasAppliedFilters && !hasActiveCustomViews"> <div v-if="hasAppliedFilters && !hasActiveFolders">
<woot-button <woot-button
v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.ADD.SAVE_BUTTON')" v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.ADD.SAVE_BUTTON')"
size="tiny" size="tiny"
variant="smooth" variant="smooth"
color-scheme="secondary" color-scheme="secondary"
icon="save" icon="save"
@click="onClickOpenAddCustomViewsModal" @click="onClickOpenAddFoldersModal"
/> />
<woot-button <woot-button
v-tooltip.top-end="$t('FILTER.CLEAR_BUTTON_LABEL')" v-tooltip.top-end="$t('FILTER.CLEAR_BUTTON_LABEL')"
@ -32,7 +32,7 @@
@click="resetAndFetchData" @click="resetAndFetchData"
/> />
</div> </div>
<div v-if="hasActiveCustomViews"> <div v-if="hasActiveFolders">
<woot-button <woot-button
v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.DELETE.DELETE_BUTTON')" v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.DELETE.DELETE_BUTTON')"
size="tiny" size="tiny"
@ -40,7 +40,7 @@
color-scheme="alert" color-scheme="alert"
icon="delete" icon="delete"
class="delete-custom-view__button" class="delete-custom-view__button"
@click="onClickOpenDeleteCustomViewsModal" @click="onClickOpenDeleteFoldersModal"
/> />
</div> </div>
@ -59,21 +59,23 @@
</div> </div>
<add-custom-views <add-custom-views
v-if="showAddCustomViewsModal" v-if="showAddFoldersModal"
:custom-views-query="customViewsQuery" :custom-views-query="foldersQuery"
@close="onCloseAddCustomViewsModal" :open-last-saved-item="openLastSavedItemInFolder"
@close="onCloseAddFoldersModal"
/> />
<delete-custom-views <delete-custom-views
v-if="showDeleteCustomViewsModal" v-if="showDeleteFoldersModal"
:show-delete-popup.sync="showDeleteCustomViewsModal" :show-delete-popup.sync="showDeleteFoldersModal"
:active-custom-view="activeCustomView" :active-custom-view="activeFolder"
:custom-views-id="customViewsId" :custom-views-id="foldersId"
@close="onCloseDeleteCustomViewsModal" :open-last-item-after-delete="openLastItemAfterDeleteInFolder"
@close="onCloseDeleteFoldersModal"
/> />
<chat-type-tabs <chat-type-tabs
v-if="!hasAppliedFiltersOrActiveCustomViews" v-if="!hasAppliedFiltersOrActiveFolders"
:items="assigneeTabItems" :items="assigneeTabItems"
:active-tab="activeAssigneeTab" :active-tab="activeAssigneeTab"
@chatTabChange="updateAssigneeTab" @chatTabChange="updateAssigneeTab"
@ -89,7 +91,7 @@
:key="chat.id" :key="chat.id"
:active-label="label" :active-label="label"
:team-id="teamId" :team-id="teamId"
:custom-views-id="customViewsId" :folders-id="foldersId"
:chat="chat" :chat="chat"
:conversation-type="conversationType" :conversation-type="conversationType"
:show-assignee="showAssigneeInConversationCard" :show-assignee="showAssigneeInConversationCard"
@ -126,7 +128,7 @@
> >
<conversation-advanced-filter <conversation-advanced-filter
v-if="showAdvancedFilters" v-if="showAdvancedFilters"
:filter-types="advancedFilterTypes" :initial-filter-types="advancedFilterTypes"
:on-close="onToggleAdvanceFiltersModal" :on-close="onToggleAdvanceFiltersModal"
@applyFilter="onApplyFilter" @applyFilter="onApplyFilter"
/> />
@ -182,7 +184,7 @@ export default {
type: String, type: String,
default: '', default: '',
}, },
customViewsId: { foldersId: {
type: [String, Number], type: [String, Number],
default: 0, default: 0,
}, },
@ -196,9 +198,9 @@ export default {
...filter, ...filter,
attributeName: this.$t(`FILTER.ATTRIBUTES.${filter.attributeI18nKey}`), attributeName: this.$t(`FILTER.ATTRIBUTES.${filter.attributeI18nKey}`),
})), })),
customViewsQuery: {}, foldersQuery: {},
showAddCustomViewsModal: false, showAddFoldersModal: false,
showDeleteCustomViewsModal: false, showDeleteFoldersModal: false,
}; };
}, },
computed: { computed: {
@ -213,20 +215,20 @@ export default {
activeInbox: 'getSelectedInbox', activeInbox: 'getSelectedInbox',
conversationStats: 'conversationStats/getStats', conversationStats: 'conversationStats/getStats',
appliedFilters: 'getAppliedConversationFilters', appliedFilters: 'getAppliedConversationFilters',
customViews: 'customViews/getCustomViews', folders: 'customViews/getCustomViews',
}), }),
hasAppliedFilters() { hasAppliedFilters() {
return this.appliedFilters.length; return this.appliedFilters.length;
}, },
hasActiveCustomViews() { hasActiveFolders() {
return this.activeCustomView && this.customViewsId !== 0; return this.activeFolder && this.foldersId !== 0;
}, },
hasAppliedFiltersOrActiveCustomViews() { hasAppliedFiltersOrActiveFolders() {
return this.hasAppliedFilters || this.hasActiveCustomViews; return this.hasAppliedFilters || this.hasActiveFolders;
}, },
savedCustomViewsValue() { savedFoldersValue() {
if (this.hasActiveCustomViews) { if (this.hasActiveFolders) {
const payload = this.activeCustomView.query; const payload = this.activeFolder.query;
this.fetchSavedFilteredConversations(payload); this.fetchSavedFilteredConversations(payload);
} }
return {}; return {};
@ -242,7 +244,10 @@ export default {
}); });
}, },
showAssigneeInConversationCard() { showAssigneeInConversationCard() {
return this.activeAssigneeTab === wootConstants.ASSIGNEE_TYPE.ALL; return (
this.hasAppliedFiltersOrActiveFolders ||
this.activeAssigneeTab === wootConstants.ASSIGNEE_TYPE.ALL
);
}, },
inbox() { inbox() {
return this.$store.getters['inboxes/getInbox'](this.activeInbox); return this.$store.getters['inboxes/getInbox'](this.activeInbox);
@ -253,7 +258,7 @@ export default {
); );
}, },
currentPageFilterKey() { currentPageFilterKey() {
return this.hasAppliedFiltersOrActiveCustomViews return this.hasAppliedFiltersOrActiveFolders
? 'appliedFilters' ? 'appliedFilters'
: this.activeAssigneeTab; : this.activeAssigneeTab;
}, },
@ -278,9 +283,7 @@ export default {
conversationType: this.conversationType conversationType: this.conversationType
? this.conversationType ? this.conversationType
: undefined, : undefined,
customViews: this.hasActiveCustomViews folders: this.hasActiveFolders ? this.savedFoldersValue : undefined,
? this.savedCustomViewsValue
: undefined,
}; };
}, },
pageTitle() { pageTitle() {
@ -296,14 +299,14 @@ export default {
if (this.conversationType === 'mention') { if (this.conversationType === 'mention') {
return this.$t('CHAT_LIST.MENTION_HEADING'); return this.$t('CHAT_LIST.MENTION_HEADING');
} }
if (this.hasActiveCustomViews) { if (this.hasActiveFolders) {
return this.activeCustomView.name; return this.activeFolder.name;
} }
return this.$t('CHAT_LIST.TAB_HEADING'); return this.$t('CHAT_LIST.TAB_HEADING');
}, },
conversationList() { conversationList() {
let conversationList = []; let conversationList = [];
if (!this.hasAppliedFiltersOrActiveCustomViews) { if (!this.hasAppliedFiltersOrActiveFolders) {
const filters = this.conversationFilters; const filters = this.conversationFilters;
if (this.activeAssigneeTab === 'me') { if (this.activeAssigneeTab === 'me') {
conversationList = [...this.mineChatsList(filters)]; conversationList = [...this.mineChatsList(filters)];
@ -318,10 +321,10 @@ export default {
return conversationList; return conversationList;
}, },
activeCustomView() { activeFolder() {
if (this.customViewsId) { if (this.foldersId) {
const activeView = this.customViews.filter( const activeView = this.folders.filter(
view => view.id === Number(this.customViewsId) view => view.id === Number(this.foldersId)
); );
const [firstValue] = activeView; const [firstValue] = activeView;
return firstValue; return firstValue;
@ -348,8 +351,10 @@ export default {
conversationType() { conversationType() {
this.resetAndFetchData(); this.resetAndFetchData();
}, },
activeCustomView() { activeFolder() {
this.resetAndFetchData(); if (!this.hasAppliedFilters) {
this.resetAndFetchData();
}
}, },
}, },
mounted() { mounted() {
@ -365,22 +370,22 @@ export default {
if (this.$route.name !== 'home') { if (this.$route.name !== 'home') {
this.$router.push({ name: 'home' }); this.$router.push({ name: 'home' });
} }
this.customViewsQuery = { payload: payload }; this.foldersQuery = filterQueryGenerator(payload);
this.$store.dispatch('conversationPage/reset'); this.$store.dispatch('conversationPage/reset');
this.$store.dispatch('emptyAllConversations'); this.$store.dispatch('emptyAllConversations');
this.fetchFilteredConversations(payload); this.fetchFilteredConversations(payload);
}, },
onClickOpenAddCustomViewsModal() { onClickOpenAddFoldersModal() {
this.showAddCustomViewsModal = true; this.showAddFoldersModal = true;
}, },
onCloseAddCustomViewsModal() { onCloseAddFoldersModal() {
this.showAddCustomViewsModal = false; this.showAddFoldersModal = false;
}, },
onClickOpenDeleteCustomViewsModal() { onClickOpenDeleteFoldersModal() {
this.showDeleteCustomViewsModal = true; this.showDeleteFoldersModal = true;
}, },
onCloseDeleteCustomViewsModal() { onCloseDeleteFoldersModal() {
this.showDeleteCustomViewsModal = false; this.showDeleteFoldersModal = false;
}, },
onToggleAdvanceFiltersModal() { onToggleAdvanceFiltersModal() {
this.showAdvancedFilters = !this.showAdvancedFilters; this.showAdvancedFilters = !this.showAdvancedFilters;
@ -433,11 +438,11 @@ export default {
this.$store.dispatch('conversationPage/reset'); this.$store.dispatch('conversationPage/reset');
this.$store.dispatch('emptyAllConversations'); this.$store.dispatch('emptyAllConversations');
this.$store.dispatch('clearConversationFilters'); this.$store.dispatch('clearConversationFilters');
if (this.hasActiveCustomViews) { if (this.hasActiveFolders) {
const payload = this.activeCustomView.query; const payload = this.activeFolder.query;
this.fetchSavedFilteredConversations(payload); this.fetchSavedFilteredConversations(payload);
} }
if (this.customViewsId) { if (this.foldersId) {
return; return;
} }
this.fetchConversations(); this.fetchConversations();
@ -448,11 +453,11 @@ export default {
.then(() => this.$emit('conversation-load')); .then(() => this.$emit('conversation-load'));
}, },
loadMoreConversations() { loadMoreConversations() {
if (!this.hasAppliedFiltersOrActiveCustomViews) { if (!this.hasAppliedFiltersOrActiveFolders) {
this.fetchConversations(); this.fetchConversations();
} }
if (this.hasActiveCustomViews) { if (this.hasActiveFolders) {
const payload = this.activeCustomView.query; const payload = this.activeFolder.query;
this.fetchSavedFilteredConversations(payload); this.fetchSavedFilteredConversations(payload);
} else { } else {
this.fetchFilteredConversations(this.appliedFilters); this.fetchFilteredConversations(this.appliedFilters);
@ -492,6 +497,22 @@ export default {
this.resetAndFetchData(); this.resetAndFetchData();
} }
}, },
openLastSavedItemInFolder() {
const lastItemOfFolder = this.folders[this.folders.length - 1];
const lastItemId = lastItemOfFolder.id;
this.$router.push({
name: 'folder_conversations',
params: { id: lastItemId },
});
},
openLastItemAfterDeleteInFolder() {
if (this.folders.length > 0) {
this.openLastSavedItemInFolder();
} else {
this.$router.push({ name: 'home' });
this.fetchConversations();
}
},
}, },
}; };
</script> </script>

View file

@ -63,7 +63,7 @@
rel="noopener noreferrer" rel="noopener noreferrer"
class="value" class="value"
> >
{{ value || '---' }} {{ urlValue }}
</a> </a>
<p v-else class="value"> <p v-else class="value">
{{ displayValue || '---' }} {{ displayValue || '---' }}
@ -119,7 +119,7 @@ import format from 'date-fns/format';
import { required, url } from 'vuelidate/lib/validators'; import { required, url } from 'vuelidate/lib/validators';
import { BUS_EVENTS } from 'shared/constants/busEvents'; import { BUS_EVENTS } from 'shared/constants/busEvents';
import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue'; import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue';
import { isValidURL } from '../helper/URLHelper';
const DATE_FORMAT = 'yyyy-MM-dd'; const DATE_FORMAT = 'yyyy-MM-dd';
export default { export default {
@ -184,6 +184,9 @@ export default {
isAttributeTypeDate() { isAttributeTypeDate() {
return this.attributeType === 'date'; return this.attributeType === 'date';
}, },
urlValue() {
return isValidURL(this.value) ? this.value : '---';
},
notAttributeTypeCheckboxAndList() { notAttributeTypeCheckboxAndList() {
return !this.isAttributeTypeCheckbox && !this.isAttributeTypeList; return !this.isAttributeTypeCheckbox && !this.isAttributeTypeList;
}, },

View file

@ -4,7 +4,12 @@
<div class="ui-notification"> <div class="ui-notification">
<fluent-icon icon="wifi-off" /> <fluent-icon icon="wifi-off" />
<p class="ui-notification-text"> <p class="ui-notification-text">
{{ $t('NETWORK.NOTIFICATION.TEXT') }} {{
useInstallationName(
$t('NETWORK.NOTIFICATION.TEXT'),
globalConfig.installationName
)
}}
</p> </p>
<woot-button variant="clear" size="small" @click="refreshPage"> <woot-button variant="clear" size="small" @click="refreshPage">
{{ $t('NETWORK.BUTTON.REFRESH') }} {{ $t('NETWORK.BUTTON.REFRESH') }}
@ -23,13 +28,22 @@
</template> </template>
<script> <script>
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
import { mapGetters } from 'vuex';
export default { export default {
mixins: [globalConfigMixin],
data() { data() {
return { return {
showNotification: !navigator.onLine, showNotification: !navigator.onLine,
}; };
}, },
computed: {
...mapGetters({ globalConfig: 'globalConfig/get' }),
},
mounted() { mounted() {
window.addEventListener('offline', this.updateOnlineStatus); window.addEventListener('offline', this.updateOnlineStatus);
}, },

View file

@ -88,12 +88,31 @@ export default {
currentUser: 'getCurrentUser', currentUser: 'getCurrentUser',
globalConfig: 'globalConfig/get', globalConfig: 'globalConfig/get',
inboxes: 'inboxes/getInboxes', inboxes: 'inboxes/getInboxes',
customViews: 'customViews/getCustomViews',
accountId: 'getCurrentAccountId', accountId: 'getCurrentAccountId',
currentRole: 'getCurrentRole', currentRole: 'getCurrentRole',
labels: 'labels/getLabelsOnSidebar', labels: 'labels/getLabelsOnSidebar',
teams: 'teams/getMyTeams', teams: 'teams/getMyTeams',
}), }),
activeCustomView() {
if (this.activePrimaryMenu.key === 'contacts') {
return 'contact';
}
if (this.activePrimaryMenu.key === 'conversations') {
return 'conversation';
}
return '';
},
customViews() {
return this.$store.getters['customViews/getCustomViewsByFilterType'](
this.activeCustomView
);
},
isConversationOrContactActive() {
return (
this.activePrimaryMenu.key === 'contacts' ||
this.activePrimaryMenu.key === 'conversations'
);
},
sideMenuConfig() { sideMenuConfig() {
return getSidebarItems(this.accountId); return getSidebarItems(this.accountId);
}, },
@ -121,16 +140,27 @@ export default {
return activePrimaryMenu; return activePrimaryMenu;
}, },
}, },
watch: {
activeCustomView() {
this.fetchCustomViews();
},
},
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('customViews/get');
this.$store.dispatch('notifications/unReadCount'); this.$store.dispatch('notifications/unReadCount');
this.$store.dispatch('teams/get'); this.$store.dispatch('teams/get');
this.$store.dispatch('attributes/get'); this.$store.dispatch('attributes/get');
this.fetchCustomViews();
}, },
methods: { methods: {
fetchCustomViews() {
if (this.isConversationOrContactActive) {
this.$store.dispatch('customViews/get', this.activeCustomView);
}
},
toggleKeyShortcutModal() { toggleKeyShortcutModal() {
this.showShortcutModal = true; this.showShortcutModal = true;
}, },

View file

@ -5,6 +5,7 @@ const contacts = accountId => ({
routes: [ routes: [
'contacts_dashboard', 'contacts_dashboard',
'contact_profile_dashboard', 'contact_profile_dashboard',
'contacts_segments_dashboard',
'contacts_labels_dashboard', 'contacts_labels_dashboard',
], ],
menuItems: [ menuItems: [

View file

@ -14,8 +14,8 @@ const conversations = accountId => ({
'conversations_through_team', 'conversations_through_team',
'conversation_mentions', 'conversation_mentions',
'conversation_through_mentions', 'conversation_through_mentions',
'custom_view_conversations', 'folder_conversations',
'conversations_through_custom_view', 'conversations_through_folders',
], ],
menuItems: [ menuItems: [
{ {

View file

@ -69,13 +69,13 @@ const settings = accountId => ({
), ),
toStateName: 'attributes_list', toStateName: 'attributes_list',
}, },
{ // {
icon: 'autocorrect', // icon: 'automation',
label: 'AUTOMATION', // label: 'AUTOMATION',
hasSubMenu: false, // hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/settings/automation/list`), // toState: frontendURL(`accounts/${accountId}/settings/automation/list`),
toStateName: 'automation_list', // toStateName: 'automation_list',
}, // },
{ {
icon: 'chat-multiple', icon: 'chat-multiple',
label: 'CANNED_RESPONSES', label: 'CANNED_RESPONSES',

View file

@ -57,6 +57,9 @@ export default {
hasSecondaryMenu() { hasSecondaryMenu() {
return this.menuConfig.menuItems && this.menuConfig.menuItems.length; return this.menuConfig.menuItems && this.menuConfig.menuItems.length;
}, },
contactCustomViews() {
return this.customViews.filter(view => view.filter_type === 'contact');
},
accessibleMenuItems() { accessibleMenuItems() {
if (!this.currentRole) { if (!this.currentRole) {
return []; return [];
@ -154,10 +157,10 @@ export default {
})), })),
}; };
}, },
customViewsSection() { foldersSection() {
return { return {
icon: 'folder', icon: 'folder',
label: 'CUSTOM_VIEWS', label: 'CUSTOM_VIEWS_FOLDER',
hasSubMenu: true, hasSubMenu: true,
key: 'custom_view', key: 'custom_view',
children: this.customViews children: this.customViews
@ -172,20 +175,39 @@ export default {
})), })),
}; };
}, },
contactSegmentsSection() {
return {
icon: 'folder',
label: 'CUSTOM_VIEWS_SEGMENTS',
hasSubMenu: true,
key: 'custom_view',
children: this.customViews
.filter(view => view.filter_type === 'contact')
.map(view => ({
id: view.id,
label: view.name,
truncateLabel: true,
toState: frontendURL(
`accounts/${this.accountId}/contacts/custom_view/${view.id}`
),
})),
};
},
additionalSecondaryMenuItems() { additionalSecondaryMenuItems() {
let conversationMenuItems = [this.inboxSection, this.labelSection]; let conversationMenuItems = [this.inboxSection, this.labelSection];
let contactMenuItems = [this.contactLabelSection];
if (this.teams.length) { if (this.teams.length) {
conversationMenuItems = [this.teamSection, ...conversationMenuItems]; conversationMenuItems = [this.teamSection, ...conversationMenuItems];
} }
if (this.customViews.length) { if (this.customViews.length) {
conversationMenuItems = [ conversationMenuItems = [this.foldersSection, ...conversationMenuItems];
this.customViewsSection, }
...conversationMenuItems, if (this.contactCustomViews.length) {
]; contactMenuItems = [this.contactSegmentsSection, ...contactMenuItems];
} }
return { return {
conversations: conversationMenuItems, conversations: conversationMenuItems,
contacts: [this.contactLabelSection], contacts: contactMenuItems,
}; };
}, },
}, },

View file

@ -73,14 +73,48 @@ export default {
hasSubMenu() { hasSubMenu() {
return !!this.menuItem.children; return !!this.menuItem.children;
}, },
isInboxConversation() {
return (
this.$store.state.route.name === 'inbox_conversation' &&
this.menuItem.toStateName === 'home'
);
},
isTeamsSettings() {
return (
this.$store.state.route.name === 'settings_teams_edit' &&
this.menuItem.toStateName === 'settings_teams_list'
);
},
isInboxsSettings() {
return (
this.$store.state.route.name === 'settings_inbox_show' &&
this.menuItem.toStateName === 'settings_inbox_list'
);
},
isIntegrationsSettings() {
return (
this.$store.state.route.name === 'settings_integrations_webhook' &&
this.menuItem.toStateName === 'settings_integrations'
);
},
isApplicationsSettings() {
return (
this.$store.state.route.name === 'settings_applications_integration' &&
this.menuItem.toStateName === 'settings_applications'
);
},
computedClass() { computedClass() {
// If active Inbox is present // If active Inbox is present
// donot highlight conversations // donot highlight conversations
if (this.activeInbox) return ' '; if (this.activeInbox) return ' ';
if ( if (
this.$store.state.route.name === 'inbox_conversation' && this.isInboxConversation ||
this.menuItem.toStateName === 'home' this.isTeamsSettings ||
this.isInboxsSettings ||
this.isIntegrationsSettings ||
this.isApplicationsSettings
) { ) {
return 'is-active'; return 'is-active';
} }

View file

@ -0,0 +1,129 @@
<template>
<div class="banner" :class="bannerClasses">
<span>
{{ bannerMessage }}
<a
v-if="hrefLink"
:href="hrefLink"
rel="noopener noreferrer nofollow"
target="_blank"
>
{{ hrefLinkText }}
</a>
</span>
<woot-button
v-if="hasActionButton"
size="small"
variant="link"
icon="arrow-right"
color-scheme="primary"
class-names="banner-action__button"
@click="onClick"
>
{{ actionButtonLabel }}
</woot-button>
<woot-button
v-if="hasCloseButton"
size="small"
variant="link"
color-scheme="warning"
icon="dismiss-circle"
class-names="banner-action__button"
@click="onClickClose"
>
</woot-button>
</div>
</template>
<script>
export default {
props: {
bannerMessage: {
type: String,
default: '',
},
hrefLink: {
type: String,
default: '',
},
hrefLinkText: {
type: String,
default: '',
},
hasActionButton: {
type: Boolean,
default: false,
},
actionButtonLabel: {
type: String,
default: '',
},
colorScheme: {
type: String,
default: '',
},
hasCloseButton: {
type: Boolean,
default: false,
},
},
computed: {
bannerClasses() {
return [this.colorScheme];
},
},
methods: {
onClick(e) {
this.$emit('click', e);
},
onClickClose(e) {
this.$emit('close', e);
},
},
};
</script>
<style lang="scss" scoped>
.banner {
display: flex;
color: var(--white);
font-size: var(--font-size-mini);
padding: var(--space-slab) var(--space-normal);
justify-content: center;
position: sticky;
&.secondary {
background: var(--s-300);
}
&.alert {
background: var(--r-400);
}
&.warning {
background: var(--y-800);
color: var(--s-600);
a {
color: var(--s-600);
}
}
&.gray {
background: var(--b-500);
}
a {
text-decoration: underline;
color: var(--white);
font-size: var(--font-size-mini);
}
.banner-action__button {
margin: 0 var(--space-smaller);
::v-deep .button__content {
white-space: nowrap;
}
}
}
</style>

View file

@ -30,7 +30,7 @@ export default {
}, },
value: { value: {
type: Date, type: Date,
default: () => [], default: [],
}, },
}, },

View file

@ -7,7 +7,7 @@
> >
<div class="thumb-wrap"> <div class="thumb-wrap">
<img <img
v-if="isTypeImage(attachment.resource.type)" v-if="isTypeImage(attachment.resource.content_type)"
class="image-thumb" class="image-thumb"
:src="attachment.thumb" :src="attachment.thumb"
/> />
@ -15,12 +15,12 @@
</div> </div>
<div class="file-name-wrap"> <div class="file-name-wrap">
<span class="item"> <span class="item">
{{ attachment.resource.name }} {{ attachment.resource.filename }}
</span> </span>
</div> </div>
<div class="file-size-wrap"> <div class="file-size-wrap">
<span class="item"> <span class="item">
{{ formatFileSize(attachment.resource.size) }} {{ formatFileSize(attachment.resource.byte_size) }}
</span> </span>
</div> </div>
<div class="remove-file-wrap"> <div class="remove-file-wrap">

View file

@ -0,0 +1,182 @@
<template>
<div
class="filter"
:class="{ error: v.action_params.$dirty && v.action_params.$error }"
>
<div class="filter-inputs">
<select
v-model="action_name"
class="action__question"
@change="resetFilter()"
>
<option
v-for="attribute in actionTypes"
:key="attribute.key"
:value="attribute.key"
>
{{ attribute.label }}
</option>
</select>
<div class="filter__answer--wrap">
<div class="multiselect-wrap--small">
<multiselect
v-model="action_params"
track-by="id"
label="name"
:placeholder="'Select'"
:multiple="true"
selected-label
:select-label="$t('FORMS.MULTISELECT.ENTER_TO_SELECT')"
deselect-label=""
:max-height="160"
:options="dropdownValues"
:allow-empty="false"
/>
</div>
</div>
<woot-button
icon="dismiss"
variant="clear"
color-scheme="secondary"
@click="removeAction"
/>
</div>
<p
v-if="v.action_params.$dirty && v.action_params.$error"
class="filter-error"
>
{{ $t('FILTER.EMPTY_VALUE_ERROR') }}
</p>
</div>
</template>
<script>
export default {
props: {
value: {
type: Object,
default: () => null,
},
actionTypes: {
type: Array,
default: () => [],
},
dropdownValues: {
type: Array,
default: () => [],
},
v: {
type: Object,
default: () => null,
},
},
computed: {
action_name: {
get() {
if (!this.value) return null;
return this.value.action_name;
},
set(value) {
const payload = this.value || {};
this.$emit('input', { ...payload, action_name: value });
},
},
action_params: {
get() {
if (!this.value) return null;
return this.value.action_params;
},
set(value) {
const payload = this.value || {};
this.$emit('input', { ...payload, action_params: value });
},
},
},
methods: {
removeAction() {
this.$emit('removeAction');
},
resetFilter() {
this.$emit('resetFilter');
},
},
};
</script>
<style lang="scss" scoped>
.filter {
background: var(--color-background);
padding: var(--space-small);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-medium);
margin-bottom: var(--space-small);
}
.filter.error {
background: var(--r-50);
}
.filter-inputs {
display: flex;
}
.filter-error {
color: var(--r-500);
display: block;
margin: var(--space-smaller) 0;
}
.action__question,
.filter__operator {
margin-bottom: var(--space-zero);
margin-right: var(--space-smaller);
}
.action__question {
max-width: 50%;
}
.filter__answer--wrap {
margin-right: var(--space-smaller);
flex-grow: 1;
input {
margin-bottom: 0;
}
}
.filter__answer {
&.answer--text-input {
margin-bottom: var(--space-zero);
}
}
.filter__join-operator-wrap {
position: relative;
z-index: var(--z-index-twenty);
margin: var(--space-zero);
}
.filter__join-operator {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin: var(--space-one) var(--space-zero);
.operator__line {
position: absolute;
width: 100%;
border-bottom: 1px solid var(--color-border);
}
.operator__select {
position: relative;
width: auto;
margin-bottom: var(--space-zero) !important;
}
}
.multiselect {
margin-bottom: var(--space-zero);
}
</style>

View file

@ -0,0 +1,75 @@
export const OPERATOR_TYPES_1 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
];
export const OPERATOR_TYPES_2 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
];
export const OPERATOR_TYPES_3 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
];
export const OPERATOR_TYPES_4 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
{
value: 'is_greater_than',
label: 'Is greater than',
},
{
value: 'is_lesser_than',
label: 'Is lesser than',
},
];

View file

@ -1,8 +1,29 @@
<template> <template>
<div class="filters"> <div class="filters">
<div class="filter"> <div class="filter" :class="{ error: v.values.$dirty && v.values.$error }">
<div class="filter-inputs"> <div class="filter-inputs">
<select <select
v-if="groupedFilters"
v-model="attributeKey"
class="filter__question"
@change="resetFilter()"
>
<optgroup
v-for="(group, i) in filterGroups"
:key="i"
:label="group.name"
>
<option
v-for="attribute in group.attributes"
:key="attribute.key"
:value="attribute.key"
>
{{ attribute.name }}
</option>
</optgroup>
</select>
<select
v-else
v-model="attributeKey" v-model="attributeKey"
class="filter__question" class="filter__question"
@change="resetFilter()" @change="resetFilter()"
@ -63,6 +84,14 @@
:option-height="104" :option-height="104"
/> />
</div> </div>
<div v-else-if="inputType === 'date'" class="multiselect-wrap--small">
<input
v-model="values"
type="date"
:editable="false"
class="answer--text-input datepicker"
/>
</div>
<input <input
v-else v-else
v-model="values" v-model="values"
@ -132,6 +161,14 @@ export default {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
groupedFilters: {
type: Boolean,
default: false,
},
filterGroups: {
type: Array,
default: () => [],
},
}, },
computed: { computed: {
attributeKey: { attributeKey: {
@ -193,6 +230,10 @@ export default {
border-radius: var(--border-radius-medium); border-radius: var(--border-radius-medium);
} }
.filter.error {
background: var(--r-50);
}
.filter-inputs { .filter-inputs {
display: flex; display: flex;
} }

View file

@ -16,9 +16,14 @@
ref="upload" ref="upload"
:size="4096 * 4096" :size="4096 * 4096"
:accept="allowedFileTypes" :accept="allowedFileTypes"
:multiple="enableMultipleFileUpload"
:drop="true" :drop="true"
:drop-directory="false" :drop-directory="false"
@input-file="onFileUpload" :data="{
direct_upload_url: '/rails/active_storage/direct_uploads',
direct_upload: true,
}"
@input-file="onDirectFileUpload"
> >
<woot-button <woot-button
v-if="showAttachButton" v-if="showAttachButton"
@ -79,6 +84,7 @@
<script> <script>
import FileUpload from 'vue-upload-component'; import FileUpload from 'vue-upload-component';
import * as ActiveStorage from 'activestorage';
import { import {
hasPressedAltAndWKey, hasPressedAltAndWKey,
hasPressedAltAndAKey, hasPressedAltAndAKey,
@ -108,7 +114,7 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
onFileUpload: { onDirectFileUpload: {
type: Function, type: Function,
default: () => {}, default: () => {},
}, },
@ -144,6 +150,10 @@ export default {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
enableMultipleFileUpload: {
type: Boolean,
default: true,
},
}, },
computed: { computed: {
isNote() { isNote() {
@ -166,6 +176,9 @@ export default {
return ALLOWED_FILE_TYPES; return ALLOWED_FILE_TYPES;
}, },
}, },
mounted() {
ActiveStorage.start();
},
methods: { methods: {
handleKeyEvents(e) { handleKeyEvents(e) {
if (hasPressedAltAndWKey(e)) { if (hasPressedAltAndWKey(e)) {

View file

@ -9,12 +9,13 @@
v-for="(filter, i) in appliedFilters" v-for="(filter, i) in appliedFilters"
:key="i" :key="i"
v-model="appliedFilters[i]" v-model="appliedFilters[i]"
:filter-attributes="filterAttributes" :filter-groups="filterGroups"
:input-type="getInputType(appliedFilters[i].attribute_key)" :input-type="getInputType(appliedFilters[i].attribute_key)"
:operators="getOperators(appliedFilters[i].attribute_key)" :operators="getOperators(appliedFilters[i].attribute_key)"
:dropdown-values="getDropdownValues(appliedFilters[i].attribute_key)" :dropdown-values="getDropdownValues(appliedFilters[i].attribute_key)"
:show-query-operator="i !== appliedFilters.length - 1" :show-query-operator="i !== appliedFilters.length - 1"
:show-user-input="showUserInput(appliedFilters[i].filter_operator)" :show-user-input="showUserInput(appliedFilters[i].filter_operator)"
:grouped-filters="true"
:v="$v.appliedFilters.$each[i]" :v="$v.appliedFilters.$each[i]"
@resetFilter="resetFilter(i, appliedFilters[i])" @resetFilter="resetFilter(i, appliedFilters[i])"
@removeFilter="removeFilter(i)" @removeFilter="removeFilter(i)"
@ -48,22 +49,24 @@
<script> <script>
import alertMixin from 'shared/mixins/alertMixin'; import alertMixin from 'shared/mixins/alertMixin';
import { required, requiredIf } from 'vuelidate/lib/validators'; import { required, requiredIf } from 'vuelidate/lib/validators';
import FilterInputBox from '../FilterInput.vue'; import FilterInputBox from '../FilterInput/Index.vue';
import languages from './advancedFilterItems/languages'; import languages from './advancedFilterItems/languages';
import countries from '/app/javascript/shared/constants/countries.js'; import countries from 'shared/constants/countries.js';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { filterAttributeGroups } from './advancedFilterItems';
import filterMixin from 'shared/mixins/filterMixin';
import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
export default { export default {
components: { components: {
FilterInputBox, FilterInputBox,
}, },
mixins: [alertMixin], mixins: [alertMixin, filterMixin],
props: { props: {
onClose: { onClose: {
type: Function, type: Function,
default: () => {}, default: () => {},
}, },
filterTypes: { initialFilterTypes: {
type: Array, type: Array,
default: () => [], default: () => [],
}, },
@ -87,22 +90,21 @@ export default {
return { return {
show: true, show: true,
appliedFilters: [], appliedFilters: [],
filterTypes: this.initialFilterTypes,
filterAttributeGroups,
filterGroups: [],
allCustomAttributes: [],
attributeModel: 'conversation_attribute',
filtersFori18n: 'FILTER',
}; };
}, },
computed: { computed: {
filterAttributes() {
return this.filterTypes.map(type => {
return {
key: type.attributeKey,
name: this.$t(`FILTER.ATTRIBUTES.${type.attributeI18nKey}`),
};
});
},
...mapGetters({ ...mapGetters({
getAppliedConversationFilters: 'getAppliedConversationFilters', getAppliedConversationFilters: 'getAppliedConversationFilters',
}), }),
}, },
mounted() { mounted() {
this.setFilterAttributes();
this.$store.dispatch('campaigns/get'); this.$store.dispatch('campaigns/get');
if (this.getAppliedConversationFilters.length) { if (this.getAppliedConversationFilters.length) {
this.appliedFilters = [...this.getAppliedConversationFilters]; this.appliedFilters = [...this.getAppliedConversationFilters];
@ -112,10 +114,41 @@ export default {
filter_operator: 'equal_to', filter_operator: 'equal_to',
values: '', values: '',
query_operator: 'and', query_operator: 'and',
attribute_model: 'standard',
}); });
} }
}, },
methods: { methods: {
getOperatorTypes(key) {
switch (key) {
case 'list':
return OPERATORS.OPERATOR_TYPES_1;
case 'text':
return OPERATORS.OPERATOR_TYPES_3;
case 'number':
return OPERATORS.OPERATOR_TYPES_1;
case 'link':
return OPERATORS.OPERATOR_TYPES_1;
case 'date':
return OPERATORS.OPERATOR_TYPES_4;
case 'checkbox':
return OPERATORS.OPERATOR_TYPES_1;
default:
return OPERATORS.OPERATOR_TYPES_1;
}
},
customAttributeInputType(key) {
switch (key) {
case 'date':
return 'date';
default:
return 'plain_text';
}
},
getAttributeModel(key) {
const type = this.filterTypes.find(filter => filter.attributeKey === key);
return type.attributeModel;
},
getInputType(key) { getInputType(key) {
const type = this.filterTypes.find(filter => filter.attributeKey === key); const type = this.filterTypes.find(filter => filter.attributeKey === key);
return type.inputType; return type.inputType;

View file

@ -128,7 +128,7 @@ export default {
type: [String, Number], type: [String, Number],
default: 0, default: 0,
}, },
customViewsId: { foldersId: {
type: [String, Number], type: [String, Number],
default: 0, default: 0,
}, },
@ -246,7 +246,7 @@ export default {
id: chat.id, id: chat.id,
label: this.activeLabel, label: this.activeLabel,
teamId: this.teamId, teamId: this.teamId,
customViewsId: this.customViewsId, foldersId: this.foldersId,
conversationType: this.conversationType, conversationType: this.conversationType,
}); });
router.push({ path: frontendURL(path) }); router.push({ path: frontendURL(path) });

View file

@ -57,22 +57,26 @@
/> />
</div> </div>
<spinner v-if="isPending" size="tiny" /> <spinner v-if="isPending" size="tiny" />
<a <div
v-if="isATweet && isIncoming && sender" v-if="showAvatar"
v-tooltip.top="tooltipForSender"
class="sender--info" class="sender--info"
:href="twitterProfileLink"
target="_blank"
rel="noopener noreferrer nofollow"
> >
<woot-thumbnail <woot-thumbnail
:src="sender.thumbnail" :src="sender.thumbnail"
:username="sender.name" :username="senderNameForAvatar"
size="16px" size="16px"
/> />
<div class="sender--available-name"> <a
v-if="isATweet && isIncoming"
class="sender--available-name"
:href="twitterProfileLink"
target="_blank"
rel="noopener noreferrer nofollow"
>
{{ sender.name }} {{ sender.name }}
</div> </a>
</a> </div>
<div v-if="isFailed" class="message-failed--alert"> <div v-if="isFailed" class="message-failed--alert">
<woot-button <woot-button
v-tooltip.top-end="$t('CONVERSATION.TRY_AGAIN')" v-tooltip.top-end="$t('CONVERSATION.TRY_AGAIN')"
@ -113,7 +117,6 @@ import BubbleActions from './bubble/Actions';
import Spinner from 'shared/components/Spinner'; import Spinner from 'shared/components/Spinner';
import ContextMenu from 'dashboard/modules/conversations/components/MessageContextMenu'; import ContextMenu from 'dashboard/modules/conversations/components/MessageContextMenu';
import { isEmptyObject } from 'dashboard/helper/commons';
import alertMixin from 'shared/mixins/alertMixin'; import alertMixin from 'shared/mixins/alertMixin';
import contentTypeMixin from 'shared/mixins/contentTypeMixin'; import contentTypeMixin from 'shared/mixins/contentTypeMixin';
import { MESSAGE_TYPE, MESSAGE_STATUS } from 'shared/constants/messages'; import { MESSAGE_TYPE, MESSAGE_STATUS } from 'shared/constants/messages';
@ -242,6 +245,12 @@ export default {
isIncoming() { isIncoming() {
return this.data.message_type === MESSAGE_TYPE.INCOMING; return this.data.message_type === MESSAGE_TYPE.INCOMING;
}, },
isOutgoing() {
return this.data.message_type === MESSAGE_TYPE.OUTGOING;
},
isTemplate() {
return this.data.message_type === MESSAGE_TYPE.TEMPLATE;
},
emailHeadAttributes() { emailHeadAttributes() {
return { return {
email: this.contentAttributes.email, email: this.contentAttributes.email,
@ -258,6 +267,19 @@ export default {
hasText() { hasText() {
return !!this.data.content; return !!this.data.content;
}, },
tooltipForSender() {
const name = this.senderNameForAvatar;
const { message_type: messageType } = this.data;
const showTooltip =
messageType === MESSAGE_TYPE.OUTGOING ||
messageType === MESSAGE_TYPE.TEMPLATE;
return showTooltip
? {
content: `${this.$t('CONVERSATION.SENT_BY')} ${name}`,
classes: 'top',
}
: false;
},
messageToolTip() { messageToolTip() {
if (this.isMessageDeleted) { if (this.isMessageDeleted) {
return false; return false;
@ -265,13 +287,7 @@ export default {
if (this.isFailed) { if (this.isFailed) {
return this.$t(`CONVERSATION.SEND_FAILED`); return this.$t(`CONVERSATION.SEND_FAILED`);
} }
const { sender } = this; return false;
return this.data.message_type === 1 && !isEmptyObject(sender)
? {
content: `${this.$t('CONVERSATION.SENT_BY')} ${sender.name}`,
classes: 'top',
}
: false;
}, },
wrapClass() { wrapClass() {
return { return {
@ -313,6 +329,19 @@ export default {
const { meta } = this.data; const { meta } = this.data;
return meta ? meta.error : ''; return meta ? meta.error : '';
}, },
showAvatar() {
if (this.isOutgoing || this.isTemplate) {
return true;
}
return this.isATweet && this.isIncoming && this.sender;
},
senderNameForAvatar() {
if (this.isOutgoing || this.isTemplate) {
const { name = this.$t('CONVERSATION.BOT') } = this.sender || {};
return name;
}
return '';
},
}, },
watch: { watch: {
data() { data() {

View file

@ -1,56 +1,29 @@
<template> <template>
<div class="view-box fill-height"> <div class="view-box fill-height">
<div <banner
v-if="!currentChat.can_reply && !isAWhatsappChannel" v-if="!currentChat.can_reply && !isAWhatsappChannel"
class="banner messenger-policy--banner" color-scheme="alert"
> :banner-message="$t('CONVERSATION.CANNOT_REPLY')"
<span> :href-link="facebookReplyPolicy"
{{ $t('CONVERSATION.CANNOT_REPLY') }} :href-link-text="$t('CONVERSATION.24_HOURS_WINDOW')"
<a />
:href="facebookReplyPolicy"
rel="noopener noreferrer nofollow" <banner
target="_blank" v-if="!currentChat.can_reply && isAWhatsappChannel"
> color-scheme="alert"
{{ $t('CONVERSATION.24_HOURS_WINDOW') }} :banner-message="$t('CONVERSATION.TWILIO_WHATSAPP_CAN_REPLY')"
</a> :href-link="twilioWhatsAppReplyPolicy"
</span> :href-link-text="$t('CONVERSATION.TWILIO_WHATSAPP_24_HOURS_WINDOW')"
</div> />
<div
v-if="!currentChat.can_reply && isAWhatsappChannel" <banner
class="banner messenger-policy--banner" v-if="isATweet"
> color-scheme="gray"
<span> :banner-message="tweetBannerText"
{{ $t('CONVERSATION.TWILIO_WHATSAPP_CAN_REPLY') }} :has-close-button="hasSelectedTweetId"
<a @close="removeTweetSelection"
:href="twilioWhatsAppReplyPolicy" />
rel="noopener noreferrer nofollow"
target="_blank"
>
{{ $t('CONVERSATION.TWILIO_WHATSAPP_24_HOURS_WINDOW') }}
</a>
</span>
</div>
<div v-if="isATweet" class="banner">
<span v-if="!selectedTweetId">
{{ $t('CONVERSATION.SELECT_A_TWEET_TO_REPLY') }}
</span>
<span v-else>
{{ $t('CONVERSATION.REPLYING_TO') }}
{{ selectedTweet.content || '' }}
</span>
<button
v-if="selectedTweetId"
class="banner-close-button"
@click="removeTweetSelection"
>
<fluent-icon
v-tooltip="$t('CONVERSATION.REMOVE_SELECTION')"
size="16"
icon="dismiss"
/>
</button>
</div>
<div class="sidebar-toggle__wrap"> <div class="sidebar-toggle__wrap">
<woot-button <woot-button
variant="smooth" variant="smooth"
@ -126,6 +99,7 @@ import { mapGetters } from 'vuex';
import ReplyBox from './ReplyBox'; import ReplyBox from './ReplyBox';
import Message from './Message'; import Message from './Message';
import conversationMixin from '../../../mixins/conversations'; import conversationMixin from '../../../mixins/conversations';
import Banner from 'dashboard/components/ui/Banner.vue';
import { getTypingUsersText } from '../../../helper/commons'; import { getTypingUsersText } from '../../../helper/commons';
import { BUS_EVENTS } from 'shared/constants/busEvents'; import { BUS_EVENTS } from 'shared/constants/busEvents';
import { REPLY_POLICY } from 'shared/constants/links'; import { REPLY_POLICY } from 'shared/constants/links';
@ -139,6 +113,7 @@ export default {
components: { components: {
Message, Message,
ReplyBox, ReplyBox,
Banner,
}, },
mixins: [conversationMixin, inboxMixin, eventListenerMixins, clickaway], mixins: [conversationMixin, inboxMixin, eventListenerMixins, clickaway],
props: { props: {
@ -173,7 +148,17 @@ export default {
inbox() { inbox() {
return this.$store.getters['inboxes/getInbox'](this.inboxId); return this.$store.getters['inboxes/getInbox'](this.inboxId);
}, },
hasSelectedTweetId() {
return !!this.selectedTweetId;
},
tweetBannerText() {
return !this.selectedTweetId
? this.$t('CONVERSATION.SELECT_A_TWEET_TO_REPLY')
: `
${this.$t('CONVERSATION.REPLYING_TO')}
${this.selectedTweet.content}` || '';
},
typingUsersList() { typingUsersList() {
const userList = this.$store.getters[ const userList = this.$store.getters[
'conversationTypingStatus/getUserList' 'conversationTypingStatus/getUserList'
@ -375,31 +360,6 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.banner {
background: var(--b-500);
color: var(--white);
font-size: var(--font-size-mini);
padding: var(--space-slab) var(--space-normal);
text-align: center;
position: relative;
a {
text-decoration: underline;
color: var(--white);
font-size: var(--font-size-mini);
}
&.messenger-policy--banner {
background: var(--r-400);
}
.banner-close-button {
cursor: pointer;
margin-left: var(--space--two);
color: var(--white);
}
}
.spinner--container { .spinner--container {
min-height: var(--space-jumbo); min-height: var(--space-jumbo);
} }

View file

@ -1,5 +1,13 @@
<template> <template>
<div class="reply-box" :class="replyBoxClass"> <div class="reply-box" :class="replyBoxClass">
<banner
v-if="showSelfAssignBanner"
color-scheme="secondary"
:banner-message="$t('CONVERSATION.NOT_ASSIGNED_TO_YOU')"
:has-action-button="true"
:action-button-label="$t('CONVERSATION.ASSIGN_TO_ME')"
@click="onClickSelfAssign"
/>
<reply-top-panel <reply-top-panel
:mode="replyType" :mode="replyType"
:set-reply-mode="setReplyMode" :set-reply-mode="setReplyMode"
@ -61,7 +69,7 @@
<reply-bottom-panel <reply-bottom-panel
:mode="replyType" :mode="replyType"
:send-button-text="replyButtonLabel" :send-button-text="replyButtonLabel"
:on-file-upload="onFileUpload" :on-direct-file-upload="onDirectFileUpload"
:show-file-upload="showFileUpload" :show-file-upload="showFileUpload"
:toggle-emoji-picker="toggleEmojiPicker" :toggle-emoji-picker="toggleEmojiPicker"
:show-emoji-picker="showEmojiPicker" :show-emoji-picker="showEmojiPicker"
@ -72,6 +80,7 @@
:is-format-mode="showRichContentEditor" :is-format-mode="showRichContentEditor"
:enable-rich-editor="isRichEditorEnabled" :enable-rich-editor="isRichEditorEnabled"
:enter-to-send-enabled="enterToSendEnabled" :enter-to-send-enabled="enterToSendEnabled"
:enable-multiple-file-upload="enableMultipleFileUpload"
@toggleEnterToSend="toggleEnterToSend" @toggleEnterToSend="toggleEnterToSend"
/> />
</div> </div>
@ -89,6 +98,7 @@ import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview';
import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel'; import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel';
import ReplyEmailHead from './ReplyEmailHead'; import ReplyEmailHead from './ReplyEmailHead';
import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel'; import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel';
import Banner from 'dashboard/components/ui/Banner.vue';
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants'; import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor'; import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
import { checkFileSizeLimit } from 'shared/helpers/FileHelper'; import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
@ -99,10 +109,12 @@ import {
isEscape, isEscape,
isEnter, isEnter,
hasPressedShift, hasPressedShift,
hasPressedCommandPlusKKey,
} 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'; import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { DirectUpload } from 'activestorage';
export default { export default {
components: { components: {
@ -114,6 +126,7 @@ export default {
ReplyEmailHead, ReplyEmailHead,
ReplyBottomPanel, ReplyBottomPanel,
WootMessageEditor, WootMessageEditor,
Banner,
}, },
mixins: [clickaway, inboxMixin, uiSettingsMixin, alertMixin], mixins: [clickaway, inboxMixin, uiSettingsMixin, alertMixin],
props: { props: {
@ -147,6 +160,11 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
currentChat: 'getSelectedChat',
currentUser: 'getCurrentUser',
}),
showRichContentEditor() { showRichContentEditor() {
if (this.isOnPrivateNote) { if (this.isOnPrivateNote) {
return true; return true;
@ -161,7 +179,36 @@ export default {
} }
return false; return false;
}, },
...mapGetters({ currentChat: 'getSelectedChat' }), assignedAgent: {
get() {
return this.currentChat.meta.assignee;
},
set(agent) {
const agentId = agent ? agent.id : 0;
this.$store.dispatch('setCurrentChatAssignee', agent);
this.$store
.dispatch('assignAgent', {
conversationId: this.currentChat.id,
agentId,
})
.then(() => {
this.showAlert(this.$t('CONVERSATION.CHANGE_AGENT'));
});
},
},
showSelfAssignBanner() {
if (this.message !== '' && !this.isOnPrivateNote) {
if (!this.assignedAgent) {
return true;
}
if (this.assignedAgent.id !== this.currentUser.id) {
return true;
}
}
return false;
},
enterToSendEnabled() { enterToSendEnabled() {
return !!this.uiSettings.enter_to_send_enabled; return !!this.uiSettings.enter_to_send_enabled;
}, },
@ -283,6 +330,9 @@ export default {
showReplyHead() { showReplyHead() {
return !this.isOnPrivateNote && this.isAnEmailChannel; return !this.isOnPrivateNote && this.isAnEmailChannel;
}, },
enableMultipleFileUpload() {
return this.isAnEmailChannel || this.isAWebWidgetInbox || this.isAPIInbox;
},
}, },
watch: { watch: {
currentChat(conversation) { currentChat(conversation) {
@ -355,11 +405,40 @@ export default {
e.preventDefault(); e.preventDefault();
this.sendMessage(); this.sendMessage();
} }
} else if (hasPressedCommandPlusKKey(e)) {
this.openCommandBar();
} }
}, },
openCommandBar() {
const ninja = document.querySelector('ninja-keys');
ninja.open();
},
toggleEnterToSend(enterToSendEnabled) { toggleEnterToSend(enterToSendEnabled) {
this.updateUISettings({ enter_to_send_enabled: enterToSendEnabled }); this.updateUISettings({ enter_to_send_enabled: enterToSendEnabled });
}, },
onClickSelfAssign() {
const {
account_id,
availability_status,
available_name,
email,
id,
name,
role,
avatar_url,
} = this.currentUser;
const selfAssign = {
account_id,
availability_status,
available_name,
email,
id,
name,
role,
thumbnail: avatar_url,
};
this.assignedAgent = selfAssign;
},
async sendMessage() { async sendMessage() {
if (this.isReplyButtonDisabled) { if (this.isReplyButtonDisabled) {
return; return;
@ -439,6 +518,38 @@ export default {
isPrivate, isPrivate,
}); });
}, },
onDirectFileUpload(file) {
if (!file) {
return;
}
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
const upload = new DirectUpload(
file.file,
'/rails/active_storage/direct_uploads',
null,
file.file.name
);
upload.create((error, blob) => {
if (error) {
this.showAlert(error);
} else {
this.attachedFiles.push({
currentChatId: this.currentChat.id,
resource: blob,
isPrivate: this.isPrivate,
thumb: null,
blobSignedId: blob.signed_id,
});
}
});
} else {
this.showAlert(
this.$t('CONVERSATION.FILE_SIZE_LIMIT', {
MAXIMUM_FILE_UPLOAD_SIZE,
})
);
}
},
onFileUpload(file) { onFileUpload(file) {
if (!file) { if (!file) {
return; return;
@ -469,7 +580,6 @@ export default {
); );
}, },
getMessagePayload(message) { getMessagePayload(message) {
const [attachment] = this.attachedFiles;
const messagePayload = { const messagePayload = {
conversationId: this.currentChat.id, conversationId: this.currentChat.id,
message, message,
@ -480,8 +590,11 @@ export default {
messagePayload.contentAttributes = { in_reply_to: this.inReplyTo }; messagePayload.contentAttributes = { in_reply_to: this.inReplyTo };
} }
if (attachment) { if (this.attachedFiles && this.attachedFiles.length) {
messagePayload.file = attachment.resource.file; messagePayload.files = [];
this.attachedFiles.forEach(attachment => {
messagePayload.files.push(attachment.blobSignedId);
});
} }
if (this.ccEmails) { if (this.ccEmails) {

View file

@ -1,229 +1,144 @@
import {
OPERATOR_TYPES_1,
OPERATOR_TYPES_2,
OPERATOR_TYPES_3,
} from '../../FilterInput/FilterOperatorTypes';
const filterTypes = [ const filterTypes = [
{ {
attributeKey: 'status', attributeKey: 'status',
attributeI18nKey: 'STATUS', attributeI18nKey: 'STATUS',
inputType: 'multi_select', inputType: 'multi_select',
dataType: 'text', dataType: 'text',
filterOperators: [ filterOperators: OPERATOR_TYPES_1,
{ attributeModel: 'standard',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
],
attribute_type: 'standard',
}, },
{ {
attributeKey: 'assignee_id', attributeKey: 'assignee_id',
attributeI18nKey: 'ASSIGNEE_NAME', attributeI18nKey: 'ASSIGNEE_NAME',
inputType: 'search_select', inputType: 'search_select',
dataType: 'text', dataType: 'text',
filterOperators: [ filterOperators: OPERATOR_TYPES_2,
{ attributeModel: 'standard',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
}, },
{ {
attributeKey: 'inbox_id', attributeKey: 'inbox_id',
attributeI18nKey: 'INBOX_NAME', attributeI18nKey: 'INBOX_NAME',
inputType: 'search_select', inputType: 'search_select',
dataType: 'text', dataType: 'text',
filterOperators: [ filterOperators: OPERATOR_TYPES_2,
{ attributeModel: 'standard',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
}, },
{ {
attributeKey: 'team_id', attributeKey: 'team_id',
attributeI18nKey: 'TEAM_NAME', attributeI18nKey: 'TEAM_NAME',
inputType: 'search_select', inputType: 'search_select',
dataType: 'number', dataType: 'number',
filterOperators: [ filterOperators: OPERATOR_TYPES_2,
{ attributeModel: 'standard',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
}, },
{ {
attributeKey: 'display_id', attributeKey: 'display_id',
attributeI18nKey: 'CONVERSATION_IDENTIFIER', attributeI18nKey: 'CONVERSATION_IDENTIFIER',
inputType: 'plain_text', inputType: 'plain_text',
dataType: 'Number', dataType: 'Number',
filterOperators: [ filterOperators: OPERATOR_TYPES_3,
{ attributeModel: 'standard',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
],
attribute_type: 'standard',
}, },
{ {
attributeKey: 'campaign_id', attributeKey: 'campaign_id',
attributeI18nKey: 'CAMPAIGN_NAME', attributeI18nKey: 'CAMPAIGN_NAME',
inputType: 'search_select', inputType: 'search_select',
dataType: 'Number', dataType: 'Number',
filterOperators: [ filterOperators: OPERATOR_TYPES_2,
{ attributeModel: 'standard',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
}, },
{ {
attributeKey: 'labels', attributeKey: 'labels',
attributeI18nKey: 'LABELS', attributeI18nKey: 'LABELS',
inputType: 'multi_select', inputType: 'multi_select',
dataType: 'text', dataType: 'text',
filterOperators: [ filterOperators: OPERATOR_TYPES_2,
{ attributeModel: 'standard',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
}, },
{ {
attributeKey: 'browser_language', attributeKey: 'browser_language',
attributeI18nKey: 'BROWSER_LANGUAGE', attributeI18nKey: 'BROWSER_LANGUAGE',
inputType: 'search_select', inputType: 'search_select',
dataType: 'text', dataType: 'text',
filterOperators: [ filterOperators: OPERATOR_TYPES_1,
{ attributeModel: 'additional',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
],
attribute_type: 'additional_attributes',
}, },
{ {
attributeKey: 'country_code', attributeKey: 'country_code',
attributeI18nKey: 'COUNTRY_NAME', attributeI18nKey: 'COUNTRY_NAME',
inputType: 'search_select', inputType: 'search_select',
dataType: 'text', dataType: 'text',
filterOperators: [ filterOperators: OPERATOR_TYPES_1,
{ attributeModel: 'additional',
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
],
attribute_type: 'additional_attributes',
}, },
{ {
attributeKey: 'referer', attributeKey: 'referer',
attributeI18nKey: 'REFERER_LINK', attributeI18nKey: 'REFERER_LINK',
inputType: 'plain_text', inputType: 'plain_text',
dataType: 'text', dataType: 'text',
filterOperators: [ filterOperators: OPERATOR_TYPES_3,
attributeModel: 'additional',
},
];
export const filterAttributeGroups = [
{
name: 'Standard Filters',
i18nGroup: 'STANDARD_FILTERS',
attributes: [
{ {
value: 'equal_to', key: 'status',
label: 'Equal to', i18nKey: 'STATUS',
}, },
{ {
value: 'not_equal_to', key: 'assignee_id',
label: 'Not equal to', i18nKey: 'ASSIGNEE_NAME',
}, },
{ {
value: 'contains', key: 'inbox_id',
label: 'Contains', i18nKey: 'INBOX_NAME',
}, },
{ {
value: 'does_not_contain', key: 'team_id',
label: 'Does not contain', i18nKey: 'TEAM_NAME',
},
{
key: 'display_id',
i18nKey: 'CONVERSATION_IDENTIFIER',
},
{
key: 'campaign_id',
i18nKey: 'CAMPAIGN_NAME',
},
{
key: 'labels',
i18nKey: 'LABELS',
},
],
},
{
name: 'Additional Filters',
i18nGroup: 'ADDITIONAL_FILTERS',
attributes: [
{
key: 'browser_language',
i18nKey: 'BROWSER_LANGUAGE',
},
{
key: 'country_code',
i18nKey: 'COUNTRY_NAME',
},
{
key: 'referer',
i18nKey: 'REFERER_LINK',
}, },
], ],
attribute_type: 'additional_attributes',
}, },
]; ];

View file

@ -12,7 +12,7 @@ export const conversationUrl = ({
label, label,
teamId, teamId,
conversationType = '', conversationType = '',
customViewsId, foldersId,
}) => { }) => {
let url = `accounts/${accountId}/conversations/${id}`; let url = `accounts/${accountId}/conversations/${id}`;
if (activeInbox) { if (activeInbox) {
@ -21,8 +21,8 @@ export const conversationUrl = ({
url = `accounts/${accountId}/label/${label}/conversations/${id}`; url = `accounts/${accountId}/label/${label}/conversations/${id}`;
} else if (teamId) { } else if (teamId) {
url = `accounts/${accountId}/team/${teamId}/conversations/${id}`; url = `accounts/${accountId}/team/${teamId}/conversations/${id}`;
} else if (customViewsId && customViewsId !== 0) { } else if (foldersId && foldersId !== 0) {
url = `accounts/${accountId}/custom_view/${customViewsId}/conversations/${id}`; url = `accounts/${accountId}/custom_view/${foldersId}/conversations/${id}`;
} else if (conversationType === 'mention') { } else if (conversationType === 'mention') {
url = `accounts/${accountId}/mentions/conversations/${id}`; url = `accounts/${accountId}/mentions/conversations/${id}`;
} }
@ -37,3 +37,9 @@ export const accountIdFromPathname = pathname => {
const accountId = isScoped ? Number(urlParam) : ''; const accountId = isScoped ? Number(urlParam) : '';
return accountId; return accountId;
}; };
export const isValidURL = value => {
/* eslint-disable no-useless-escape */
const URL_REGEX = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/gm;
return URL_REGEX.test(value);
};

View file

@ -0,0 +1,18 @@
const generatePayload = data => {
const actions = JSON.parse(JSON.stringify(data));
let payload = actions.map(item => {
if (Array.isArray(item.action_params)) {
item.action_params = item.action_params.map(val => val.id);
} else if (typeof item.values === 'object') {
item.action_params = [item.action_params.id];
} else if (!item.action_params) {
item.action_params = [];
} else {
item.action_params = [item.action_params];
}
return item;
});
return payload;
};
export default generatePayload;

View file

@ -1,5 +1,19 @@
const lowerCaseValues = (operator, values) => {
if (operator === 'equal_to' || operator === 'not_equal_to') {
values = values.map(val => {
if (typeof val === 'string') {
return val.toLowerCase();
}
return val;
});
}
return values;
};
const generatePayload = data => { const generatePayload = data => {
let payload = data.map(item => { // Make a copy of data to avoid vue data reactivity issues
const filters = JSON.parse(JSON.stringify(data));
let payload = filters.map(item => {
if (Array.isArray(item.values)) { if (Array.isArray(item.values)) {
item.values = item.values.map(val => val.id); item.values = item.values.map(val => val.id);
} else if (typeof item.values === 'object') { } else if (typeof item.values === 'object') {
@ -9,6 +23,8 @@ const generatePayload = data => {
} else { } else {
item.values = [item.values]; item.values = [item.values];
} }
// Convert all values to lowerCase if operator_type is 'equal_to' or 'not_equal_to'
item.values = lowerCaseValues(item.filter_operator, item.values);
return item; return item;
}); });
// For every query added, the query_operator is set default to and so the // For every query added, the query_operator is set default to and so the

View file

@ -2,6 +2,7 @@ import {
frontendURL, frontendURL,
conversationUrl, conversationUrl,
accountIdFromPathname, accountIdFromPathname,
isValidURL,
} from '../URLHelper'; } from '../URLHelper';
describe('#URL Helpers', () => { describe('#URL Helpers', () => {
@ -48,4 +49,13 @@ describe('#URL Helpers', () => {
expect(accountIdFromPathname('')).toBe(''); expect(accountIdFromPathname('')).toBe('');
}); });
}); });
describe('isValidURL', () => {
it('should return true if valid url is passed', () => {
expect(isValidURL('https://chatwoot.com')).toBe(true);
});
it('should return false if invalid url is passed', () => {
expect(isValidURL('alert.window')).toBe(false);
});
});
}); });

View file

@ -0,0 +1,41 @@
import actionQueryGenerator from '../actionQueryGenerator';
const testData = [
{
action_name: 'add_label',
action_params: [{ id: 'testlabel', name: 'testlabel' }],
},
{
action_name: 'assign_team',
action_params: [
{
id: 1,
name: 'sales team',
description: 'This is our internal sales team',
allow_auto_assign: true,
account_id: 1,
is_member: true,
},
],
},
];
const finalResult = [
{
action_name: 'add_label',
action_params: ['testlabel'],
},
{
action_name: 'assign_team',
action_params: [1],
},
];
describe('#actionQueryGenerator', () => {
it('returns the correct format of filter query', () => {
expect(actionQueryGenerator(testData)).toEqual(finalResult);
expect(
actionQueryGenerator(testData).every(i => Array.isArray(i.action_params))
).toBe(true);
});
});

View file

@ -5,7 +5,7 @@ const testData = [
attribute_key: 'status', attribute_key: 'status',
filter_operator: 'equal_to', filter_operator: 'equal_to',
values: [ values: [
{ id: 'pending', name: 'Pending' }, { id: 'PENDING', name: 'Pending' },
{ id: 'resolved', name: 'Resolved' }, { id: 'resolved', name: 'Resolved' },
], ],
query_operator: 'and', query_operator: 'and',
@ -52,7 +52,7 @@ const finalResult = {
{ {
attribute_key: 'id', attribute_key: 'id',
filter_operator: 'equal_to', filter_operator: 'equal_to',
values: ['This is a test'], values: ['this is a test'],
}, },
], ],
}; };

View file

@ -1,6 +1,6 @@
{ {
"FILTER": { "FILTER": {
"TITLE": "تصفية المحادثة", "TITLE": "تصفية المحادثات",
"SUBTITLE": "إضافة فلاتر أدناه واضغط على 'إرسال' لتصفية المحادثات.", "SUBTITLE": "إضافة فلاتر أدناه واضغط على 'إرسال' لتصفية المحادثات.",
"ADD_NEW_FILTER": "إضافة فلتر", "ADD_NEW_FILTER": "إضافة فلتر",
"FILTER_DELETE_ERROR": "يجب ان يكون لديك فلتر واحد على الاقل", "FILTER_DELETE_ERROR": "يجب ان يكون لديك فلتر واحد على الاقل",
@ -19,7 +19,9 @@
"contains": "يحتوي", "contains": "يحتوي",
"does_not_contain": "لا يحتوي", "does_not_contain": "لا يحتوي",
"is_present": "موجود", "is_present": "موجود",
"is_not_present": "غير موجود" "is_not_present": "غير موجود",
"is_greater_than": "هو أكبر من",
"is_lesser_than": "هو أقل من"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"STATUS": "الحالة", "STATUS": "الحالة",
@ -31,7 +33,54 @@
"LABELS": "الوسوم", "LABELS": "الوسوم",
"BROWSER_LANGUAGE": "لغة المتصفح", "BROWSER_LANGUAGE": "لغة المتصفح",
"COUNTRY_NAME": "اسم الدولة", "COUNTRY_NAME": "اسم الدولة",
"REFERER_LINK": "رابط المرجع" "REFERER_LINK": "رابط المرجع",
"CUSTOM_ATTRIBUTE_LIST": "القائمة",
"CUSTOM_ATTRIBUTE_TEXT": "النص",
"CUSTOM_ATTRIBUTE_NUMBER": "العدد",
"CUSTOM_ATTRIBUTE_LINK": "الرابط",
"CUSTOM_ATTRIBUTE_CHECKBOX": "مربع"
},
"GROUPS": {
"STANDARD_FILTERS": "الفلاتر القياسية",
"ADDITIONAL_FILTERS": "فلاتر إضافية",
"CUSTOM_ATTRIBUTES": "سمات مخصصة"
},
"CUSTOM_VIEWS": {
"ADD": {
"TITLE": "هل تريد حفظ هذا الفلتر؟",
"LABEL": "تسمية هذا الفلتر",
"PLACEHOLDER": "أدخل اسم لهذا الفلتر",
"ERROR_MESSAGE": "الاسم مطلوب",
"SAVE_BUTTON": "حفظ الفلتر",
"CANCEL_BUTTON": "إلغاء",
"API_FOLDERS": {
"SUCCESS_MESSAGE": "تم إنشاء طريقة عرض مخصصة بنجاح",
"ERROR_MESSAGE": "خطأ أثناء إنشاء طريقة عرض مخصصة"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "تم إنشاء طريقة عرض مخصصة بنجاح",
"ERROR_MESSAGE": "خطأ أثناء إنشاء طريقة عرض مخصصة"
}
},
"DELETE": {
"DELETE_BUTTON": "حذف الفلتر",
"MODAL": {
"CONFIRM": {
"TITLE": "تأكيد الحذف",
"MESSAGE": "هل أنت متأكد من حذف الفلتر ",
"YES": "نعم، احذف",
"NO": "لا، احتفظ به"
}
},
"API_FOLDERS": {
"SUCCESS_MESSAGE": "تم حذف طريقة عرض مخصصة بنجاح",
"ERROR_MESSAGE": "حدث خطأ أثناء حذف المجلد"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "تم حذف العرض المخصص بنجاح",
"ERROR_MESSAGE": "حدث خطأ أثناء حذف طريقة عرض مخصصة"
}
}
} }
} }
} }

View file

@ -1,6 +1,89 @@
{ {
"AUTOMATION": { "AUTOMATION": {
"HEADER": "الأتمتة", "HEADER": "الأتمتة",
"HEADER_BTN_TXT": "إضافة قاعدة أتمتة" "HEADER_BTN_TXT": "إضافة قاعدة أتمتة",
"LOADING": "جلب قواعد الأتمتة",
"SIDEBAR_TXT": "<p><b>قواعد الأتمتة الآليه</b> <p>يمكن للأتمتة استبدال وأتمتة العمليات القائمة التي تتطلب جهداً يدوياً. يمكنك القيام بالعديد من الأشياء مع التشغيل الآلي، بما في ذلك إضافة تسميات وتعيين المحادثة لأفضل وكيل. لذا يركز الفريق على ما يفعلونه على أفضل وجه ويقضي وقتاً قليلاً على المهام اليدوية.</p>",
"ADD": {
"TITLE": "إضافة قاعدة أتمتة",
"SUBMIT": "إنشاء",
"CANCEL_BUTTON_TEXT": "إلغاء",
"FORM": {
"NAME": {
"LABEL": "اسم القاعدة",
"PLACEHOLDER": "أدخل اسم القاعدة",
"ERROR": "الاسم مطلوب"
},
"DESC": {
"LABEL": "الوصف",
"PLACEHOLDER": "ادخل وصف القاعدة",
"ERROR": "الوصف مطلوب"
},
"EVENT": {
"LABEL": "الحدث",
"PLACEHOLDER": "الرجاء اختيار واحد",
"ERROR": "الحدث مطلوب"
},
"CONDITIONS": {
"LABEL": "الشروط"
},
"ACTIONS": {
"LABEL": "الإجراءات"
}
},
"CONDITION_BUTTON_LABEL": "إضافة شرط",
"ACTION_BUTTON_LABEL": "إضافة إجراء",
"API": {
"SUCCESS_MESSAGE": "تمت إضافة قاعدة الأتمتة بنجاح",
"ERROR_MESSAGE": "تعذر إنشاء قاعدة أتمتة ، يرجى المحاولة مرة أخرى لاحقاً"
}
},
"LIST": {
"TABLE_HEADER": [
"الاسم",
"الوصف",
"مفعل",
"تم إنشاؤها في"
],
"404": "لم يتم العثور على قواعد أتمتة"
},
"DELETE": {
"TITLE": "حذف قاعدة الأتمتة",
"SUBMIT": "حذف",
"CANCEL_BUTTON_TEXT": "إلغاء",
"CONFIRM": {
"TITLE": "تأكيد الحذف",
"MESSAGE": "هل أنت متأكد من الحذف ",
"YES": "نعم، احذف ",
"NO": "لا، احتفظ "
},
"API": {
"SUCCESS_MESSAGE": "تم حذف قاعدة الأتمتة بنجاح",
"ERROR_MESSAGE": "تعذر حذف قاعدة الأتمتة، يرجى المحاولة مرة أخرى لاحقاً"
}
},
"EDIT": {
"TITLE": "تعديل قاعدة الأتمتة",
"SUBMIT": "تعديل",
"CANCEL_BUTTON_TEXT": "إلغاء",
"API": {
"SUCCESS_MESSAGE": "تم تحديث قاعدة الأتمتة بنجاح",
"ERROR_MESSAGE": "تعذر تحديث قاعدة الأتمتة، الرجاء المحاولة مرة أخرى في وقت لاحق"
}
},
"CLONE": {
"TOOLTIP": "نسخ",
"API": {
"SUCCESS_MESSAGE": "تم نسخ الأتمتة بنجاح",
"ERROR_MESSAGE": "تعذر استنساخ قاعدة الأتمتة، الرجاء المحاولة مرة أخرى لاحقاً"
}
},
"FORM": {
"EDIT": "تعديل",
"CREATE": "إنشاء",
"DELETE": "حذف",
"CANCEL": "إلغاء",
"RESET_MESSAGE": "تغيير نوع الحدث سوف يعيد تعيين الشروط والأحداث التي أضفتها أدناه"
}
} }
} }

View file

@ -17,7 +17,7 @@
}, },
"ADD": { "ADD": {
"TITLE": "إضافة رد جاهز", "TITLE": "إضافة رد جاهز",
"DESC": "الردود الجاهزة هي قوالب رسائل معدة مسبقاً يمكن استخدامها لتسريع كتابة الردود في المحادثات .", "DESC": "الردود الجاهزة هي قوالب رسائل معدة مسبقاً يمكن استخدامها لتسريع كتابة الردود في المحادثات.",
"CANCEL_BUTTON_TEXT": "إلغاء", "CANCEL_BUTTON_TEXT": "إلغاء",
"FORM": { "FORM": {
"SHORT_CODE": { "SHORT_CODE": {

View file

@ -7,7 +7,7 @@
"404": "لا توجد محادثات نشطة في هذه المجموعة." "404": "لا توجد محادثات نشطة في هذه المجموعة."
}, },
"TAB_HEADING": "المحادثات", "TAB_HEADING": "المحادثات",
"MENTION_HEADING": "Mentions", "MENTION_HEADING": "الإشارات",
"SEARCH": { "SEARCH": {
"INPUT": "البحث عن جهات الاتصال، المحادثات، قوالب الردود الجاهزة .." "INPUT": "البحث عن جهات الاتصال، المحادثات، قوالب الردود الجاهزة .."
}, },

View file

@ -111,7 +111,7 @@
"LABEL": "رقم الهاتف", "LABEL": "رقم الهاتف",
"HELP": "يجب ان يحتوى رقم الهاتف على كود دولتك تسبقها علامة +\nمثال: +20101243567", "HELP": "يجب ان يحتوى رقم الهاتف على كود دولتك تسبقها علامة +\nمثال: +20101243567",
"ERROR": "يجب ان تكون خانة رقم الهاتف إما فارغة او مكتملة مع رمز الدولة", "ERROR": "يجب ان تكون خانة رقم الهاتف إما فارغة او مكتملة مع رمز الدولة",
"DUPLICATE": "This phone number is in use for another contact." "DUPLICATE": "رقم الهاتف هذا مستخدم لجهة اتصال أخرى."
}, },
"LOCATION": { "LOCATION": {
"PLACEHOLDER": "أدخل موقع جهة الاتصال", "PLACEHOLDER": "أدخل موقع جهة الاتصال",
@ -169,6 +169,7 @@
"SUBMIT": "إرسال الرسالة", "SUBMIT": "إرسال الرسالة",
"CANCEL": "إلغاء", "CANCEL": "إلغاء",
"SUCCESS_MESSAGE": "تم إرسال الرسالة!", "SUCCESS_MESSAGE": "تم إرسال الرسالة!",
"GO_TO_CONVERSATION": "عرض",
"ERROR_MESSAGE": "تعذر الإرسال! حاول مرة أخرى" "ERROR_MESSAGE": "تعذر الإرسال! حاول مرة أخرى"
} }
}, },
@ -177,7 +178,9 @@
"FIELDS": "تصنفيات جهات الاتصال", "FIELDS": "تصنفيات جهات الاتصال",
"SEARCH_BUTTON": "بحث", "SEARCH_BUTTON": "بحث",
"SEARCH_INPUT_PLACEHOLDER": "بحث عن جهات الاتصال", "SEARCH_INPUT_PLACEHOLDER": "بحث عن جهات الاتصال",
"FILTER_CONTACTS": "Filter", "FILTER_CONTACTS": "فلترة",
"FILTER_CONTACTS_SAVE": "حفظ الفلتر",
"FILTER_CONTACTS_DELETE": "حذف الفلتر",
"LIST": { "LIST": {
"LOADING_MESSAGE": "جاري تحميل جهات الاتصال...", "LOADING_MESSAGE": "جاري تحميل جهات الاتصال...",
"404": "لا توجد جهات اتصال تطابق بحثك 🔍", "404": "لا توجد جهات اتصال تطابق بحثك 🔍",

View file

@ -1,15 +1,15 @@
{ {
"CONTACTS_FILTER": { "CONTACTS_FILTER": {
"TITLE": "Filter Contacts", "TITLE": "تصفية جهات الاتصال",
"SUBTITLE": "Add filters below and hit 'Submit' to filter contacts.", "SUBTITLE": "إضافة فلاتر أدناه واضغط على 'إرسال' لتصفية جهات الاتصال.",
"ADD_NEW_FILTER": "إضافة فلتر", "ADD_NEW_FILTER": "إضافة فلتر",
"CLEAR_ALL_FILTERS": "Clear All Filters", "CLEAR_ALL_FILTERS": "مسح جميع الفلاتر",
"FILTER_DELETE_ERROR": "يجب ان يكون لديك فلتر واحد على الاقل", "FILTER_DELETE_ERROR": "يجب ان يكون لديك فلتر واحد على الاقل",
"SUBMIT_BUTTON_LABEL": "إرسال", "SUBMIT_BUTTON_LABEL": "إرسال",
"CANCEL_BUTTON_LABEL": "إلغاء", "CANCEL_BUTTON_LABEL": "إلغاء",
"CLEAR_BUTTON_LABEL": "مسح الفلاتر", "CLEAR_BUTTON_LABEL": "مسح الفلاتر",
"EMPTY_VALUE_ERROR": "القيمة مطلوبة", "EMPTY_VALUE_ERROR": "القيمة مطلوبة",
"TOOLTIP_LABEL": "Filter contacts", "TOOLTIP_LABEL": "تصفية جهات الاتصال",
"QUERY_DROPDOWN_LABELS": { "QUERY_DROPDOWN_LABELS": {
"AND": "و", "AND": "و",
"OR": "أو" "OR": "أو"
@ -20,15 +20,27 @@
"contains": "يحتوي", "contains": "يحتوي",
"does_not_contain": "لا يحتوي", "does_not_contain": "لا يحتوي",
"is_present": "موجود", "is_present": "موجود",
"is_not_present": "غير موجود" "is_not_present": "غير موجود",
"is_greater_than": "هو أكبر من",
"is_lesser_than": "هو أقل من"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"NAME": "الاسم", "NAME": "الاسم",
"EMAIL": "البريد الإلكتروني", "EMAIL": "البريد الإلكتروني",
"PHONE_NUMBER": "رقم الهاتف", "PHONE_NUMBER": "رقم الهاتف",
"IDENTIFIER": "Identifier", "IDENTIFIER": "المعرف",
"CITY": "المدينة", "CITY": "المدينة",
"COUNTRY": "الدولة" "COUNTRY": "الدولة",
"CUSTOM_ATTRIBUTE_LIST": "القائمة",
"CUSTOM_ATTRIBUTE_TEXT": "النص",
"CUSTOM_ATTRIBUTE_NUMBER": "العدد",
"CUSTOM_ATTRIBUTE_LINK": "الرابط",
"CUSTOM_ATTRIBUTE_CHECKBOX": "مربع"
},
"GROUPS": {
"STANDARD_FILTERS": "الفلاتر القياسية",
"ADDITIONAL_FILTERS": "فلاتر إضافية",
"CUSTOM_ATTRIBUTES": "سمات مخصصة"
} }
} }
} }

View file

@ -22,6 +22,8 @@
"LOADING_CONVERSATIONS": "جاري تحميل المحادثات", "LOADING_CONVERSATIONS": "جاري تحميل المحادثات",
"CANNOT_REPLY": "لا يمكنك الرد بسبب", "CANNOT_REPLY": "لا يمكنك الرد بسبب",
"24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة", "24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة",
"NOT_ASSIGNED_TO_YOU": "لم يتم تعيين هذه المحادثة لك. هل ترغب في تعيين هذه المحادثة لنفسك؟",
"ASSIGN_TO_ME": "إسناد لي",
"TWILIO_WHATSAPP_CAN_REPLY": "يمكنك فقط الرد على هذه المحادثة باستخدام رسالة قالب بسبب", "TWILIO_WHATSAPP_CAN_REPLY": "يمكنك فقط الرد على هذه المحادثة باستخدام رسالة قالب بسبب",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة",
"SELECT_A_TWEET_TO_REPLY": "الرجاء تحديد تغريدة للرد عليها.", "SELECT_A_TWEET_TO_REPLY": "الرجاء تحديد تغريدة للرد عليها.",
@ -90,6 +92,9 @@
"FILE_SIZE_LIMIT": "حجم الملف يتجاوز حد الاقصى وهو {MAXIMUM_FILE_UPLOAD_SIZE}", "FILE_SIZE_LIMIT": "حجم الملف يتجاوز حد الاقصى وهو {MAXIMUM_FILE_UPLOAD_SIZE}",
"MESSAGE_ERROR": "غير قادر على إرسال هذه الرسالة، الرجاء المحاولة مرة أخرى لاحقاً", "MESSAGE_ERROR": "غير قادر على إرسال هذه الرسالة، الرجاء المحاولة مرة أخرى لاحقاً",
"SENT_BY": "أرسلت بواسطة:", "SENT_BY": "أرسلت بواسطة:",
"BOT": "رد آلي",
"SEND_FAILED": "تعذر إرسال الرسالة! حاول مرة أخرى",
"TRY_AGAIN": "إعادة المحاولة",
"ASSIGNMENT": { "ASSIGNMENT": {
"SELECT_AGENT": "اختر وكيل", "SELECT_AGENT": "اختر وكيل",
"REMOVE": "حذف", "REMOVE": "حذف",

View file

@ -14,8 +14,8 @@
"NOTE": "" "NOTE": ""
}, },
"ACCOUNT_ID": { "ACCOUNT_ID": {
"TITLE": "Account ID", "TITLE": "معرف الحساب",
"NOTE": "This ID is required if you are building an API based integration" "NOTE": "هذا المعرف مطلوب إذا كنت بصدد بناء تكامل على API"
}, },
"NAME": { "NAME": {
"LABEL": "اسم الحساب", "LABEL": "اسم الحساب",
@ -40,7 +40,7 @@
"AUTO_RESOLVE_DURATION": { "AUTO_RESOLVE_DURATION": {
"LABEL": "عدد الأيام بعد التذكرة التي يجب أن يحل تلقائياً إذا لم يكن هناك أي نشاط", "LABEL": "عدد الأيام بعد التذكرة التي يجب أن يحل تلقائياً إذا لم يكن هناك أي نشاط",
"PLACEHOLDER": "30", "PLACEHOLDER": "30",
"ERROR": "الرجاء إدخال مدة الحل التلقائي صحيحة (يوم واحد على الأقل)" "ERROR": "الرجاء إدخال مدة حل تلقائي صالحة (حد أدنى 1 يوم والحد الأقصى 999 يوما)"
}, },
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "الاستمرار في المحادثة عبر رسائل البريد الإلكتروني مفعّل لحسابك.", "INBOUND_EMAIL_ENABLED": "الاستمرار في المحادثة عبر رسائل البريد الإلكتروني مفعّل لحسابك.",

View file

@ -49,7 +49,7 @@
"HELP": "لإضافة حساب تويتر الخاص بك كقناة تواصل، تحتاج إلى مصادقة حسابك على تويتر بك بالنقر على زر \"تسجيل الدخول باستخدام تويتر\" ", "HELP": "لإضافة حساب تويتر الخاص بك كقناة تواصل، تحتاج إلى مصادقة حسابك على تويتر بك بالنقر على زر \"تسجيل الدخول باستخدام تويتر\" ",
"ERROR_MESSAGE": "حدث خطأ أثناء الاتصال بـ Twitter، الرجاء المحاولة مرة أخرى", "ERROR_MESSAGE": "حدث خطأ أثناء الاتصال بـ Twitter، الرجاء المحاولة مرة أخرى",
"TWEETS": { "TWEETS": {
"ENABLE": "Create conversations from mentioned Tweets" "ENABLE": "إنشاء محادثات من التغريدات المشار إليها"
} }
}, },
"WEBSITE_CHANNEL": { "WEBSITE_CHANNEL": {
@ -136,8 +136,56 @@
} }
}, },
"SMS": { "SMS": {
"TITLE": "قناة SMS عبر Twilio", "TITLE": "قناة SMS",
"DESC": "ابدأ في دعم عملائك عبر الرسائل القصيرة بإستخدام Twilio." "DESC": "ابدأ في دعم عملائك عبر الرسائل القصيرة.",
"PROVIDERS": {
"LABEL": "API Provider",
"TWILIO": "تويليو",
"BANDWIDTH": "سعة الإنترنت"
},
"API": {
"ERROR_MESSAGE": "لم نتمكن من حفظ قناة الرسائل القصيرة"
},
"BANDWIDTH": {
"ACCOUNT_ID": {
"LABEL": "معرف الحساب",
"PLACEHOLDER": "الرجاء إدخال معرف حساب النطاق الترددي الخاص بك",
"ERROR": "هذا الحقل مطلوب"
},
"API_KEY": {
"LABEL": "مفتاح API",
"PLACEHOLDER": "الرجاء إدخال مفتاح API الخاص بك",
"ERROR": "هذا الحقل مطلوب"
},
"API_SECRET": {
"LABEL": "سرية API",
"PLACEHOLDER": "الرجاء إدخال مفتاح API الخاص بك",
"ERROR": "هذا الحقل مطلوب"
},
"APPLICATION_ID": {
"LABEL": "معرف التطبيق",
"PLACEHOLDER": "الرجاء إدخال معرف تطبيق النطاق الترددي الخاص بك",
"ERROR": "هذا الحقل مطلوب"
},
"INBOX_NAME": {
"LABEL": "اسم صندوق الوارد لقناة التواصل",
"PLACEHOLDER": "الرجاء إدخال اسم القناة",
"ERROR": "هذا الحقل مطلوب"
},
"PHONE_NUMBER": {
"LABEL": "رقم الهاتف",
"PLACEHOLDER": "الرجاء إدخال رقم الهاتف الذي سيتم إرسال الرسائل منه.",
"ERROR": "الرجاء إدخال قيمة صحيحة. يجب أن يبدأ رقم الهاتف بعلامة `+`."
},
"SUBMIT_BUTTON": "إنشاء قناة عرض التردد",
"API": {
"ERROR_MESSAGE": "تعذر تكوين المصادقة بواسطة بيانات الاعتماد الخاصة بحسابك على Twilio، يرجى المحاولة مرة أخرى"
},
"API_CALLBACK": {
"TITLE": "عنوان Callback URL",
"SUBTITLE": "يتوجب تهيئة عنوان callback URL في إعدادات Twilio بإدخال القيمة أدناه."
}
}
}, },
"WHATSAPP": { "WHATSAPP": {
"TITLE": "قناة واتساب", "TITLE": "قناة واتساب",
@ -305,6 +353,14 @@
"ENABLED": "مفعل", "ENABLED": "مفعل",
"DISABLED": "معطّل" "DISABLED": "معطّل"
}, },
"ALLOW_MESSAGES_AFTER_RESOLVED": {
"ENABLED": "مفعل",
"DISABLED": "معطّل"
},
"ENABLE_CONTINUITY_VIA_EMAIL": {
"ENABLED": "مفعل",
"DISABLED": "معطّل"
},
"ENABLE_HMAC": { "ENABLE_HMAC": {
"LABEL": "تمكين" "LABEL": "تمكين"
} }
@ -351,6 +407,8 @@
"AUTO_ASSIGNMENT": "تفعيل الإسناد التلقائي", "AUTO_ASSIGNMENT": "تفعيل الإسناد التلقائي",
"ENABLE_CSAT": "تمكين تقييم خدمة العملاء", "ENABLE_CSAT": "تمكين تقييم خدمة العملاء",
"ENABLE_CSAT_SUB_TEXT": "تمكين/تعطيل تقييم خدمة العملاء بعد إنتهاء المحادثة", "ENABLE_CSAT_SUB_TEXT": "تمكين/تعطيل تقييم خدمة العملاء بعد إنتهاء المحادثة",
"ENABLE_CONTINUITY_VIA_EMAIL": "تمكين استمرارية المحادثة عبر البريد الإلكتروني",
"ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT": "المحادثات ستستمر عبر البريد الإلكتروني إذا كان عنوان البريد الإلكتروني لجهة الاتصال متاحاً.",
"INBOX_UPDATE_TITLE": "إعدادات قناة التواصل", "INBOX_UPDATE_TITLE": "إعدادات قناة التواصل",
"INBOX_UPDATE_SUB_TEXT": "تحديث إعدادات قناة التواصل", "INBOX_UPDATE_SUB_TEXT": "تحديث إعدادات قناة التواصل",
"AUTO_ASSIGNMENT_SUB_TEXT": "تمكين أو تعطيل الإسناد التلقائي للمحادثات الجديدة إلى الموظفين المضافين إلى قناة التواصل هذه.", "AUTO_ASSIGNMENT_SUB_TEXT": "تمكين أو تعطيل الإسناد التلقائي للمحادثات الجديدة إلى الموظفين المضافين إلى قناة التواصل هذه.",
@ -361,7 +419,9 @@
"INBOX_IDENTIFIER": "معرف صندوق الوارد", "INBOX_IDENTIFIER": "معرف صندوق الوارد",
"INBOX_IDENTIFIER_SUB_TEXT": "استخدم رمز 'inbox_identifier' المعروض هنا للمصادقة على عملاء API الخاص بك.", "INBOX_IDENTIFIER_SUB_TEXT": "استخدم رمز 'inbox_identifier' المعروض هنا للمصادقة على عملاء API الخاص بك.",
"FORWARD_EMAIL_TITLE": "إعادة التوجيه إلى البريد الإلكتروني", "FORWARD_EMAIL_TITLE": "إعادة التوجيه إلى البريد الإلكتروني",
"FORWARD_EMAIL_SUB_TEXT": "بدء إعادة توجيه رسائل البريد الإلكتروني الخاصة بك إلى عنوان البريد الإلكتروني التالي." "FORWARD_EMAIL_SUB_TEXT": "بدء إعادة توجيه رسائل البريد الإلكتروني الخاصة بك إلى عنوان البريد الإلكتروني التالي.",
"ALLOW_MESSAGES_AFTER_RESOLVED": "السماح بالرسائل بعد حل المحادثة",
"ALLOW_MESSAGES_AFTER_RESOLVED_SUB_TEXT": "السماح للمستخدمين النهائيين بإرسال رسائل حتى بعد تسوية المحادثة."
}, },
"FACEBOOK_REAUTHORIZE": { "FACEBOOK_REAUTHORIZE": {
"TITLE": "إعادة التصريح", "TITLE": "إعادة التصريح",

View file

@ -1,9 +1,12 @@
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json'; import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json'; import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _campaign } from './campaign.json'; import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json'; import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json'; import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json'; import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json'; import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json'; import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json'; import { default as _generalSettings } from './generalSettings.json';
@ -20,12 +23,15 @@ import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json'; import { default as _teamsSettings } from './teamsSettings.json';
export default { export default {
..._advancedFilters,
..._agentMgmt, ..._agentMgmt,
..._attributesMgmt, ..._attributesMgmt,
..._automation,
..._campaign, ..._campaign,
..._cannedMgmt, ..._cannedMgmt,
..._chatlist, ..._chatlist,
..._contact, ..._contact,
..._contactFilters,
..._conversation, ..._conversation,
..._csatMgmtMgmt, ..._csatMgmtMgmt,
..._generalSettings, ..._generalSettings,

View file

@ -4,8 +4,8 @@
"TITLE": "إعدادات الملف الشخصي", "TITLE": "إعدادات الملف الشخصي",
"BTN_TEXT": "تعديل الملف الشخصي", "BTN_TEXT": "تعديل الملف الشخصي",
"DELETE_AVATAR": "حذف الصورة الرمزية", "DELETE_AVATAR": "حذف الصورة الرمزية",
"AVATAR_DELETE_SUCCESS": "Avatar has been deleted successfully", "AVATAR_DELETE_SUCCESS": "تم حذف الصورة الرمزية بنجاح",
"AVATAR_DELETE_FAILED": "There is an error while deleting avatar, please try again", "AVATAR_DELETE_FAILED": "حدث خطأ أثناء حذف الصورة الرمزية، الرجاء المحاولة مرة أخرى",
"UPDATE_SUCCESS": "تم تحديث حسابك بنجاح", "UPDATE_SUCCESS": "تم تحديث حسابك بنجاح",
"PASSWORD_UPDATE_SUCCESS": "تم تغيير كلمة المرور بنجاح", "PASSWORD_UPDATE_SUCCESS": "تم تغيير كلمة المرور بنجاح",
"AFTER_EMAIL_CHANGED": "تم تحديث ملفك الشخصي بنجاح، الرجاء تسجيل الدخول مرة أخرى حيث أنه قد تم تغيير بيانات تسجيل الدخول الخاصة بك", "AFTER_EMAIL_CHANGED": "تم تحديث ملفك الشخصي بنجاح، الرجاء تسجيل الدخول مرة أخرى حيث أنه قد تم تغيير بيانات تسجيل الدخول الخاصة بك",
@ -89,7 +89,7 @@
"PLACEHOLDER": "الرجاء إدخال كلمة مرور جديدة" "PLACEHOLDER": "الرجاء إدخال كلمة مرور جديدة"
}, },
"PASSWORD": { "PASSWORD": {
"LABEL": "كلمة المرور", "LABEL": "كلمة مرور جديدة",
"ERROR": "الرجاء إدخال كلمة مرور بطول 6 أحرف أو أكثر", "ERROR": "الرجاء إدخال كلمة مرور بطول 6 أحرف أو أكثر",
"PLACEHOLDER": "الرجاء إدخال كلمة مرور جديدة" "PLACEHOLDER": "الرجاء إدخال كلمة مرور جديدة"
}, },
@ -103,7 +103,7 @@
"SIDEBAR_ITEMS": { "SIDEBAR_ITEMS": {
"CHANGE_AVAILABILITY_STATUS": "يتغيرون", "CHANGE_AVAILABILITY_STATUS": "يتغيرون",
"CHANGE_ACCOUNTS": "تبديل الحساب", "CHANGE_ACCOUNTS": "تبديل الحساب",
"CONTACT_SUPPORT": "Contact Support", "CONTACT_SUPPORT": "تواصل مع الدعم",
"SELECTOR_SUBTITLE": "اختر حساباً من القائمة التالية", "SELECTOR_SUBTITLE": "اختر حساباً من القائمة التالية",
"PROFILE_SETTINGS": "إعدادات الملف الشخصي", "PROFILE_SETTINGS": "إعدادات الملف الشخصي",
"KEYBOARD_SHORTCUTS": "اختصارات لوحة المفاتيح", "KEYBOARD_SHORTCUTS": "اختصارات لوحة المفاتيح",
@ -136,7 +136,7 @@
"SIDEBAR": { "SIDEBAR": {
"CONVERSATIONS": "المحادثات", "CONVERSATIONS": "المحادثات",
"ALL_CONVERSATIONS": "كل المحادثات", "ALL_CONVERSATIONS": "كل المحادثات",
"MENTIONED_CONVERSATIONS": "Mentions", "MENTIONED_CONVERSATIONS": "الإشارات",
"REPORTS": "التقارير", "REPORTS": "التقارير",
"SETTINGS": "الإعدادات", "SETTINGS": "الإعدادات",
"CONTACTS": "جهات الاتصال", "CONTACTS": "جهات الاتصال",
@ -153,11 +153,13 @@
"CUSTOM_ATTRIBUTES": "سمات مخصصة", "CUSTOM_ATTRIBUTES": "سمات مخصصة",
"AUTOMATION": "الأتمتة", "AUTOMATION": "الأتمتة",
"TEAMS": "الفرق", "TEAMS": "الفرق",
"CUSTOM_VIEWS_FOLDER": "المجلدات",
"CUSTOM_VIEWS_SEGMENTS": "الأجزاء",
"ALL_CONTACTS": "جميع جهات الاتصال", "ALL_CONTACTS": "جميع جهات الاتصال",
"TAGGED_WITH": "مشار إليه بواسطة", "TAGGED_WITH": "مشار إليه بواسطة",
"NEW_LABEL": "New label", "NEW_LABEL": "علامة جديدة",
"NEW_TEAM": "New team", "NEW_TEAM": "فريق جديد",
"NEW_INBOX": "New inbox", "NEW_INBOX": "صندوق الوارد الجديد",
"REPORTS_OVERVIEW": "نظرة عامة", "REPORTS_OVERVIEW": "نظرة عامة",
"CSAT": "CSAT", "CSAT": "CSAT",
"CAMPAIGNS": "الحملات", "CAMPAIGNS": "الحملات",
@ -167,7 +169,7 @@
"REPORTS_LABEL": "الوسوم", "REPORTS_LABEL": "الوسوم",
"REPORTS_INBOX": "صندوق الوارد", "REPORTS_INBOX": "صندوق الوارد",
"REPORTS_TEAM": "الفريق", "REPORTS_TEAM": "الفريق",
"SET_AVAILABILITY_TITLE": "Set yourself as" "SET_AVAILABILITY_TITLE": "تعيين نفسك كـ"
}, },
"CREATE_ACCOUNT": { "CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "أوه! لم نتمكن من العثور على الحساب. الرجاء إنشاء حساب جديد للمتابعة.", "NO_ACCOUNT_WARNING": "أوه! لم نتمكن من العثور على الحساب. الرجاء إنشاء حساب جديد للمتابعة.",

View file

@ -19,7 +19,9 @@
"contains": "Съдържа", "contains": "Съдържа",
"does_not_contain": "Не съдържа", "does_not_contain": "Не съдържа",
"is_present": "Присъства", "is_present": "Присъства",
"is_not_present": "Не присъства" "is_not_present": "Не присъства",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"STATUS": "Статус", "STATUS": "Статус",
@ -31,7 +33,54 @@
"LABELS": "Етикети", "LABELS": "Етикети",
"BROWSER_LANGUAGE": "Език на браузъра", "BROWSER_LANGUAGE": "Език на браузъра",
"COUNTRY_NAME": "Име на държавата", "COUNTRY_NAME": "Име на държавата",
"REFERER_LINK": "Референтна връзка" "REFERER_LINK": "Референтна връзка",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Персонализирани атрибути"
},
"CUSTOM_VIEWS": {
"ADD": {
"TITLE": "Do you want to save this filter?",
"LABEL": "Name this filter",
"PLACEHOLDER": "Enter a name for this filter",
"ERROR_MESSAGE": "Name is required",
"SAVE_BUTTON": "Save filter",
"CANCEL_BUTTON": "Отмени",
"API_FOLDERS": {
"SUCCESS_MESSAGE": "Folder created successfully",
"ERROR_MESSAGE": "Error while creating folder"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "Segment created successfully",
"ERROR_MESSAGE": "Error while creating segment"
}
},
"DELETE": {
"DELETE_BUTTON": "Delete filter",
"MODAL": {
"CONFIRM": {
"TITLE": "Потвърди изтриването",
"MESSAGE": "Are you sure to delete the filter ",
"YES": "Да, изтрий",
"NO": "No, Keep it"
}
},
"API_FOLDERS": {
"SUCCESS_MESSAGE": "Folder deleted successfully",
"ERROR_MESSAGE": "Error while deleting folder"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "Segment deleted successfully",
"ERROR_MESSAGE": "Error while deleting segment"
}
}
} }
} }
} }

View file

@ -1,6 +1,89 @@
{ {
"AUTOMATION": { "AUTOMATION": {
"HEADER": "Автоматизация", "HEADER": "Автоматизация",
"HEADER_BTN_TXT": "Добавяне правило за автоматизация" "HEADER_BTN_TXT": "Добавяне правило за автоматизация",
"LOADING": "Fetching automation rules",
"SIDEBAR_TXT": "<p><b>Automation Rules</b> <p>Automation can replace and automate existing processes that require manual effort. You can do many things with automation, including adding labels and assigning conversation to the best agent. So the team focuses on what they do best and spends more little time on manual tasks.</p>",
"ADD": {
"TITLE": "Добавяне правило за автоматизация",
"SUBMIT": "Създаване",
"CANCEL_BUTTON_TEXT": "Отмени",
"FORM": {
"NAME": {
"LABEL": "Rule Name",
"PLACEHOLDER": "Enter rule name",
"ERROR": "Name is required"
},
"DESC": {
"LABEL": "Описание",
"PLACEHOLDER": "Enter rule description",
"ERROR": "Description is required"
},
"EVENT": {
"LABEL": "Event",
"PLACEHOLDER": "Please select one",
"ERROR": "Event is required"
},
"CONDITIONS": {
"LABEL": "Conditions"
},
"ACTIONS": {
"LABEL": "Действия"
}
},
"CONDITION_BUTTON_LABEL": "Add Condition",
"ACTION_BUTTON_LABEL": "Add Action",
"API": {
"SUCCESS_MESSAGE": "Automation rule added successfully",
"ERROR_MESSAGE": "Could not able to create a automation rule, Please try again later"
}
},
"LIST": {
"TABLE_HEADER": [
"Име",
"Описание",
"Активен",
"Created on"
],
"404": "No automation rules found"
},
"DELETE": {
"TITLE": "Delete Automation Rule",
"SUBMIT": "Изтрий",
"CANCEL_BUTTON_TEXT": "Отмени",
"CONFIRM": {
"TITLE": "Потвърди изтриването",
"MESSAGE": "Сигурни ли сте за изтриването ",
"YES": "Да, изтрий ",
"NO": "Не, запази "
},
"API": {
"SUCCESS_MESSAGE": "Automation rule deleted successfully",
"ERROR_MESSAGE": "Could not able to delete a automation rule, Please try again later"
}
},
"EDIT": {
"TITLE": "Edit Automation Rule",
"SUBMIT": "Редактирай",
"CANCEL_BUTTON_TEXT": "Отмени",
"API": {
"SUCCESS_MESSAGE": "Automation rule updated successfully",
"ERROR_MESSAGE": "Could not update automation rule, Please try again later"
}
},
"CLONE": {
"TOOLTIP": "Clone",
"API": {
"SUCCESS_MESSAGE": "Automation cloned successfully",
"ERROR_MESSAGE": "Could not clone automation rule, Please try again later"
}
},
"FORM": {
"EDIT": "Редактирай",
"CREATE": "Създаване",
"DELETE": "Изтрий",
"CANCEL": "Отмени",
"RESET_MESSAGE": "Changing event type will reset the conditions and events you have added below"
}
} }
} }

View file

@ -4,7 +4,7 @@
"HEADER_BTN_TXT": "Добавяне на готов отговор", "HEADER_BTN_TXT": "Добавяне на готов отговор",
"LOADING": "Извличане на готови отговори", "LOADING": "Извличане на готови отговори",
"SEARCH_404": "Няма резултати отговарящи на тази заявка", "SEARCH_404": "Няма резултати отговарящи на тази заявка",
"SIDEBAR_TXT": "<p><b>Готови отговори</b> </p><p> Готовите отговори са запазени шаблони за отговори, които могат да се използват за бързо изпращане на отговор в разговора. </p><p> За да създадете готов отговор, просто щракнете върху <b>Добавяне на готов отговор</b>. Можете също да редактирате или изтриете съществуващ готов отговор, като щракнете върху бутона Редактиране или Изтриване </p><p> Готовите отговори се използват с помощта на <b>Кратки кодове</b>. Агентите имат достъп до готовите отговори, докато са в чат, като напишат <b>'/'</b>, последвано от краткия код. </p>", "SIDEBAR_TXT": "<p><b>Canned Responses</b> </p><p> Canned Responses are saved reply templates which can be used to quickly send out a reply to a conversation. </p><p> For creating a Canned Response, just click on the <b>Add Canned Response</b>. You can also edit or delete an existing Canned Response by clicking on the Edit or Delete button </p><p> Canned responses are used with the help of <b>Short Codes</b>. Agents can access canned responses while on a chat by typing <b>'/'</b> followed by the short code. </p>",
"LIST": { "LIST": {
"404": "Няма налични готови отговори в този акаунт.", "404": "Няма налични готови отговори в този акаунт.",
"TITLE": "Управлявайте готовите отговори", "TITLE": "Управлявайте готовите отговори",
@ -17,12 +17,12 @@
}, },
"ADD": { "ADD": {
"TITLE": "Добавяне на готов отговор", "TITLE": "Добавяне на готов отговор",
"DESC": "Готовите отговори са предварително дефинирани шаблони за отговор, които могат да се изпращат бързо в чата.", "DESC": "Canned Responses are saved reply templates which can be used to quickly send out reply to conversation.",
"CANCEL_BUTTON_TEXT": "Отмени", "CANCEL_BUTTON_TEXT": "Отмени",
"FORM": { "FORM": {
"SHORT_CODE": { "SHORT_CODE": {
"LABEL": "Кратък код", "LABEL": "Кратък код",
"PLACEHOLDER": "Моля, въведете кратък код", "PLACEHOLDER": "Please enter a short code",
"ERROR": "Краткия код е задължителен" "ERROR": "Краткия код е задължителен"
}, },
"CONTENT": { "CONTENT": {

View file

@ -169,6 +169,7 @@
"SUBMIT": "Изпрати съобщение", "SUBMIT": "Изпрати съобщение",
"CANCEL": "Отмени", "CANCEL": "Отмени",
"SUCCESS_MESSAGE": "Съобщението е изпратено!", "SUCCESS_MESSAGE": "Съобщението е изпратено!",
"GO_TO_CONVERSATION": "View",
"ERROR_MESSAGE": "Не може да се изпрати! Опитайте пак" "ERROR_MESSAGE": "Не може да се изпрати! Опитайте пак"
} }
}, },
@ -178,6 +179,8 @@
"SEARCH_BUTTON": "Търсене", "SEARCH_BUTTON": "Търсене",
"SEARCH_INPUT_PLACEHOLDER": "Търсене на контакти", "SEARCH_INPUT_PLACEHOLDER": "Търсене на контакти",
"FILTER_CONTACTS": "Филтър", "FILTER_CONTACTS": "Филтър",
"FILTER_CONTACTS_SAVE": "Save filter",
"FILTER_CONTACTS_DELETE": "Delete filter",
"LIST": { "LIST": {
"LOADING_MESSAGE": "Зареждане на контактите...", "LOADING_MESSAGE": "Зареждане на контактите...",
"404": "Няма контакти отговарящи на търсенети ви 🔍", "404": "Няма контакти отговарящи на търсенети ви 🔍",

View file

@ -20,7 +20,9 @@
"contains": "Съдържа", "contains": "Съдържа",
"does_not_contain": "Не съдържа", "does_not_contain": "Не съдържа",
"is_present": "Присъства", "is_present": "Присъства",
"is_not_present": "Не присъства" "is_not_present": "Не присъства",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"NAME": "Име", "NAME": "Име",
@ -28,7 +30,17 @@
"PHONE_NUMBER": "Телефон", "PHONE_NUMBER": "Телефон",
"IDENTIFIER": "Идентификатор", "IDENTIFIER": "Идентификатор",
"CITY": "Град", "CITY": "Град",
"COUNTRY": "Държава" "COUNTRY": "Държава",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Персонализирани атрибути"
} }
} }
} }

View file

@ -22,6 +22,8 @@
"LOADING_CONVERSATIONS": "Loading Conversations", "LOADING_CONVERSATIONS": "Loading Conversations",
"CANNOT_REPLY": "You cannot reply due to", "CANNOT_REPLY": "You cannot reply due to",
"24_HOURS_WINDOW": "24 hour message window restriction", "24_HOURS_WINDOW": "24 hour message window restriction",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to", "TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"SELECT_A_TWEET_TO_REPLY": "Please select a tweet to reply to.", "SELECT_A_TWEET_TO_REPLY": "Please select a tweet to reply to.",
@ -90,6 +92,9 @@
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit", "FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit",
"MESSAGE_ERROR": "Unable to send this message, please try again later", "MESSAGE_ERROR": "Unable to send this message, please try again later",
"SENT_BY": "Sent by:", "SENT_BY": "Sent by:",
"BOT": "Бот",
"SEND_FAILED": "Couldn't send message! Try again",
"TRY_AGAIN": "retry",
"ASSIGNMENT": { "ASSIGNMENT": {
"SELECT_AGENT": "Select Agent", "SELECT_AGENT": "Select Agent",
"REMOVE": "Remove", "REMOVE": "Remove",
@ -127,7 +132,7 @@
}, },
"TEAM_MEMBERS": { "TEAM_MEMBERS": {
"TITLE": "Invite your team members", "TITLE": "Invite your team members",
"DESCRIPTION": "Since you are getting ready to talk to your customer, bring in your teammates to assist you. You can invite your teammates by adding their email address to the agent list.", "DESCRIPTION": "Since you are getting ready to talk to your customer, bring in your teammates to assist you. You can invite your teammates by adding their email addresses to the agent list.",
"NEW_LINK": "Click here to invite a team member" "NEW_LINK": "Click here to invite a team member"
}, },
"INBOXES": { "INBOXES": {

View file

@ -82,7 +82,7 @@
}, },
"CHANNEL_GREETING_TOGGLE": { "CHANNEL_GREETING_TOGGLE": {
"LABEL": "Enable channel greeting", "LABEL": "Enable channel greeting",
"HELP_TEXT": "Send a greeting message to the user when he starts the conversation.", "HELP_TEXT": "Send a greeting message to the users when they starts the conversation.",
"ENABLED": "Enabled", "ENABLED": "Enabled",
"DISABLED": "Disabled" "DISABLED": "Disabled"
}, },
@ -136,8 +136,56 @@
} }
}, },
"SMS": { "SMS": {
"TITLE": "SMS Channel via Twilio", "TITLE": "SMS Channel",
"DESC": "Start supporting your customers via SMS with Twilio integration." "DESC": "Start supporting your customers via SMS.",
"PROVIDERS": {
"LABEL": "API Provider",
"TWILIO": "Twilio",
"BANDWIDTH": "Bandwidth"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the SMS channel"
},
"BANDWIDTH": {
"ACCOUNT_ID": {
"LABEL": "Account ID",
"PLACEHOLDER": "Please enter your Bandwidth Account ID",
"ERROR": "This field is required"
},
"API_KEY": {
"LABEL": "API Key",
"PLACEHOLDER": "Please enter your Bandwith API Key",
"ERROR": "This field is required"
},
"API_SECRET": {
"LABEL": "API Secret",
"PLACEHOLDER": "Please enter your Bandwith API Secret",
"ERROR": "This field is required"
},
"APPLICATION_ID": {
"LABEL": "Application ID",
"PLACEHOLDER": "Please enter your Bandwidth Application ID",
"ERROR": "This field is required"
},
"INBOX_NAME": {
"LABEL": "Име на входящата кутия",
"PLACEHOLDER": "Please enter a inbox name",
"ERROR": "This field is required"
},
"PHONE_NUMBER": {
"LABEL": "Телефон",
"PLACEHOLDER": "Please enter the phone number from which message will be sent.",
"ERROR": "Please enter a valid value. Phone number should start with `+` sign."
},
"SUBMIT_BUTTON": "Create Bandwidth Channel",
"API": {
"ERROR_MESSAGE": "We were not able to authenticate Bandwidth credentials, please try again"
},
"API_CALLBACK": {
"TITLE": "Callback URL",
"SUBTITLE": "You have to configure the message callback URL in Bandwidth with the URL mentioned here."
}
}
}, },
"WHATSAPP": { "WHATSAPP": {
"TITLE": "WhatsApp Channel", "TITLE": "WhatsApp Channel",
@ -305,6 +353,14 @@
"ENABLED": "Enabled", "ENABLED": "Enabled",
"DISABLED": "Disabled" "DISABLED": "Disabled"
}, },
"ALLOW_MESSAGES_AFTER_RESOLVED": {
"ENABLED": "Включен",
"DISABLED": "Изключен"
},
"ENABLE_CONTINUITY_VIA_EMAIL": {
"ENABLED": "Включен",
"DISABLED": "Изключен"
},
"ENABLE_HMAC": { "ENABLE_HMAC": {
"LABEL": "Enable" "LABEL": "Enable"
} }
@ -351,6 +407,8 @@
"AUTO_ASSIGNMENT": "Enable auto assignment", "AUTO_ASSIGNMENT": "Enable auto assignment",
"ENABLE_CSAT": "Enable CSAT", "ENABLE_CSAT": "Enable CSAT",
"ENABLE_CSAT_SUB_TEXT": "Enable/Disable CSAT(Customer satisfaction) survey after resolving a conversation", "ENABLE_CSAT_SUB_TEXT": "Enable/Disable CSAT(Customer satisfaction) survey after resolving a conversation",
"ENABLE_CONTINUITY_VIA_EMAIL": "Enable conversation continuity via email",
"ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT": "Conversations will continue over email if the contact email address is available.",
"INBOX_UPDATE_TITLE": "Inbox Settings", "INBOX_UPDATE_TITLE": "Inbox Settings",
"INBOX_UPDATE_SUB_TEXT": "Update your inbox settings", "INBOX_UPDATE_SUB_TEXT": "Update your inbox settings",
"AUTO_ASSIGNMENT_SUB_TEXT": "Enable or disable the automatic assignment of new conversations to the agents added to this inbox.", "AUTO_ASSIGNMENT_SUB_TEXT": "Enable or disable the automatic assignment of new conversations to the agents added to this inbox.",
@ -361,7 +419,9 @@
"INBOX_IDENTIFIER": "Inbox Identifier", "INBOX_IDENTIFIER": "Inbox Identifier",
"INBOX_IDENTIFIER_SUB_TEXT": "Use the `inbox_identifier` token shown here to authentication your API clients.", "INBOX_IDENTIFIER_SUB_TEXT": "Use the `inbox_identifier` token shown here to authentication your API clients.",
"FORWARD_EMAIL_TITLE": "Forward to Email", "FORWARD_EMAIL_TITLE": "Forward to Email",
"FORWARD_EMAIL_SUB_TEXT": "Start forwarding your emails to the following email address." "FORWARD_EMAIL_SUB_TEXT": "Start forwarding your emails to the following email address.",
"ALLOW_MESSAGES_AFTER_RESOLVED": "Allow messages after conversation resolved",
"ALLOW_MESSAGES_AFTER_RESOLVED_SUB_TEXT": "Allow the end-users to send messages even after the conversation is resolved."
}, },
"FACEBOOK_REAUTHORIZE": { "FACEBOOK_REAUTHORIZE": {
"TITLE": "Reauthorize", "TITLE": "Reauthorize",

View file

@ -0,0 +1,49 @@
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
export default {
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
};

View file

@ -89,14 +89,14 @@
"PLACEHOLDER": "Please enter the current password" "PLACEHOLDER": "Please enter the current password"
}, },
"PASSWORD": { "PASSWORD": {
"LABEL": "Password", "LABEL": "New password",
"ERROR": "Please enter a password of length 6 or more", "ERROR": "Please enter a password of length 6 or more",
"PLACEHOLDER": "Please enter a new password" "PLACEHOLDER": "Please enter a new password"
}, },
"PASSWORD_CONFIRMATION": { "PASSWORD_CONFIRMATION": {
"LABEL": "Confirm new password", "LABEL": "Confirm new password",
"ERROR": "Confirm password should match the password", "ERROR": "Confirm password should match the password",
"PLACEHOLDER": "Please re-enter your password" "PLACEHOLDER": "Please re-enter your new password"
} }
} }
}, },
@ -153,6 +153,8 @@
"CUSTOM_ATTRIBUTES": "Персонализирани атрибути", "CUSTOM_ATTRIBUTES": "Персонализирани атрибути",
"AUTOMATION": "Автоматизация", "AUTOMATION": "Автоматизация",
"TEAMS": "Teams", "TEAMS": "Teams",
"CUSTOM_VIEWS_FOLDER": "Folders",
"CUSTOM_VIEWS_SEGMENTS": "Segments",
"ALL_CONTACTS": "All Contacts", "ALL_CONTACTS": "All Contacts",
"TAGGED_WITH": "Tagged with", "TAGGED_WITH": "Tagged with",
"NEW_LABEL": "New label", "NEW_LABEL": "New label",

View file

@ -2,7 +2,7 @@
"TEAMS_SETTINGS": { "TEAMS_SETTINGS": {
"NEW_TEAM": "Create new team", "NEW_TEAM": "Create new team",
"HEADER": "Teams", "HEADER": "Teams",
"SIDEBAR_TXT": "<p><b>Teams</b></p> <p>Teams let you organize your agents into groups based on their responsibilities. <br /> A user can be part of multiple teams. You can assign conversations to a team when you are working collaboratively. </p>", "SIDEBAR_TXT": "<p><b>Teams</b></p> <p>Teams let you organize your agents into groups based on their responsibilities. <br /> An agent can be part of multiple teams. You can assign conversations to a team when you are working collaboratively. </p>",
"LIST": { "LIST": {
"404": "There are no teams created on this account.", "404": "There are no teams created on this account.",
"EDIT_TEAM": "Edit team" "EDIT_TEAM": "Edit team"

View file

@ -19,7 +19,9 @@
"contains": "Contains", "contains": "Contains",
"does_not_contain": "Does not contain", "does_not_contain": "Does not contain",
"is_present": "Is present", "is_present": "Is present",
"is_not_present": "Is not present" "is_not_present": "Is not present",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"STATUS": "Estat", "STATUS": "Estat",
@ -31,7 +33,54 @@
"LABELS": "Etiquetes", "LABELS": "Etiquetes",
"BROWSER_LANGUAGE": "Browser Language", "BROWSER_LANGUAGE": "Browser Language",
"COUNTRY_NAME": "Country Name", "COUNTRY_NAME": "Country Name",
"REFERER_LINK": "Referer link" "REFERER_LINK": "Referer link",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Atributs personalitzats"
},
"CUSTOM_VIEWS": {
"ADD": {
"TITLE": "Do you want to save this filter?",
"LABEL": "Name this filter",
"PLACEHOLDER": "Enter a name for this filter",
"ERROR_MESSAGE": "Name is required",
"SAVE_BUTTON": "Save filter",
"CANCEL_BUTTON": "Cancel·la",
"API_FOLDERS": {
"SUCCESS_MESSAGE": "Folder created successfully",
"ERROR_MESSAGE": "Error while creating folder"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "Segment created successfully",
"ERROR_MESSAGE": "Error while creating segment"
}
},
"DELETE": {
"DELETE_BUTTON": "Delete filter",
"MODAL": {
"CONFIRM": {
"TITLE": "Confirma l'esborrat",
"MESSAGE": "Are you sure to delete the filter ",
"YES": "Si, esborra",
"NO": "No, manten-la"
}
},
"API_FOLDERS": {
"SUCCESS_MESSAGE": "Folder deleted successfully",
"ERROR_MESSAGE": "Error while deleting folder"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "Segment deleted successfully",
"ERROR_MESSAGE": "Error while deleting segment"
}
}
} }
} }
} }

View file

@ -90,12 +90,12 @@
} }
}, },
"SEARCH": { "SEARCH": {
"NO_RESULTS": "No results found." "NO_RESULTS": "No s'ha trobat agents."
}, },
"MULTI_SELECTOR": { "MULTI_SELECTOR": {
"PLACEHOLDER": "None", "PLACEHOLDER": "Ningú",
"TITLE": { "TITLE": {
"AGENT": "Select agent", "AGENT": "Seleccionar Agent",
"TEAM": "Select team" "TEAM": "Select team"
}, },
"SEARCH": { "SEARCH": {

View file

@ -1,6 +1,89 @@
{ {
"AUTOMATION": { "AUTOMATION": {
"HEADER": "Automation", "HEADER": "Automation",
"HEADER_BTN_TXT": "Add Automation Rule" "HEADER_BTN_TXT": "Add Automation Rule",
"LOADING": "Fetching automation rules",
"SIDEBAR_TXT": "<p><b>Automation Rules</b> <p>Automation can replace and automate existing processes that require manual effort. You can do many things with automation, including adding labels and assigning conversation to the best agent. So the team focuses on what they do best and spends more little time on manual tasks.</p>",
"ADD": {
"TITLE": "Add Automation Rule",
"SUBMIT": "Crear",
"CANCEL_BUTTON_TEXT": "Cancel·la",
"FORM": {
"NAME": {
"LABEL": "Rule Name",
"PLACEHOLDER": "Enter rule name",
"ERROR": "Name is required"
},
"DESC": {
"LABEL": "Descripció",
"PLACEHOLDER": "Enter rule description",
"ERROR": "Description is required"
},
"EVENT": {
"LABEL": "Event",
"PLACEHOLDER": "Please select one",
"ERROR": "Event is required"
},
"CONDITIONS": {
"LABEL": "Conditions"
},
"ACTIONS": {
"LABEL": "Accions"
}
},
"CONDITION_BUTTON_LABEL": "Add Condition",
"ACTION_BUTTON_LABEL": "Add Action",
"API": {
"SUCCESS_MESSAGE": "Automation rule added successfully",
"ERROR_MESSAGE": "Could not able to create a automation rule, Please try again later"
}
},
"LIST": {
"TABLE_HEADER": [
"Nom",
"Descripció",
"Active",
"Created on"
],
"404": "No automation rules found"
},
"DELETE": {
"TITLE": "Delete Automation Rule",
"SUBMIT": "Esborrar",
"CANCEL_BUTTON_TEXT": "Cancel·la",
"CONFIRM": {
"TITLE": "Confirma l'esborrat",
"MESSAGE": "N'estas segur? ",
"YES": "Si, esborra ",
"NO": "No, segueix "
},
"API": {
"SUCCESS_MESSAGE": "Automation rule deleted successfully",
"ERROR_MESSAGE": "Could not able to delete a automation rule, Please try again later"
}
},
"EDIT": {
"TITLE": "Edit Automation Rule",
"SUBMIT": "Edita",
"CANCEL_BUTTON_TEXT": "Cancel·la",
"API": {
"SUCCESS_MESSAGE": "Automation rule updated successfully",
"ERROR_MESSAGE": "Could not update automation rule, Please try again later"
}
},
"CLONE": {
"TOOLTIP": "Clone",
"API": {
"SUCCESS_MESSAGE": "Automation cloned successfully",
"ERROR_MESSAGE": "Could not clone automation rule, Please try again later"
}
},
"FORM": {
"EDIT": "Edita",
"CREATE": "Crear",
"DELETE": "Esborrar",
"CANCEL": "Cancel·la",
"RESET_MESSAGE": "Changing event type will reset the conditions and events you have added below"
}
} }
} }

View file

@ -4,7 +4,7 @@
"HEADER_BTN_TXT": "Afegeix una resposta predeterminada", "HEADER_BTN_TXT": "Afegeix una resposta predeterminada",
"LOADING": "S'estan recollint les respostes predeterminades", "LOADING": "S'estan recollint les respostes predeterminades",
"SEARCH_404": "No hi ha cap resposta que coincideixi amb aquesta consulta", "SEARCH_404": "No hi ha cap resposta que coincideixi amb aquesta consulta",
"SIDEBAR_TXT": "<p><b>Respostes predeterminades</b> </p><p> Les respostes predeterminades són plantilles de resposta que es poden utilitzar per enviar ràpidament una resposta a una conversa .</p><p> Per crear una Resposta Predeterminada, clica en <b>Afegir Resposta Predeterminada</b>. També pots editar o suprimir una resposta predeterminada fent clic al botó Edita o Suprimeix </p><p> Les respostes predeterminades s'utilitzen amb l'ajuda dels <b>Codi curt</b>. Els agents poden accedir a les respostes predeterminades en un xat escrivint <b>'/'</b> seguit del codi curt. </p>", "SIDEBAR_TXT": "<p><b>Canned Responses</b> </p><p> Canned Responses are saved reply templates which can be used to quickly send out a reply to a conversation. </p><p> For creating a Canned Response, just click on the <b>Add Canned Response</b>. You can also edit or delete an existing Canned Response by clicking on the Edit or Delete button </p><p> Canned responses are used with the help of <b>Short Codes</b>. Agents can access canned responses while on a chat by typing <b>'/'</b> followed by the short code. </p>",
"LIST": { "LIST": {
"404": "No hi ha respostes predeterminades disponibles en aquest compte.", "404": "No hi ha respostes predeterminades disponibles en aquest compte.",
"TITLE": "Gestiona les respostes predeterminades", "TITLE": "Gestiona les respostes predeterminades",
@ -17,12 +17,12 @@
}, },
"ADD": { "ADD": {
"TITLE": "Afegeix Resposta Predeterminada", "TITLE": "Afegeix Resposta Predeterminada",
"DESC": "Les respostes predeterminades són plantilles de resposta que es poden utilitzar per enviar ràpidament les respostes a les converses.", "DESC": "Canned Responses are saved reply templates which can be used to quickly send out reply to conversation.",
"CANCEL_BUTTON_TEXT": "Cancel·la", "CANCEL_BUTTON_TEXT": "Cancel·la",
"FORM": { "FORM": {
"SHORT_CODE": { "SHORT_CODE": {
"LABEL": "Codi curt", "LABEL": "Codi curt",
"PLACEHOLDER": "Introduïu un codi curt", "PLACEHOLDER": "Please enter a short code",
"ERROR": "És necessari el codi curt" "ERROR": "És necessari el codi curt"
}, },
"CONTENT": { "CONTENT": {

View file

@ -169,6 +169,7 @@
"SUBMIT": "Send message", "SUBMIT": "Send message",
"CANCEL": "Cancel·la", "CANCEL": "Cancel·la",
"SUCCESS_MESSAGE": "Message sent!", "SUCCESS_MESSAGE": "Message sent!",
"GO_TO_CONVERSATION": "Veure",
"ERROR_MESSAGE": "Couldn't send! try again" "ERROR_MESSAGE": "Couldn't send! try again"
} }
}, },
@ -178,6 +179,8 @@
"SEARCH_BUTTON": "Cercar", "SEARCH_BUTTON": "Cercar",
"SEARCH_INPUT_PLACEHOLDER": "Cerca de contactes", "SEARCH_INPUT_PLACEHOLDER": "Cerca de contactes",
"FILTER_CONTACTS": "Filter", "FILTER_CONTACTS": "Filter",
"FILTER_CONTACTS_SAVE": "Save filter",
"FILTER_CONTACTS_DELETE": "Delete filter",
"LIST": { "LIST": {
"LOADING_MESSAGE": "Carregant contactes...", "LOADING_MESSAGE": "Carregant contactes...",
"404": "No hi ha cap contacte que coincideixi amb la vostra cerca 🔍", "404": "No hi ha cap contacte que coincideixi amb la vostra cerca 🔍",

View file

@ -20,7 +20,9 @@
"contains": "Contains", "contains": "Contains",
"does_not_contain": "Does not contain", "does_not_contain": "Does not contain",
"is_present": "Is present", "is_present": "Is present",
"is_not_present": "Is not present" "is_not_present": "Is not present",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"NAME": "Nom", "NAME": "Nom",
@ -28,7 +30,17 @@
"PHONE_NUMBER": "Número de telèfon", "PHONE_NUMBER": "Número de telèfon",
"IDENTIFIER": "Identifier", "IDENTIFIER": "Identifier",
"CITY": "City", "CITY": "City",
"COUNTRY": "Country" "COUNTRY": "Country",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Atributs personalitzats"
} }
} }
} }

View file

@ -22,6 +22,8 @@
"LOADING_CONVERSATIONS": "S'estan carregant les converses", "LOADING_CONVERSATIONS": "S'estan carregant les converses",
"CANNOT_REPLY": "No pots respondre degut a", "CANNOT_REPLY": "No pots respondre degut a",
"24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores", "24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to", "TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores",
"SELECT_A_TWEET_TO_REPLY": "Please select a tweet to reply to.", "SELECT_A_TWEET_TO_REPLY": "Please select a tweet to reply to.",
@ -90,6 +92,9 @@
"FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit", "FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_FILE_UPLOAD_SIZE} attachment limit",
"MESSAGE_ERROR": "Unable to send this message, please try again later", "MESSAGE_ERROR": "Unable to send this message, please try again later",
"SENT_BY": "Enviat per:", "SENT_BY": "Enviat per:",
"BOT": "Bot",
"SEND_FAILED": "Couldn't send message! Try again",
"TRY_AGAIN": "retry",
"ASSIGNMENT": { "ASSIGNMENT": {
"SELECT_AGENT": "Seleccionar Agent", "SELECT_AGENT": "Seleccionar Agent",
"REMOVE": "Suprimeix", "REMOVE": "Suprimeix",
@ -127,7 +132,7 @@
}, },
"TEAM_MEMBERS": { "TEAM_MEMBERS": {
"TITLE": "Invite your team members", "TITLE": "Invite your team members",
"DESCRIPTION": "Since you are getting ready to talk to your customer, bring in your teammates to assist you. You can invite your teammates by adding their email address to the agent list.", "DESCRIPTION": "Since you are getting ready to talk to your customer, bring in your teammates to assist you. You can invite your teammates by adding their email addresses to the agent list.",
"NEW_LINK": "Click here to invite a team member" "NEW_LINK": "Click here to invite a team member"
}, },
"INBOXES": { "INBOXES": {

View file

@ -40,7 +40,7 @@
"AUTO_RESOLVE_DURATION": { "AUTO_RESOLVE_DURATION": {
"LABEL": "El nombre de dies després que un ticket es resolgui automàticament si no hi ha activitat", "LABEL": "El nombre de dies després que un ticket es resolgui automàticament si no hi ha activitat",
"PLACEHOLDER": "30", "PLACEHOLDER": "30",
"ERROR": "Introdueix una durada vàlida de resolució automàtica (mínim 1 dia)" "ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
}, },
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "La continuïtat de converses amb correus electrònics està habilitada per al vostre compte.", "INBOUND_EMAIL_ENABLED": "La continuïtat de converses amb correus electrònics està habilitada per al vostre compte.",

View file

@ -82,7 +82,7 @@
}, },
"CHANNEL_GREETING_TOGGLE": { "CHANNEL_GREETING_TOGGLE": {
"LABEL": "Activa la salutació del canal", "LABEL": "Activa la salutació del canal",
"HELP_TEXT": "Envia un missatge de felicitació a l'usuari quan comenci la conversa.", "HELP_TEXT": "Send a greeting message to the users when they starts the conversation.",
"ENABLED": "Habilita", "ENABLED": "Habilita",
"DISABLED": "Inhabilita" "DISABLED": "Inhabilita"
}, },
@ -136,8 +136,56 @@
} }
}, },
"SMS": { "SMS": {
"TITLE": "SMS Channel via Twilio", "TITLE": "SMS Channel",
"DESC": "Start supporting your customers via SMS with Twilio integration." "DESC": "Start supporting your customers via SMS.",
"PROVIDERS": {
"LABEL": "API Provider",
"TWILIO": "Twilio",
"BANDWIDTH": "Bandwidth"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the SMS channel"
},
"BANDWIDTH": {
"ACCOUNT_ID": {
"LABEL": "Account ID",
"PLACEHOLDER": "Please enter your Bandwidth Account ID",
"ERROR": "Aquest camp és obligatori"
},
"API_KEY": {
"LABEL": "API Key",
"PLACEHOLDER": "Please enter your Bandwith API Key",
"ERROR": "Aquest camp és obligatori"
},
"API_SECRET": {
"LABEL": "API Secret",
"PLACEHOLDER": "Please enter your Bandwith API Secret",
"ERROR": "Aquest camp és obligatori"
},
"APPLICATION_ID": {
"LABEL": "Application ID",
"PLACEHOLDER": "Please enter your Bandwidth Application ID",
"ERROR": "Aquest camp és obligatori"
},
"INBOX_NAME": {
"LABEL": "Nom de la safata d'entrada",
"PLACEHOLDER": "Please enter a inbox name",
"ERROR": "Aquest camp és obligatori"
},
"PHONE_NUMBER": {
"LABEL": "Número de telèfon",
"PLACEHOLDER": "Introduïu el número de telèfon des del qual serà enviat el missatge.",
"ERROR": "Introduïu un valor vàlid. El número de telèfon hauria de començar amb el signe `+`."
},
"SUBMIT_BUTTON": "Create Bandwidth Channel",
"API": {
"ERROR_MESSAGE": "We were not able to authenticate Bandwidth credentials, please try again"
},
"API_CALLBACK": {
"TITLE": "Callback URL",
"SUBTITLE": "You have to configure the message callback URL in Bandwidth with the URL mentioned here."
}
}
}, },
"WHATSAPP": { "WHATSAPP": {
"TITLE": "WhatsApp Channel", "TITLE": "WhatsApp Channel",
@ -305,6 +353,14 @@
"ENABLED": "Habilita", "ENABLED": "Habilita",
"DISABLED": "Inhabilita" "DISABLED": "Inhabilita"
}, },
"ALLOW_MESSAGES_AFTER_RESOLVED": {
"ENABLED": "Habilita",
"DISABLED": "Inhabilita"
},
"ENABLE_CONTINUITY_VIA_EMAIL": {
"ENABLED": "Habilita",
"DISABLED": "Inhabilita"
},
"ENABLE_HMAC": { "ENABLE_HMAC": {
"LABEL": "Enable" "LABEL": "Enable"
} }
@ -351,6 +407,8 @@
"AUTO_ASSIGNMENT": "Activa l'assignació automàtica", "AUTO_ASSIGNMENT": "Activa l'assignació automàtica",
"ENABLE_CSAT": "Enable CSAT", "ENABLE_CSAT": "Enable CSAT",
"ENABLE_CSAT_SUB_TEXT": "Enable/Disable CSAT(Customer satisfaction) survey after resolving a conversation", "ENABLE_CSAT_SUB_TEXT": "Enable/Disable CSAT(Customer satisfaction) survey after resolving a conversation",
"ENABLE_CONTINUITY_VIA_EMAIL": "Enable conversation continuity via email",
"ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT": "Conversations will continue over email if the contact email address is available.",
"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",
@ -361,7 +419,9 @@
"INBOX_IDENTIFIER": "Inbox Identifier", "INBOX_IDENTIFIER": "Inbox Identifier",
"INBOX_IDENTIFIER_SUB_TEXT": "Use the `inbox_identifier` token shown here to authentication your API clients.", "INBOX_IDENTIFIER_SUB_TEXT": "Use the `inbox_identifier` token shown here to authentication your API clients.",
"FORWARD_EMAIL_TITLE": "Forward to Email", "FORWARD_EMAIL_TITLE": "Forward to Email",
"FORWARD_EMAIL_SUB_TEXT": "Comença a reenviar els teus correus electrònics a la següent adreça electrònica." "FORWARD_EMAIL_SUB_TEXT": "Comença a reenviar els teus correus electrònics a la següent adreça electrònica.",
"ALLOW_MESSAGES_AFTER_RESOLVED": "Allow messages after conversation resolved",
"ALLOW_MESSAGES_AFTER_RESOLVED_SUB_TEXT": "Allow the end-users to send messages even after the conversation is resolved."
}, },
"FACEBOOK_REAUTHORIZE": { "FACEBOOK_REAUTHORIZE": {
"TITLE": "Reautoritza", "TITLE": "Reautoritza",

View file

@ -1,9 +1,12 @@
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json'; import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json'; import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _campaign } from './campaign.json'; import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json'; import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json'; import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json'; import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json'; import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json'; import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json'; import { default as _generalSettings } from './generalSettings.json';
@ -20,12 +23,15 @@ import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json'; import { default as _teamsSettings } from './teamsSettings.json';
export default { export default {
..._advancedFilters,
..._agentMgmt, ..._agentMgmt,
..._attributesMgmt, ..._attributesMgmt,
..._automation,
..._campaign, ..._campaign,
..._cannedMgmt, ..._cannedMgmt,
..._chatlist, ..._chatlist,
..._contact, ..._contact,
..._contactFilters,
..._conversation, ..._conversation,
..._csatMgmtMgmt, ..._csatMgmtMgmt,
..._generalSettings, ..._generalSettings,

View file

@ -89,14 +89,14 @@
"PLACEHOLDER": "Please enter the current password" "PLACEHOLDER": "Please enter the current password"
}, },
"PASSWORD": { "PASSWORD": {
"LABEL": "Contrasenya", "LABEL": "New password",
"ERROR": "Introduïu una contrasenya d'una longitud de 6 o més", "ERROR": "Introduïu una contrasenya d'una longitud de 6 o més",
"PLACEHOLDER": "Introduïu una nova contrasenya" "PLACEHOLDER": "Introduïu una nova contrasenya"
}, },
"PASSWORD_CONFIRMATION": { "PASSWORD_CONFIRMATION": {
"LABEL": "Confirmació de la nova contrasenya", "LABEL": "Confirmació de la nova contrasenya",
"ERROR": "Confirmeu que les contrasenyes coincideixin", "ERROR": "Confirmeu que les contrasenyes coincideixin",
"PLACEHOLDER": "Torneu a introduir la vostra contrasenya" "PLACEHOLDER": "Please re-enter your new password"
} }
} }
}, },
@ -153,6 +153,8 @@
"CUSTOM_ATTRIBUTES": "Atributs personalitzats", "CUSTOM_ATTRIBUTES": "Atributs personalitzats",
"AUTOMATION": "Automation", "AUTOMATION": "Automation",
"TEAMS": "Equips", "TEAMS": "Equips",
"CUSTOM_VIEWS_FOLDER": "Folders",
"CUSTOM_VIEWS_SEGMENTS": "Segments",
"ALL_CONTACTS": "All Contacts", "ALL_CONTACTS": "All Contacts",
"TAGGED_WITH": "Tagged with", "TAGGED_WITH": "Tagged with",
"NEW_LABEL": "New label", "NEW_LABEL": "New label",

View file

@ -2,7 +2,7 @@
"TEAMS_SETTINGS": { "TEAMS_SETTINGS": {
"NEW_TEAM": "Create new team", "NEW_TEAM": "Create new team",
"HEADER": "Equips", "HEADER": "Equips",
"SIDEBAR_TXT": "<p><b>Teams</b></p> <p>Teams let you organize your agents into groups based on their responsibilities. <br /> A user can be part of multiple teams. You can assign conversations to a team when you are working collaboratively. </p>", "SIDEBAR_TXT": "<p><b>Teams</b></p> <p>Teams let you organize your agents into groups based on their responsibilities. <br /> An agent can be part of multiple teams. You can assign conversations to a team when you are working collaboratively. </p>",
"LIST": { "LIST": {
"404": "There are no teams created on this account.", "404": "There are no teams created on this account.",
"EDIT_TEAM": "Edit team" "EDIT_TEAM": "Edit team"

View file

@ -19,7 +19,9 @@
"contains": "Contains", "contains": "Contains",
"does_not_contain": "Does not contain", "does_not_contain": "Does not contain",
"is_present": "Is present", "is_present": "Is present",
"is_not_present": "Is not present" "is_not_present": "Is not present",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"STATUS": "Stav", "STATUS": "Stav",
@ -31,7 +33,54 @@
"LABELS": "Štítky", "LABELS": "Štítky",
"BROWSER_LANGUAGE": "Browser Language", "BROWSER_LANGUAGE": "Browser Language",
"COUNTRY_NAME": "Country Name", "COUNTRY_NAME": "Country Name",
"REFERER_LINK": "Referer link" "REFERER_LINK": "Referer link",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Vlastní atributy"
},
"CUSTOM_VIEWS": {
"ADD": {
"TITLE": "Do you want to save this filter?",
"LABEL": "Name this filter",
"PLACEHOLDER": "Enter a name for this filter",
"ERROR_MESSAGE": "Name is required",
"SAVE_BUTTON": "Save filter",
"CANCEL_BUTTON": "Zrušit",
"API_FOLDERS": {
"SUCCESS_MESSAGE": "Folder created successfully",
"ERROR_MESSAGE": "Error while creating folder"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "Segment created successfully",
"ERROR_MESSAGE": "Error while creating segment"
}
},
"DELETE": {
"DELETE_BUTTON": "Delete filter",
"MODAL": {
"CONFIRM": {
"TITLE": "Potvrdit odstranění",
"MESSAGE": "Are you sure to delete the filter ",
"YES": "Ano, odstranit",
"NO": "No, Keep it"
}
},
"API_FOLDERS": {
"SUCCESS_MESSAGE": "Folder deleted successfully",
"ERROR_MESSAGE": "Error while deleting folder"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "Segment deleted successfully",
"ERROR_MESSAGE": "Error while deleting segment"
}
}
} }
} }
} }

View file

@ -1,6 +1,89 @@
{ {
"AUTOMATION": { "AUTOMATION": {
"HEADER": "Automation", "HEADER": "Automation",
"HEADER_BTN_TXT": "Add Automation Rule" "HEADER_BTN_TXT": "Add Automation Rule",
"LOADING": "Fetching automation rules",
"SIDEBAR_TXT": "<p><b>Automation Rules</b> <p>Automation can replace and automate existing processes that require manual effort. You can do many things with automation, including adding labels and assigning conversation to the best agent. So the team focuses on what they do best and spends more little time on manual tasks.</p>",
"ADD": {
"TITLE": "Add Automation Rule",
"SUBMIT": "Create",
"CANCEL_BUTTON_TEXT": "Zrušit",
"FORM": {
"NAME": {
"LABEL": "Rule Name",
"PLACEHOLDER": "Enter rule name",
"ERROR": "Name is required"
},
"DESC": {
"LABEL": "Description",
"PLACEHOLDER": "Enter rule description",
"ERROR": "Description is required"
},
"EVENT": {
"LABEL": "Event",
"PLACEHOLDER": "Please select one",
"ERROR": "Event is required"
},
"CONDITIONS": {
"LABEL": "Conditions"
},
"ACTIONS": {
"LABEL": "Akce"
}
},
"CONDITION_BUTTON_LABEL": "Add Condition",
"ACTION_BUTTON_LABEL": "Add Action",
"API": {
"SUCCESS_MESSAGE": "Automation rule added successfully",
"ERROR_MESSAGE": "Could not able to create a automation rule, Please try again later"
}
},
"LIST": {
"TABLE_HEADER": [
"Název",
"Description",
"Active",
"Created on"
],
"404": "No automation rules found"
},
"DELETE": {
"TITLE": "Delete Automation Rule",
"SUBMIT": "Vymazat",
"CANCEL_BUTTON_TEXT": "Zrušit",
"CONFIRM": {
"TITLE": "Potvrdit odstranění",
"MESSAGE": "Opravdu chcete odstranit ",
"YES": "Ano, odstranit ",
"NO": "Ne, zachovat "
},
"API": {
"SUCCESS_MESSAGE": "Automation rule deleted successfully",
"ERROR_MESSAGE": "Could not able to delete a automation rule, Please try again later"
}
},
"EDIT": {
"TITLE": "Edit Automation Rule",
"SUBMIT": "Upravit",
"CANCEL_BUTTON_TEXT": "Zrušit",
"API": {
"SUCCESS_MESSAGE": "Automation rule updated successfully",
"ERROR_MESSAGE": "Could not update automation rule, Please try again later"
}
},
"CLONE": {
"TOOLTIP": "Clone",
"API": {
"SUCCESS_MESSAGE": "Automation cloned successfully",
"ERROR_MESSAGE": "Could not clone automation rule, Please try again later"
}
},
"FORM": {
"EDIT": "Upravit",
"CREATE": "Create",
"DELETE": "Vymazat",
"CANCEL": "Zrušit",
"RESET_MESSAGE": "Changing event type will reset the conditions and events you have added below"
}
} }
} }

View file

@ -4,7 +4,7 @@
"HEADER_BTN_TXT": "Přidat konzervovanou odpověď", "HEADER_BTN_TXT": "Přidat konzervovanou odpověď",
"LOADING": "Načítání Konzervovaných odpovědí", "LOADING": "Načítání Konzervovaných odpovědí",
"SEARCH_404": "Neexistují žádné položky odpovídající tomuto dotazu", "SEARCH_404": "Neexistují žádné položky odpovídající tomuto dotazu",
"SIDEBAR_TXT": "<p><b>Canned Response</b> </p><p> Canned Response are saved šablony odpovědí, které mohou být použity pro rychlé odeslání odpovědi do konverzace . </p><p> Pro vytvoření Canned Response klikněte na <b>Přidat Canned Response</b>. Můžete také upravit nebo odstranit existující Canned Response kliknutím na tlačítko Upravit nebo vymazat </p><p> Rušené odpovědi jsou použity s pomocí <b>Krátké kódy</b>. Pracovníci mohou během chatu získat přístup k uloženým odpovědím napsaním <b>'/'</b> následovaným krátkým kódem. </p>", "SIDEBAR_TXT": "<p><b>Canned Responses</b> </p><p> Canned Responses are saved reply templates which can be used to quickly send out a reply to a conversation. </p><p> For creating a Canned Response, just click on the <b>Add Canned Response</b>. You can also edit or delete an existing Canned Response by clicking on the Edit or Delete button </p><p> Canned responses are used with the help of <b>Short Codes</b>. Agents can access canned responses while on a chat by typing <b>'/'</b> followed by the short code. </p>",
"LIST": { "LIST": {
"404": "V tomto účtu nejsou k dispozici žádné konzervované odpovědi.", "404": "V tomto účtu nejsou k dispozici žádné konzervované odpovědi.",
"TITLE": "Spravovat konzervované odpovědi", "TITLE": "Spravovat konzervované odpovědi",
@ -17,12 +17,12 @@
}, },
"ADD": { "ADD": {
"TITLE": "Přidat konzervovanou odpověď", "TITLE": "Přidat konzervovanou odpověď",
"DESC": "Konzervované odpovědi jsou uložené šablony odpovědí, které lze použít pro rychlé odeslání odpovědi do konverzace .", "DESC": "Canned Responses are saved reply templates which can be used to quickly send out reply to conversation.",
"CANCEL_BUTTON_TEXT": "Zrušit", "CANCEL_BUTTON_TEXT": "Zrušit",
"FORM": { "FORM": {
"SHORT_CODE": { "SHORT_CODE": {
"LABEL": "Krátký kód", "LABEL": "Krátký kód",
"PLACEHOLDER": "Zadejte zkratkový kód", "PLACEHOLDER": "Please enter a short code",
"ERROR": "Krátký kód je povinný" "ERROR": "Krátký kód je povinný"
}, },
"CONTENT": { "CONTENT": {

View file

@ -169,6 +169,7 @@
"SUBMIT": "Send message", "SUBMIT": "Send message",
"CANCEL": "Zrušit", "CANCEL": "Zrušit",
"SUCCESS_MESSAGE": "Message sent!", "SUCCESS_MESSAGE": "Message sent!",
"GO_TO_CONVERSATION": "Zobrazit",
"ERROR_MESSAGE": "Couldn't send! try again" "ERROR_MESSAGE": "Couldn't send! try again"
} }
}, },
@ -178,6 +179,8 @@
"SEARCH_BUTTON": "Hledat", "SEARCH_BUTTON": "Hledat",
"SEARCH_INPUT_PLACEHOLDER": "Hledat kontakty", "SEARCH_INPUT_PLACEHOLDER": "Hledat kontakty",
"FILTER_CONTACTS": "Filter", "FILTER_CONTACTS": "Filter",
"FILTER_CONTACTS_SAVE": "Save filter",
"FILTER_CONTACTS_DELETE": "Delete filter",
"LIST": { "LIST": {
"LOADING_MESSAGE": "Načítání kontaktů...", "LOADING_MESSAGE": "Načítání kontaktů...",
"404": "Vašemu hledání neodpovídají žádné kontakty 🔍", "404": "Vašemu hledání neodpovídají žádné kontakty 🔍",

View file

@ -20,7 +20,9 @@
"contains": "Contains", "contains": "Contains",
"does_not_contain": "Does not contain", "does_not_contain": "Does not contain",
"is_present": "Is present", "is_present": "Is present",
"is_not_present": "Is not present" "is_not_present": "Is not present",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"NAME": "Název", "NAME": "Název",
@ -28,7 +30,17 @@
"PHONE_NUMBER": "Telefonní číslo", "PHONE_NUMBER": "Telefonní číslo",
"IDENTIFIER": "Identifier", "IDENTIFIER": "Identifier",
"CITY": "Město", "CITY": "Město",
"COUNTRY": "Země" "COUNTRY": "Země",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Vlastní atributy"
} }
} }
} }

View file

@ -22,6 +22,8 @@
"LOADING_CONVERSATIONS": "Načítání konverzací", "LOADING_CONVERSATIONS": "Načítání konverzací",
"CANNOT_REPLY": "Nemůžete odpovědět z důvodu", "CANNOT_REPLY": "Nemůžete odpovědět z důvodu",
"24_HOURS_WINDOW": "24 hodinové omezení okna", "24_HOURS_WINDOW": "24 hodinové omezení okna",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Přiřadit mi",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to", "TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hodinové omezení okna", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hodinové omezení okna",
"SELECT_A_TWEET_TO_REPLY": "Please select a tweet to reply to.", "SELECT_A_TWEET_TO_REPLY": "Please select a tweet to reply to.",
@ -90,6 +92,9 @@
"FILE_SIZE_LIMIT": "Soubor překračuje limit {MAXIMUM_FILE_UPLOAD_SIZE} přílohy", "FILE_SIZE_LIMIT": "Soubor překračuje limit {MAXIMUM_FILE_UPLOAD_SIZE} přílohy",
"MESSAGE_ERROR": "Unable to send this message, please try again later", "MESSAGE_ERROR": "Unable to send this message, please try again later",
"SENT_BY": "Odeslal:", "SENT_BY": "Odeslal:",
"BOT": "Bot",
"SEND_FAILED": "Couldn't send message! Try again",
"TRY_AGAIN": "retry",
"ASSIGNMENT": { "ASSIGNMENT": {
"SELECT_AGENT": "Vybrat agenta", "SELECT_AGENT": "Vybrat agenta",
"REMOVE": "Odebrat", "REMOVE": "Odebrat",
@ -127,7 +132,7 @@
}, },
"TEAM_MEMBERS": { "TEAM_MEMBERS": {
"TITLE": "Pozvěte své členy týmu", "TITLE": "Pozvěte své členy týmu",
"DESCRIPTION": "Vzhledem k tomu, že se připravujete na rozhovory se zákazníkem, vdechněte své týmové spolupracovníky, kteří vám pomohou. Můžete pozvat své přátele přidáním jejich e-mailové adresy do seznamu agentů.", "DESCRIPTION": "Since you are getting ready to talk to your customer, bring in your teammates to assist you. You can invite your teammates by adding their email addresses to the agent list.",
"NEW_LINK": "Klikněte zde pro pozvání člena týmu" "NEW_LINK": "Klikněte zde pro pozvání člena týmu"
}, },
"INBOXES": { "INBOXES": {

View file

@ -40,7 +40,7 @@
"AUTO_RESOLVE_DURATION": { "AUTO_RESOLVE_DURATION": {
"LABEL": "Počet dnů, po kterých by měl být ticket automaticky vyřešen při žádné aktivitě", "LABEL": "Počet dnů, po kterých by měl být ticket automaticky vyřešen při žádné aktivitě",
"PLACEHOLDER": "30", "PLACEHOLDER": "30",
"ERROR": "Zadejte platnou hodnotu automatického vyřešení (minimálně 1 den)" "ERROR": "Please enter a valid auto resolve duration (minimum 1 day and maximum 999 days)"
}, },
"FEATURES": { "FEATURES": {
"INBOUND_EMAIL_ENABLED": "E-mailová konverzace je u vašeho účtu povolena.", "INBOUND_EMAIL_ENABLED": "E-mailová konverzace je u vašeho účtu povolena.",

View file

@ -82,7 +82,7 @@
}, },
"CHANNEL_GREETING_TOGGLE": { "CHANNEL_GREETING_TOGGLE": {
"LABEL": "Povolit uvítání", "LABEL": "Povolit uvítání",
"HELP_TEXT": "Poslat uvítací zprávu uživateli, když začne konverzaci.", "HELP_TEXT": "Send a greeting message to the users when they starts the conversation.",
"ENABLED": "Povoleno", "ENABLED": "Povoleno",
"DISABLED": "Zakázáno" "DISABLED": "Zakázáno"
}, },
@ -136,8 +136,56 @@
} }
}, },
"SMS": { "SMS": {
"TITLE": "SMS Channel via Twilio", "TITLE": "SMS Channel",
"DESC": "Start supporting your customers via SMS with Twilio integration." "DESC": "Start supporting your customers via SMS.",
"PROVIDERS": {
"LABEL": "API Provider",
"TWILIO": "Twilio",
"BANDWIDTH": "Bandwidth"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the SMS channel"
},
"BANDWIDTH": {
"ACCOUNT_ID": {
"LABEL": "Account ID",
"PLACEHOLDER": "Please enter your Bandwidth Account ID",
"ERROR": "Toto pole je povinné"
},
"API_KEY": {
"LABEL": "API Key",
"PLACEHOLDER": "Please enter your Bandwith API Key",
"ERROR": "Toto pole je povinné"
},
"API_SECRET": {
"LABEL": "API Secret",
"PLACEHOLDER": "Please enter your Bandwith API Secret",
"ERROR": "Toto pole je povinné"
},
"APPLICATION_ID": {
"LABEL": "Application ID",
"PLACEHOLDER": "Please enter your Bandwidth Application ID",
"ERROR": "Toto pole je povinné"
},
"INBOX_NAME": {
"LABEL": "Název schránky",
"PLACEHOLDER": "Please enter a inbox name",
"ERROR": "Toto pole je povinné"
},
"PHONE_NUMBER": {
"LABEL": "Telefonní číslo",
"PLACEHOLDER": "Zadejte prosím telefonní číslo, ze kterého bude zpráva odeslána.",
"ERROR": "Zadejte platnou hodnotu. Telefonní číslo by mělo začínat znakem `+`."
},
"SUBMIT_BUTTON": "Create Bandwidth Channel",
"API": {
"ERROR_MESSAGE": "We were not able to authenticate Bandwidth credentials, please try again"
},
"API_CALLBACK": {
"TITLE": "Callback URL",
"SUBTITLE": "You have to configure the message callback URL in Bandwidth with the URL mentioned here."
}
}
}, },
"WHATSAPP": { "WHATSAPP": {
"TITLE": "WhatsApp Channel", "TITLE": "WhatsApp Channel",
@ -305,6 +353,14 @@
"ENABLED": "Povoleno", "ENABLED": "Povoleno",
"DISABLED": "Zakázáno" "DISABLED": "Zakázáno"
}, },
"ALLOW_MESSAGES_AFTER_RESOLVED": {
"ENABLED": "Povoleno",
"DISABLED": "Zakázáno"
},
"ENABLE_CONTINUITY_VIA_EMAIL": {
"ENABLED": "Povoleno",
"DISABLED": "Zakázáno"
},
"ENABLE_HMAC": { "ENABLE_HMAC": {
"LABEL": "Enable" "LABEL": "Enable"
} }
@ -351,6 +407,8 @@
"AUTO_ASSIGNMENT": "Povolit automatické přiřazení", "AUTO_ASSIGNMENT": "Povolit automatické přiřazení",
"ENABLE_CSAT": "Enable CSAT", "ENABLE_CSAT": "Enable CSAT",
"ENABLE_CSAT_SUB_TEXT": "Enable/Disable CSAT(Customer satisfaction) survey after resolving a conversation", "ENABLE_CSAT_SUB_TEXT": "Enable/Disable CSAT(Customer satisfaction) survey after resolving a conversation",
"ENABLE_CONTINUITY_VIA_EMAIL": "Enable conversation continuity via email",
"ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT": "Conversations will continue over email if the contact email address is available.",
"INBOX_UPDATE_TITLE": "Nastavení doručené pošty", "INBOX_UPDATE_TITLE": "Nastavení doručené pošty",
"INBOX_UPDATE_SUB_TEXT": "Aktualizujte nastavení doručené pošty", "INBOX_UPDATE_SUB_TEXT": "Aktualizujte nastavení doručené pošty",
"AUTO_ASSIGNMENT_SUB_TEXT": "Povolit nebo zakázat automatické přiřazování nových konverzací agentům přidaným do této schránky.", "AUTO_ASSIGNMENT_SUB_TEXT": "Povolit nebo zakázat automatické přiřazování nových konverzací agentům přidaným do této schránky.",
@ -361,7 +419,9 @@
"INBOX_IDENTIFIER": "Inbox Identifier", "INBOX_IDENTIFIER": "Inbox Identifier",
"INBOX_IDENTIFIER_SUB_TEXT": "Use the `inbox_identifier` token shown here to authentication your API clients.", "INBOX_IDENTIFIER_SUB_TEXT": "Use the `inbox_identifier` token shown here to authentication your API clients.",
"FORWARD_EMAIL_TITLE": "Forward to Email", "FORWARD_EMAIL_TITLE": "Forward to Email",
"FORWARD_EMAIL_SUB_TEXT": "Start forwarding your emails to the following email address." "FORWARD_EMAIL_SUB_TEXT": "Start forwarding your emails to the following email address.",
"ALLOW_MESSAGES_AFTER_RESOLVED": "Allow messages after conversation resolved",
"ALLOW_MESSAGES_AFTER_RESOLVED_SUB_TEXT": "Allow the end-users to send messages even after the conversation is resolved."
}, },
"FACEBOOK_REAUTHORIZE": { "FACEBOOK_REAUTHORIZE": {
"TITLE": "Znovu autorizovat", "TITLE": "Znovu autorizovat",

View file

@ -1,9 +1,12 @@
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json'; import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json'; import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _campaign } from './campaign.json'; import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json'; import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json'; import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json'; import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json'; import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json'; import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json'; import { default as _generalSettings } from './generalSettings.json';
@ -20,12 +23,15 @@ import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json'; import { default as _teamsSettings } from './teamsSettings.json';
export default { export default {
..._advancedFilters,
..._agentMgmt, ..._agentMgmt,
..._attributesMgmt, ..._attributesMgmt,
..._automation,
..._campaign, ..._campaign,
..._cannedMgmt, ..._cannedMgmt,
..._chatlist, ..._chatlist,
..._contact, ..._contact,
..._contactFilters,
..._conversation, ..._conversation,
..._csatMgmtMgmt, ..._csatMgmtMgmt,
..._generalSettings, ..._generalSettings,

View file

@ -89,14 +89,14 @@
"PLACEHOLDER": "Please enter the current password" "PLACEHOLDER": "Please enter the current password"
}, },
"PASSWORD": { "PASSWORD": {
"LABEL": "Heslo", "LABEL": "New password",
"ERROR": "Zadejte prosím heslo o délce 6 nebo více", "ERROR": "Zadejte prosím heslo o délce 6 nebo více",
"PLACEHOLDER": "Zadejte prosím nové heslo" "PLACEHOLDER": "Zadejte prosím nové heslo"
}, },
"PASSWORD_CONFIRMATION": { "PASSWORD_CONFIRMATION": {
"LABEL": "Potvrdit nové heslo", "LABEL": "Potvrdit nové heslo",
"ERROR": "Potvrzení hesla by mělo odpovídat heslu", "ERROR": "Potvrzení hesla by mělo odpovídat heslu",
"PLACEHOLDER": "Zadejte prosím znovu své heslo" "PLACEHOLDER": "Please re-enter your new password"
} }
} }
}, },
@ -153,6 +153,8 @@
"CUSTOM_ATTRIBUTES": "Vlastní atributy", "CUSTOM_ATTRIBUTES": "Vlastní atributy",
"AUTOMATION": "Automation", "AUTOMATION": "Automation",
"TEAMS": "Týmy", "TEAMS": "Týmy",
"CUSTOM_VIEWS_FOLDER": "Folders",
"CUSTOM_VIEWS_SEGMENTS": "Segments",
"ALL_CONTACTS": "All Contacts", "ALL_CONTACTS": "All Contacts",
"TAGGED_WITH": "Tagged with", "TAGGED_WITH": "Tagged with",
"NEW_LABEL": "New label", "NEW_LABEL": "New label",

View file

@ -2,7 +2,7 @@
"TEAMS_SETTINGS": { "TEAMS_SETTINGS": {
"NEW_TEAM": "Create new team", "NEW_TEAM": "Create new team",
"HEADER": "Týmy", "HEADER": "Týmy",
"SIDEBAR_TXT": "<p><b>Teams</b></p> <p>Teams let you organize your agents into groups based on their responsibilities. <br /> A user can be part of multiple teams. You can assign conversations to a team when you are working collaboratively. </p>", "SIDEBAR_TXT": "<p><b>Teams</b></p> <p>Teams let you organize your agents into groups based on their responsibilities. <br /> An agent can be part of multiple teams. You can assign conversations to a team when you are working collaboratively. </p>",
"LIST": { "LIST": {
"404": "There are no teams created on this account.", "404": "There are no teams created on this account.",
"EDIT_TEAM": "Edit team" "EDIT_TEAM": "Edit team"

View file

@ -19,7 +19,9 @@
"contains": "Contains", "contains": "Contains",
"does_not_contain": "Does not contain", "does_not_contain": "Does not contain",
"is_present": "Is present", "is_present": "Is present",
"is_not_present": "Is not present" "is_not_present": "Is not present",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
}, },
"ATTRIBUTES": { "ATTRIBUTES": {
"STATUS": "Status", "STATUS": "Status",
@ -31,7 +33,54 @@
"LABELS": "Etiketter", "LABELS": "Etiketter",
"BROWSER_LANGUAGE": "Browser Language", "BROWSER_LANGUAGE": "Browser Language",
"COUNTRY_NAME": "Country Name", "COUNTRY_NAME": "Country Name",
"REFERER_LINK": "Referer link" "REFERER_LINK": "Referer link",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Brugerdefinerede Egenskaber"
},
"CUSTOM_VIEWS": {
"ADD": {
"TITLE": "Do you want to save this filter?",
"LABEL": "Name this filter",
"PLACEHOLDER": "Enter a name for this filter",
"ERROR_MESSAGE": "Name is required",
"SAVE_BUTTON": "Save filter",
"CANCEL_BUTTON": "Annuller",
"API_FOLDERS": {
"SUCCESS_MESSAGE": "Folder created successfully",
"ERROR_MESSAGE": "Error while creating folder"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "Segment created successfully",
"ERROR_MESSAGE": "Error while creating segment"
}
},
"DELETE": {
"DELETE_BUTTON": "Delete filter",
"MODAL": {
"CONFIRM": {
"TITLE": "Bekræft Sletning",
"MESSAGE": "Are you sure to delete the filter ",
"YES": "Ja, Slet",
"NO": "Nej, behold det"
}
},
"API_FOLDERS": {
"SUCCESS_MESSAGE": "Folder deleted successfully",
"ERROR_MESSAGE": "Error while deleting folder"
},
"API_SEGMENTS": {
"SUCCESS_MESSAGE": "Segment deleted successfully",
"ERROR_MESSAGE": "Error while deleting segment"
}
}
} }
} }
} }

View file

@ -1,6 +1,89 @@
{ {
"AUTOMATION": { "AUTOMATION": {
"HEADER": "Automation", "HEADER": "Automation",
"HEADER_BTN_TXT": "Add Automation Rule" "HEADER_BTN_TXT": "Add Automation Rule",
"LOADING": "Fetching automation rules",
"SIDEBAR_TXT": "<p><b>Automation Rules</b> <p>Automation can replace and automate existing processes that require manual effort. You can do many things with automation, including adding labels and assigning conversation to the best agent. So the team focuses on what they do best and spends more little time on manual tasks.</p>",
"ADD": {
"TITLE": "Add Automation Rule",
"SUBMIT": "Opret",
"CANCEL_BUTTON_TEXT": "Annuller",
"FORM": {
"NAME": {
"LABEL": "Rule Name",
"PLACEHOLDER": "Enter rule name",
"ERROR": "Name is required"
},
"DESC": {
"LABEL": "Beskrivelse",
"PLACEHOLDER": "Enter rule description",
"ERROR": "Description is required"
},
"EVENT": {
"LABEL": "Event",
"PLACEHOLDER": "Please select one",
"ERROR": "Event is required"
},
"CONDITIONS": {
"LABEL": "Conditions"
},
"ACTIONS": {
"LABEL": "Handlinger"
}
},
"CONDITION_BUTTON_LABEL": "Add Condition",
"ACTION_BUTTON_LABEL": "Add Action",
"API": {
"SUCCESS_MESSAGE": "Automation rule added successfully",
"ERROR_MESSAGE": "Could not able to create a automation rule, Please try again later"
}
},
"LIST": {
"TABLE_HEADER": [
"Navn",
"Beskrivelse",
"Active",
"Created on"
],
"404": "No automation rules found"
},
"DELETE": {
"TITLE": "Delete Automation Rule",
"SUBMIT": "Slet",
"CANCEL_BUTTON_TEXT": "Annuller",
"CONFIRM": {
"TITLE": "Bekræft Sletning",
"MESSAGE": "Er du sikker på du vil slette ",
"YES": "Ja, Slet ",
"NO": "Nej, Behold "
},
"API": {
"SUCCESS_MESSAGE": "Automation rule deleted successfully",
"ERROR_MESSAGE": "Could not able to delete a automation rule, Please try again later"
}
},
"EDIT": {
"TITLE": "Edit Automation Rule",
"SUBMIT": "Rediger",
"CANCEL_BUTTON_TEXT": "Annuller",
"API": {
"SUCCESS_MESSAGE": "Automation rule updated successfully",
"ERROR_MESSAGE": "Could not update automation rule, Please try again later"
}
},
"CLONE": {
"TOOLTIP": "Clone",
"API": {
"SUCCESS_MESSAGE": "Automation cloned successfully",
"ERROR_MESSAGE": "Could not clone automation rule, Please try again later"
}
},
"FORM": {
"EDIT": "Rediger",
"CREATE": "Opret",
"DELETE": "Slet",
"CANCEL": "Annuller",
"RESET_MESSAGE": "Changing event type will reset the conditions and events you have added below"
}
} }
} }

Some files were not shown because too many files have changed in this diff Show more