Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd016c5ad4 |
@@ -2,7 +2,7 @@
|
|||||||
# It has the dependencies already installed so that codespace will boot up fast
|
# It has the dependencies already installed so that codespace will boot up fast
|
||||||
FROM ghcr.io/chatwoot/chatwoot_codespace:latest
|
FROM ghcr.io/chatwoot/chatwoot_codespace:latest
|
||||||
|
|
||||||
# Do the set up required for chatwoot app
|
# Do the set up required for chatwoot app
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
COPY . /workspace
|
COPY . /workspace
|
||||||
RUN yarn && gem install bundler && bundle install
|
RUN yarn && gem install bundler && bundle install
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
|
|
||||||
ARG VARIANT
|
ARG VARIANT=ubuntu-20.04
|
||||||
|
FROM mcr.microsoft.com/vscode/devcontainers/base:${VARIANT}
|
||||||
FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT}
|
|
||||||
|
|
||||||
ARG NODE_VERSION
|
|
||||||
ARG RUBY_VERSION
|
|
||||||
ARG USER_UID
|
|
||||||
ARG USER_GID
|
|
||||||
|
|
||||||
# Update args in docker-compose.yaml to set the UID/GID of the "vscode" user.
|
# Update args in docker-compose.yaml to set the UID/GID of the "vscode" user.
|
||||||
|
ARG USER_UID=1000
|
||||||
|
ARG USER_GID=$USER_UID
|
||||||
RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
|
RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
|
||||||
groupmod --gid $USER_GID vscode \
|
groupmod --gid $USER_GID vscode \
|
||||||
&& usermod --uid $USER_UID --gid $USER_GID vscode \
|
&& usermod --uid $USER_UID --gid $USER_GID vscode \
|
||||||
@@ -31,10 +27,10 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|||||||
tmux \
|
tmux \
|
||||||
zsh \
|
zsh \
|
||||||
git-flow \
|
git-flow \
|
||||||
npm \
|
npm
|
||||||
libyaml-dev
|
|
||||||
|
|
||||||
# Install rbenv and ruby
|
# Install rbenv and ruby
|
||||||
|
ARG RUBY_VERSION="3.2.2"
|
||||||
RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv \
|
RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv \
|
||||||
&& echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc \
|
&& echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc \
|
||||||
&& echo 'eval "$(rbenv init -)"' >> ~/.bashrc
|
&& echo 'eval "$(rbenv init -)"' >> ~/.bashrc
|
||||||
@@ -50,7 +46,7 @@ RUN rbenv install $RUBY_VERSION && \
|
|||||||
RUN curl -L https://github.com/DarthSim/overmind/releases/download/v2.1.0/overmind-v2.1.0-linux-amd64.gz > overmind.gz \
|
RUN curl -L https://github.com/DarthSim/overmind/releases/download/v2.1.0/overmind-v2.1.0-linux-amd64.gz > overmind.gz \
|
||||||
&& gunzip overmind.gz \
|
&& gunzip overmind.gz \
|
||||||
&& sudo mv overmind /usr/local/bin \
|
&& sudo mv overmind /usr/local/bin \
|
||||||
&& chmod +x /usr/local/bin/overmind
|
&& chmod +x /usr/local/bin/overmind
|
||||||
|
|
||||||
|
|
||||||
# Install gh
|
# Install gh
|
||||||
@@ -60,7 +56,7 @@ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | s
|
|||||||
&& sudo apt install gh
|
&& sudo apt install gh
|
||||||
|
|
||||||
|
|
||||||
# Do the set up required for chatwoot app
|
# Do the set up required for chatwoot app
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
COPY . /workspace
|
COPY . /workspace
|
||||||
|
|
||||||
@@ -69,7 +65,8 @@ COPY Gemfile Gemfile.lock ./
|
|||||||
RUN gem install bundler && bundle install
|
RUN gem install bundler && bundle install
|
||||||
|
|
||||||
# set up node js
|
# set up node js
|
||||||
RUN npm install n -g && \
|
RUN npm install npm@latest -g && \
|
||||||
n $NODE_VERSION
|
npm install n -g && \
|
||||||
|
n latest
|
||||||
RUN npm install --global yarn
|
RUN npm install --global yarn
|
||||||
RUN yarn
|
RUN yarn
|
||||||
|
|||||||
@@ -1,41 +1,30 @@
|
|||||||
# https://github.com/microsoft/vscode-dev-containers/blob/master/containers/python-3-postgres/.devcontainer/docker-compose.yml
|
# https://github.com/microsoft/vscode-dev-containers/blob/master/containers/python-3-postgres/.devcontainer/docker-compose.yml
|
||||||
# https://github.com/microsoft/vscode-dev-containers/blob/master/containers/ruby-rails/.devcontainer/devcontainer.json
|
# https://github.com/microsoft/vscode-dev-containers/blob/master/containers/ruby-rails/.devcontainer/devcontainer.json
|
||||||
#
|
#
|
||||||
|
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
base:
|
app:
|
||||||
build:
|
|
||||||
context: ..
|
|
||||||
dockerfile: .devcontainer/Dockerfile.base
|
|
||||||
args:
|
|
||||||
VARIANT: "ubuntu-22.04"
|
|
||||||
NODE_VERSION: "20.9.0"
|
|
||||||
RUBY_VERSION: "3.2.2"
|
|
||||||
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
|
||||||
USER_UID: "1000"
|
|
||||||
USER_GID: "1000"
|
|
||||||
image: base:latest
|
|
||||||
|
|
||||||
app:
|
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
dockerfile: .devcontainer/Dockerfile
|
dockerfile: .devcontainer/Dockerfile
|
||||||
args:
|
args:
|
||||||
VARIANT: "ubuntu-22.04"
|
# Update 'VARIANT' to pick a Ruby version: https://github.com/microsoft/vscode-dev-containers/tree/main/containers/ruby
|
||||||
NODE_VERSION: "20.9.0"
|
VARIANT: 3
|
||||||
RUBY_VERSION: "3.2.2"
|
# [Choice] Install Node.js
|
||||||
|
INSTALL_NODE: "true"
|
||||||
|
NODE_VERSION: "lts/*"
|
||||||
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
# On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000.
|
||||||
USER_UID: "1000"
|
USER_UID: 1000
|
||||||
USER_GID: "1000"
|
USER_GID: 1000
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- ..:/workspace:cached
|
- ..:/workspace:cached
|
||||||
|
|
||||||
# Overrides default command so things don't shut down after the process ends.
|
# Overrides default command so things don't shut down after the process ends.
|
||||||
command: sleep infinity
|
command: sleep infinity
|
||||||
|
|
||||||
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
|
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
|
||||||
network_mode: service:db
|
network_mode: service:db
|
||||||
|
|
||||||
@@ -60,7 +49,8 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: mailhog/mailhog
|
image: mailhog/mailhog
|
||||||
network_mode: service:db
|
network_mode: service:db
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres-data:
|
postgres-data:
|
||||||
redis-data:
|
redis-data:
|
||||||
|
|
||||||
@@ -19,6 +19,5 @@ jobs:
|
|||||||
|
|
||||||
- name: Build the Codespace Base Image
|
- name: Build the Codespace Base Image
|
||||||
run: |
|
run: |
|
||||||
docker-compose -f .devcontainer/docker-compose.yml build base
|
docker build . -t ghcr.io/chatwoot/chatwoot_codespace:latest -f .devcontainer/Dockerfile.base
|
||||||
docker tag base:latest ghcr.io/chatwoot/chatwoot_codespace:latest
|
|
||||||
docker push ghcr.io/chatwoot/chatwoot_codespace:latest
|
docker push ghcr.io/chatwoot/chatwoot_codespace:latest
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ gem 'jwt'
|
|||||||
gem 'pundit'
|
gem 'pundit'
|
||||||
# super admin
|
# super admin
|
||||||
gem 'administrate', '>= 0.19.0'
|
gem 'administrate', '>= 0.19.0'
|
||||||
gem 'administrate-field-active_storage', '>= 1.0.1'
|
gem 'administrate-field-active_storage'
|
||||||
gem 'administrate-field-belongs_to_search', '>= 0.9.0'
|
gem 'administrate-field-belongs_to_search'
|
||||||
|
|
||||||
##--- gems for pubsub service ---##
|
##--- gems for pubsub service ---##
|
||||||
# https://karolgalanciak.com/blog/2019/11/30/from-activerecord-callbacks-to-publish-slash-subscribe-pattern-and-event-driven-design/
|
# https://karolgalanciak.com/blog/2019/11/30/from-activerecord-callbacks-to-publish-slash-subscribe-pattern-and-event-driven-design/
|
||||||
@@ -109,14 +109,14 @@ gem 'elastic-apm', require: false
|
|||||||
gem 'newrelic_rpm', require: false
|
gem 'newrelic_rpm', require: false
|
||||||
gem 'newrelic-sidekiq-metrics', '>= 1.6.2', require: false
|
gem 'newrelic-sidekiq-metrics', '>= 1.6.2', require: false
|
||||||
gem 'scout_apm', require: false
|
gem 'scout_apm', require: false
|
||||||
gem 'sentry-rails', '>= 5.14.0', require: false
|
gem 'sentry-rails', '>= 5.13.0', require: false
|
||||||
gem 'sentry-ruby', require: false
|
gem 'sentry-ruby', require: false
|
||||||
gem 'sentry-sidekiq', '>= 5.14.0', require: false
|
gem 'sentry-sidekiq', '>= 5.13.0', require: false
|
||||||
|
|
||||||
##-- background job processing --##
|
##-- background job processing --##
|
||||||
gem 'sidekiq', '>= 7.1.3'
|
gem 'sidekiq', '>= 7.1.3'
|
||||||
# We want cron jobs
|
# We want cron jobs
|
||||||
gem 'sidekiq-cron', '>= 1.12.0'
|
gem 'sidekiq-cron', '>= 1.11.0'
|
||||||
|
|
||||||
##-- Push notification service --##
|
##-- Push notification service --##
|
||||||
gem 'fcm'
|
gem 'fcm'
|
||||||
@@ -162,7 +162,7 @@ gem 'omniauth-oauth2'
|
|||||||
gem 'audited', '~> 5.4', '>= 5.4.1'
|
gem 'audited', '~> 5.4', '>= 5.4.1'
|
||||||
|
|
||||||
# need for google auth
|
# need for google auth
|
||||||
gem 'omniauth', '>= 2.1.2'
|
gem 'omniauth'
|
||||||
gem 'omniauth-google-oauth2'
|
gem 'omniauth-google-oauth2'
|
||||||
gem 'omniauth-rails_csrf_protection', '~> 1.0'
|
gem 'omniauth-rails_csrf_protection', '~> 1.0'
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ group :development do
|
|||||||
gem 'squasher'
|
gem 'squasher'
|
||||||
|
|
||||||
# profiling
|
# profiling
|
||||||
gem 'rack-mini-profiler', '>= 3.2.0', require: false
|
gem 'rack-mini-profiler', '>= 3.1.1', require: false
|
||||||
gem 'stackprof'
|
gem 'stackprof'
|
||||||
# Should install the associated chrome extension to view query logs
|
# Should install the associated chrome extension to view query logs
|
||||||
gem 'meta_request'
|
gem 'meta_request'
|
||||||
@@ -224,7 +224,7 @@ group :development, :test do
|
|||||||
gem 'byebug', platform: :mri
|
gem 'byebug', platform: :mri
|
||||||
gem 'climate_control'
|
gem 'climate_control'
|
||||||
gem 'debug', '~> 1.8'
|
gem 'debug', '~> 1.8'
|
||||||
gem 'factory_bot_rails', '>= 6.4.3'
|
gem 'factory_bot_rails'
|
||||||
gem 'listen'
|
gem 'listen'
|
||||||
gem 'mock_redis'
|
gem 'mock_redis'
|
||||||
gem 'pry-rails'
|
gem 'pry-rails'
|
||||||
|
|||||||
+40
-40
@@ -113,13 +113,13 @@ GEM
|
|||||||
kaminari (>= 1.0)
|
kaminari (>= 1.0)
|
||||||
sassc-rails (~> 2.1)
|
sassc-rails (~> 2.1)
|
||||||
selectize-rails (~> 0.6)
|
selectize-rails (~> 0.6)
|
||||||
administrate-field-active_storage (1.0.1)
|
administrate-field-active_storage (0.4.2)
|
||||||
administrate (>= 0.2.2)
|
administrate (>= 0.2.2)
|
||||||
rails (>= 7.0)
|
rails (>= 7.0)
|
||||||
administrate-field-belongs_to_search (0.9.0)
|
administrate-field-belongs_to_search (0.8.0)
|
||||||
administrate (>= 0.3, < 1.0)
|
administrate (>= 0.3, < 1.0)
|
||||||
jbuilder (~> 2)
|
jbuilder (~> 2)
|
||||||
rails (>= 4.2, < 7.2)
|
rails (>= 4.2, < 7.1)
|
||||||
selectize-rails (~> 0.6)
|
selectize-rails (~> 0.6)
|
||||||
annotate (3.2.0)
|
annotate (3.2.0)
|
||||||
activerecord (>= 3.2, < 8.0)
|
activerecord (>= 3.2, < 8.0)
|
||||||
@@ -183,7 +183,7 @@ GEM
|
|||||||
activerecord (>= 5.a)
|
activerecord (>= 5.a)
|
||||||
database_cleaner-core (~> 2.0.0)
|
database_cleaner-core (~> 2.0.0)
|
||||||
database_cleaner-core (2.0.1)
|
database_cleaner-core (2.0.1)
|
||||||
date (3.3.4)
|
date (3.3.3)
|
||||||
ddtrace (1.11.1)
|
ddtrace (1.11.1)
|
||||||
debase-ruby_core_source (>= 0.10.16, <= 3.2.0)
|
debase-ruby_core_source (>= 0.10.16, <= 3.2.0)
|
||||||
libdatadog (~> 2.0.0.1.0)
|
libdatadog (~> 2.0.0.1.0)
|
||||||
@@ -230,10 +230,10 @@ GEM
|
|||||||
facebook-messenger (2.0.1)
|
facebook-messenger (2.0.1)
|
||||||
httparty (~> 0.13, >= 0.13.7)
|
httparty (~> 0.13, >= 0.13.7)
|
||||||
rack (>= 1.4.5)
|
rack (>= 1.4.5)
|
||||||
factory_bot (6.4.5)
|
factory_bot (6.2.1)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
factory_bot_rails (6.4.3)
|
factory_bot_rails (6.2.0)
|
||||||
factory_bot (~> 6.4)
|
factory_bot (~> 6.2.0)
|
||||||
railties (>= 5.0.0)
|
railties (>= 5.0.0)
|
||||||
faker (3.2.0)
|
faker (3.2.0)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
@@ -256,7 +256,7 @@ GEM
|
|||||||
fcm (1.0.8)
|
fcm (1.0.8)
|
||||||
faraday (>= 1.0.0, < 3.0)
|
faraday (>= 1.0.0, < 3.0)
|
||||||
googleauth (~> 1)
|
googleauth (~> 1)
|
||||||
ffi (1.16.3)
|
ffi (1.15.5)
|
||||||
ffi-compiler (1.0.1)
|
ffi-compiler (1.0.1)
|
||||||
ffi (>= 1.0.0)
|
ffi (>= 1.0.0)
|
||||||
rake
|
rake
|
||||||
@@ -440,7 +440,7 @@ GEM
|
|||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
railties (>= 4)
|
railties (>= 4)
|
||||||
request_store (~> 1.0)
|
request_store (~> 1.0)
|
||||||
loofah (2.22.0)
|
loofah (2.21.4)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
mail (2.8.1)
|
mail (2.8.1)
|
||||||
@@ -472,14 +472,14 @@ GEM
|
|||||||
activerecord (>= 5.2)
|
activerecord (>= 5.2)
|
||||||
net-http-persistent (4.0.2)
|
net-http-persistent (4.0.2)
|
||||||
connection_pool (~> 2.2)
|
connection_pool (~> 2.2)
|
||||||
net-imap (0.4.9)
|
net-imap (0.3.7)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-pop (0.1.2)
|
net-pop (0.1.2)
|
||||||
net-protocol
|
net-protocol
|
||||||
net-protocol (0.2.2)
|
net-protocol (0.2.1)
|
||||||
timeout
|
timeout
|
||||||
net-smtp (0.4.0)
|
net-smtp (0.3.3)
|
||||||
net-protocol
|
net-protocol
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
newrelic-sidekiq-metrics (1.6.2)
|
newrelic-sidekiq-metrics (1.6.2)
|
||||||
@@ -487,15 +487,15 @@ GEM
|
|||||||
sidekiq
|
sidekiq
|
||||||
newrelic_rpm (9.6.0)
|
newrelic_rpm (9.6.0)
|
||||||
base64
|
base64
|
||||||
nio4r (2.7.0)
|
nio4r (2.5.9)
|
||||||
nokogiri (1.16.0)
|
nokogiri (1.15.4)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.0-arm64-darwin)
|
nokogiri (1.15.4-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.0-x86_64-darwin)
|
nokogiri (1.15.4-x86_64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.0-x86_64-linux)
|
nokogiri (1.15.4-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
numo-narray (0.9.2.1)
|
numo-narray (0.9.2.1)
|
||||||
oauth (1.1.0)
|
oauth (1.1.0)
|
||||||
@@ -511,7 +511,7 @@ GEM
|
|||||||
rack (>= 1.2, < 4)
|
rack (>= 1.2, < 4)
|
||||||
snaky_hash (~> 2.0)
|
snaky_hash (~> 2.0)
|
||||||
version_gem (~> 1.1)
|
version_gem (~> 1.1)
|
||||||
omniauth (2.1.2)
|
omniauth (2.1.1)
|
||||||
hashie (>= 3.4.6)
|
hashie (>= 3.4.6)
|
||||||
rack (>= 2.2.3)
|
rack (>= 2.2.3)
|
||||||
rack-protection
|
rack-protection
|
||||||
@@ -553,7 +553,7 @@ GEM
|
|||||||
pry-rails (0.3.9)
|
pry-rails (0.3.9)
|
||||||
pry (>= 0.10.4)
|
pry (>= 0.10.4)
|
||||||
public_suffix (5.0.1)
|
public_suffix (5.0.1)
|
||||||
puma (6.4.2)
|
puma (6.3.1)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
pundit (2.3.0)
|
pundit (2.3.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
@@ -566,10 +566,10 @@ GEM
|
|||||||
rack (< 4)
|
rack (< 4)
|
||||||
rack-cors (2.0.1)
|
rack-cors (2.0.1)
|
||||||
rack (>= 2.0.0)
|
rack (>= 2.0.0)
|
||||||
rack-mini-profiler (3.2.0)
|
rack-mini-profiler (3.1.1)
|
||||||
rack (>= 1.2.0)
|
rack (>= 1.2.0)
|
||||||
rack-protection (3.1.0)
|
rack-protection (3.0.6)
|
||||||
rack (~> 2.2, >= 2.2.4)
|
rack
|
||||||
rack-proxy (0.7.6)
|
rack-proxy (0.7.6)
|
||||||
rack
|
rack
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
@@ -610,7 +610,7 @@ GEM
|
|||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
redis (5.0.6)
|
redis (5.0.6)
|
||||||
redis-client (>= 0.9.0)
|
redis-client (>= 0.9.0)
|
||||||
redis-client (0.19.0)
|
redis-client (0.18.0)
|
||||||
connection_pool
|
connection_pool
|
||||||
redis-namespace (1.10.0)
|
redis-namespace (1.10.0)
|
||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
@@ -709,13 +709,13 @@ GEM
|
|||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
selectize-rails (0.12.6)
|
selectize-rails (0.12.6)
|
||||||
semantic_range (3.0.0)
|
semantic_range (3.0.0)
|
||||||
sentry-rails (5.14.0)
|
sentry-rails (5.13.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
sentry-ruby (~> 5.14.0)
|
sentry-ruby (~> 5.13.0)
|
||||||
sentry-ruby (5.14.0)
|
sentry-ruby (5.13.0)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
sentry-sidekiq (5.14.0)
|
sentry-sidekiq (5.13.0)
|
||||||
sentry-ruby (~> 5.14.0)
|
sentry-ruby (~> 5.13.0)
|
||||||
sidekiq (>= 3.0)
|
sidekiq (>= 3.0)
|
||||||
sexp_processor (4.17.0)
|
sexp_processor (4.17.0)
|
||||||
shoulda-matchers (5.3.0)
|
shoulda-matchers (5.3.0)
|
||||||
@@ -725,7 +725,7 @@ GEM
|
|||||||
connection_pool (>= 2.3.0)
|
connection_pool (>= 2.3.0)
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
redis-client (>= 0.14.0)
|
redis-client (>= 0.14.0)
|
||||||
sidekiq-cron (1.12.0)
|
sidekiq-cron (1.11.0)
|
||||||
fugit (~> 1.8)
|
fugit (~> 1.8)
|
||||||
globalid (>= 1.0.1)
|
globalid (>= 1.0.1)
|
||||||
sidekiq (>= 6)
|
sidekiq (>= 6)
|
||||||
@@ -752,7 +752,7 @@ GEM
|
|||||||
spring-watcher-listen (2.1.0)
|
spring-watcher-listen (2.1.0)
|
||||||
listen (>= 2.7, < 4.0)
|
listen (>= 2.7, < 4.0)
|
||||||
spring (>= 4)
|
spring (>= 4)
|
||||||
sprockets (4.2.1)
|
sprockets (4.2.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (>= 2.2.4, < 4)
|
rack (>= 2.2.4, < 4)
|
||||||
sprockets-rails (3.4.2)
|
sprockets-rails (3.4.2)
|
||||||
@@ -766,11 +766,11 @@ GEM
|
|||||||
telephone_number (1.4.20)
|
telephone_number (1.4.20)
|
||||||
test-prof (1.2.1)
|
test-prof (1.2.1)
|
||||||
thor (1.3.0)
|
thor (1.3.0)
|
||||||
tilt (2.3.0)
|
tilt (2.2.0)
|
||||||
time_diff (0.3.0)
|
time_diff (0.3.0)
|
||||||
activesupport
|
activesupport
|
||||||
i18n
|
i18n
|
||||||
timeout (0.4.1)
|
timeout (0.4.0)
|
||||||
trailblazer-option (0.1.2)
|
trailblazer-option (0.1.2)
|
||||||
twilio-ruby (5.77.0)
|
twilio-ruby (5.77.0)
|
||||||
faraday (>= 0.9, < 3.0)
|
faraday (>= 0.9, < 3.0)
|
||||||
@@ -841,8 +841,8 @@ DEPENDENCIES
|
|||||||
activerecord-import
|
activerecord-import
|
||||||
acts-as-taggable-on
|
acts-as-taggable-on
|
||||||
administrate (>= 0.19.0)
|
administrate (>= 0.19.0)
|
||||||
administrate-field-active_storage (>= 1.0.1)
|
administrate-field-active_storage
|
||||||
administrate-field-belongs_to_search (>= 0.9.0)
|
administrate-field-belongs_to_search
|
||||||
annotate
|
annotate
|
||||||
attr_extras
|
attr_extras
|
||||||
audited (~> 5.4, >= 5.4.1)
|
audited (~> 5.4, >= 5.4.1)
|
||||||
@@ -870,7 +870,7 @@ DEPENDENCIES
|
|||||||
elastic-apm
|
elastic-apm
|
||||||
email_reply_trimmer
|
email_reply_trimmer
|
||||||
facebook-messenger
|
facebook-messenger
|
||||||
factory_bot_rails (>= 6.4.3)
|
factory_bot_rails
|
||||||
faker
|
faker
|
||||||
fcm
|
fcm
|
||||||
flag_shih_tzu
|
flag_shih_tzu
|
||||||
@@ -905,7 +905,7 @@ DEPENDENCIES
|
|||||||
neighbor
|
neighbor
|
||||||
newrelic-sidekiq-metrics (>= 1.6.2)
|
newrelic-sidekiq-metrics (>= 1.6.2)
|
||||||
newrelic_rpm
|
newrelic_rpm
|
||||||
omniauth (>= 2.1.2)
|
omniauth
|
||||||
omniauth-google-oauth2
|
omniauth-google-oauth2
|
||||||
omniauth-oauth2
|
omniauth-oauth2
|
||||||
omniauth-rails_csrf_protection (~> 1.0)
|
omniauth-rails_csrf_protection (~> 1.0)
|
||||||
@@ -918,7 +918,7 @@ DEPENDENCIES
|
|||||||
pundit
|
pundit
|
||||||
rack-attack (>= 6.7.0)
|
rack-attack (>= 6.7.0)
|
||||||
rack-cors
|
rack-cors
|
||||||
rack-mini-profiler (>= 3.2.0)
|
rack-mini-profiler (>= 3.1.1)
|
||||||
rack-timeout
|
rack-timeout
|
||||||
rails (~> 7.0.8.0)
|
rails (~> 7.0.8.0)
|
||||||
redis
|
redis
|
||||||
@@ -935,12 +935,12 @@ DEPENDENCIES
|
|||||||
scout_apm
|
scout_apm
|
||||||
scss_lint
|
scss_lint
|
||||||
seed_dump
|
seed_dump
|
||||||
sentry-rails (>= 5.14.0)
|
sentry-rails (>= 5.13.0)
|
||||||
sentry-ruby
|
sentry-ruby
|
||||||
sentry-sidekiq (>= 5.14.0)
|
sentry-sidekiq (>= 5.13.0)
|
||||||
shoulda-matchers
|
shoulda-matchers
|
||||||
sidekiq (>= 7.1.3)
|
sidekiq (>= 7.1.3)
|
||||||
sidekiq-cron (>= 1.12.0)
|
sidekiq-cron (>= 1.11.0)
|
||||||
simplecov (= 0.17.1)
|
simplecov (= 0.17.1)
|
||||||
slack-ruby-client (~> 2.2.0)
|
slack-ruby-client (~> 2.2.0)
|
||||||
spring
|
spring
|
||||||
|
|||||||
@@ -118,4 +118,4 @@ Thanks goes to all these [wonderful people](https://www.chatwoot.com/docs/contri
|
|||||||
<a href="https://github.com/chatwoot/chatwoot/graphs/contributors"><img src="https://opencollective.com/chatwoot/contributors.svg?width=890&button=false" /></a>
|
<a href="https://github.com/chatwoot/chatwoot/graphs/contributors"><img src="https://opencollective.com/chatwoot/contributors.svg?width=890&button=false" /></a>
|
||||||
|
|
||||||
|
|
||||||
*Chatwoot* © 2017-2024, Chatwoot Inc - Released under the MIT License.
|
*Chatwoot* © 2017-2023, Chatwoot Inc - Released under the MIT License.
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
3.3.1
|
3.1.0
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
2.7.0
|
2.6.0
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class ContactIdentifyAction
|
|||||||
# TODO: replace reject { |_k, v| v.blank? } with compact_blank when rails is upgraded
|
# TODO: replace reject { |_k, v| v.blank? } with compact_blank when rails is upgraded
|
||||||
@contact.discard_invalid_attrs if discard_invalid_attrs
|
@contact.discard_invalid_attrs if discard_invalid_attrs
|
||||||
@contact.save!
|
@contact.save!
|
||||||
Avatar::AvatarFromUrlJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present? && !@contact.avatar.attached?
|
Avatar::AvatarFromUrlJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_contact(base_contact, merge_contact)
|
def merge_contact(base_contact, merge_contact)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
@import 'components/flashes';
|
@import 'components/flashes';
|
||||||
@import 'components/form-actions';
|
@import 'components/form-actions';
|
||||||
@import 'components/main-content';
|
@import 'components/main-content';
|
||||||
|
@import 'components/navigation';
|
||||||
@import 'components/pagination';
|
@import 'components/pagination';
|
||||||
@import 'components/search';
|
@import 'components/search';
|
||||||
@import 'components/reports';
|
@import 'components/reports';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
html {
|
html {
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-size: 16px;
|
font-size: 10px;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
.attribute-data {
|
.attribute-data {
|
||||||
float: left;
|
float: left;
|
||||||
margin-bottom: $base-spacing;
|
margin-bottom: $base-spacing;
|
||||||
margin-left: 1.25rem;
|
margin-left: 2rem;
|
||||||
width: calc(84% - 0.625rem);
|
width: calc(84% - 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.attribute--nested {
|
.attribute--nested {
|
||||||
|
|||||||
@@ -9,22 +9,22 @@
|
|||||||
|
|
||||||
.field-unit__label {
|
.field-unit__label {
|
||||||
float: left;
|
float: left;
|
||||||
margin-left: 0.625rem;
|
margin-left: 1rem;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
width: calc(15% - 0.625rem);
|
width: calc(15% - 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-unit__field {
|
.field-unit__field {
|
||||||
float: left;
|
float: left;
|
||||||
margin-left: 1.25rem;
|
margin-left: 2rem;
|
||||||
max-width: 31.15rem;
|
max-width: 50rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-unit--nested {
|
.field-unit--nested {
|
||||||
border: $base-border;
|
border: $base-border;
|
||||||
margin-left: 7.5%;
|
margin-left: 7.5%;
|
||||||
max-width: 37.5rem;
|
max-width: 60rem;
|
||||||
padding: $small-spacing;
|
padding: $small-spacing;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
.form-actions {
|
.form-actions {
|
||||||
margin-left: calc(15% + 1.25rem);
|
margin-left: calc(15% + 2rem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,6 @@
|
|||||||
table {
|
table {
|
||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
|
||||||
margin-top: $space-two;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content__header {
|
.main-content__header {
|
||||||
@@ -24,7 +20,7 @@
|
|||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
border-bottom: 1px solid $color-border;
|
border-bottom: 1px solid $color-border;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 3.5rem;
|
min-height: 5.6rem;
|
||||||
padding: $space-small $space-normal;
|
padding: $space-small $space-normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
.logo-brand {
|
||||||
|
margin-bottom: $space-normal;
|
||||||
|
padding: $space-normal $space-smaller $space-small;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-bottom: $space-smaller;
|
||||||
|
max-height: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation {
|
||||||
|
background: $white;
|
||||||
|
border-right: 1px solid $color-border;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: $font-size-default;
|
||||||
|
font-weight: $font-weight-medium;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
left: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: auto;
|
||||||
|
padding: $space-normal;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 21rem;
|
||||||
|
z-index: 1023;
|
||||||
|
|
||||||
|
li {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $color-gray;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
min-width: $space-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: $space-slab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation__link {
|
||||||
|
background-color: transparent;
|
||||||
|
color: $color-gray;
|
||||||
|
display: block;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: $space-smaller;
|
||||||
|
padding: $space-small;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $blue;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&.navigation__link--active {
|
||||||
|
background-color: $color-background;
|
||||||
|
border-radius: $base-border-radius;
|
||||||
|
color: $blue;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout {
|
||||||
|
bottom: $space-normal;
|
||||||
|
left: $space-normal;
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-version {
|
||||||
|
color: $color-gray;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
padding-top: $space-smaller;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
.search {
|
.search {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: 1.25rem;
|
margin-right: 2rem;
|
||||||
max-width: 27.5rem;
|
max-width: 44rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Typography
|
// Typography
|
||||||
$base-font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
$base-font-family: PlusJakarta, Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||||
sans-serif !default;
|
sans-serif !default;
|
||||||
$heading-font-family: $base-font-family !default;
|
$heading-font-family: $base-font-family !default;
|
||||||
|
|
||||||
$base-font-size: 16px !default;
|
$base-font-size: 14px !default;
|
||||||
|
|
||||||
$base-line-height: 1.5 !default;
|
$base-line-height: 1.5 !default;
|
||||||
$heading-line-height: 1.2 !default;
|
$heading-line-height: 1.2 !default;
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
// Font sizes
|
// Font sizes
|
||||||
$font-size-nano: 0.5rem;
|
$font-size-nano: 0.8rem;
|
||||||
$font-size-micro: 0.675rem;
|
$font-size-micro: 1.0rem;
|
||||||
$font-size-mini: 0.75rem;
|
$font-size-mini: 1.2rem;
|
||||||
$font-size-small: 0.875rem;
|
$font-size-small: 1.4rem;
|
||||||
$font-size-default: 1rem;
|
$font-size-default: 1.6rem;
|
||||||
$font-size-medium: 1.125rem;
|
$font-size-medium: 1.8rem;
|
||||||
$font-size-large: 1.375rem;
|
$font-size-large: 2.2rem;
|
||||||
$font-size-big: 1.5rem;
|
$font-size-big: 2.4rem;
|
||||||
$font-size-bigger: 1.75rem;
|
$font-size-bigger: 3.0rem;
|
||||||
$font-size-mega: 2.125rem;
|
$font-size-mega: 3.4rem;
|
||||||
$font-size-giga: 2.5rem;
|
$font-size-giga: 4.0rem;
|
||||||
|
|
||||||
// spaces
|
// spaces
|
||||||
$zero: 0;
|
$zero: 0;
|
||||||
$space-micro: 0.125rem;
|
$space-micro: 0.2rem;
|
||||||
$space-smaller: 0.25rem;
|
$space-smaller: 0.4rem;
|
||||||
$space-small: 0.5rem;
|
$space-small: 0.8rem;
|
||||||
$space-one: 0.675rem;
|
$space-one: 1rem;
|
||||||
$space-slab: 0.75rem;
|
$space-slab: 1.2rem;
|
||||||
$space-normal: 1rem;
|
$space-normal: 1.6rem;
|
||||||
$space-two: 1.25rem;
|
$space-two: 2.0rem;
|
||||||
$space-medium: 1.5rem;
|
$space-medium: 2.4rem;
|
||||||
$space-large: 2rem;
|
$space-large: 3.2rem;
|
||||||
$space-larger: 3rem;
|
$space-larger: 4.8rem;
|
||||||
$space-jumbo: 4rem;
|
$space-jumbo: 6.4rem;
|
||||||
$space-mega: 6.25rem;
|
$space-mega: 10.0rem;
|
||||||
|
|
||||||
// font-weight
|
// font-weight
|
||||||
$font-weight-feather: 100;
|
$font-weight-feather: 100;
|
||||||
|
|||||||
@@ -25,9 +25,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
build_contact_inbox
|
build_contact_inbox
|
||||||
build_message
|
build_message
|
||||||
end
|
end
|
||||||
rescue Koala::Facebook::AuthenticationError => e
|
rescue Koala::Facebook::AuthenticationError
|
||||||
Rails.logger.warn("Facebook authentication error for inbox: #{@inbox.id} with error: #{e.message}")
|
|
||||||
Rails.logger.error e
|
|
||||||
@inbox.channel.authorization_error!
|
@inbox.channel.authorization_error!
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception
|
ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception
|
||||||
@@ -110,15 +108,11 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# rubocop:disable Metrics/AbcSize
|
|
||||||
# rubocop:disable Metrics/MethodLength
|
|
||||||
def contact_params
|
def contact_params
|
||||||
begin
|
begin
|
||||||
k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook?
|
k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook?
|
||||||
result = k.get_object(@sender_id) || {}
|
result = k.get_object(@sender_id) || {}
|
||||||
rescue Koala::Facebook::AuthenticationError => e
|
rescue Koala::Facebook::AuthenticationError
|
||||||
Rails.logger.warn("Facebook authentication error for inbox: #{@inbox.id} with error: #{e.message}")
|
|
||||||
Rails.logger.error e
|
|
||||||
@inbox.channel.authorization_error!
|
@inbox.channel.authorization_error!
|
||||||
raise
|
raise
|
||||||
rescue Koala::Facebook::ClientError => e
|
rescue Koala::Facebook::ClientError => e
|
||||||
@@ -136,6 +130,4 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
end
|
end
|
||||||
process_contact_params_result(result)
|
process_contact_params_result(result)
|
||||||
end
|
end
|
||||||
# rubocop:enable Metrics/AbcSize
|
|
||||||
# rubocop:enable Metrics/MethodLength
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ class Messages::Instagram::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
build_message
|
build_message
|
||||||
end
|
end
|
||||||
rescue Koala::Facebook::AuthenticationError => e
|
rescue Koala::Facebook::AuthenticationError
|
||||||
Rails.logger.warn("Instagram authentication error for inbox: #{@inbox.id} with error: #{e.message}")
|
|
||||||
Rails.logger.error e
|
|
||||||
@inbox.channel.authorization_error!
|
@inbox.channel.authorization_error!
|
||||||
raise
|
raise
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
@@ -48,10 +46,6 @@ class Messages::Instagram::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
@outgoing_echo ? recipient_id : sender_id
|
@outgoing_echo ? recipient_id : sender_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_is_unsupported?
|
|
||||||
message[:is_unsupported].present? && @messaging[:message][:is_unsupported] == true
|
|
||||||
end
|
|
||||||
|
|
||||||
def sender_id
|
def sender_id
|
||||||
@messaging[:sender][:id]
|
@messaging[:sender][:id]
|
||||||
end
|
end
|
||||||
@@ -122,7 +116,7 @@ class Messages::Instagram::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
end
|
end
|
||||||
|
|
||||||
def message_params
|
def message_params
|
||||||
params = {
|
{
|
||||||
account_id: conversation.account_id,
|
account_id: conversation.account_id,
|
||||||
inbox_id: conversation.inbox_id,
|
inbox_id: conversation.inbox_id,
|
||||||
message_type: message_type,
|
message_type: message_type,
|
||||||
@@ -133,9 +127,6 @@ class Messages::Instagram::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
in_reply_to_external_id: message_reply_attributes
|
in_reply_to_external_id: message_reply_attributes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params[:content_attributes][:is_unsupported] = true if message_is_unsupported?
|
|
||||||
params
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def already_sent_from_chatwoot?
|
def already_sent_from_chatwoot?
|
||||||
|
|||||||
@@ -149,8 +149,7 @@ class Messages::MessageBuilder
|
|||||||
content_type: @params[:content_type],
|
content_type: @params[:content_type],
|
||||||
items: @items,
|
items: @items,
|
||||||
in_reply_to: @in_reply_to,
|
in_reply_to: @in_reply_to,
|
||||||
echo_id: @params[:echo_id],
|
echo_id: @params[:echo_id]
|
||||||
source_id: @params[:source_id]
|
|
||||||
}.merge(external_created_at).merge(automation_rule_id).merge(campaign_id).merge(template_params)
|
}.merge(external_created_at).merge(automation_rule_id).merge(campaign_id).merge(template_params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
class NotificationBuilder
|
class NotificationBuilder
|
||||||
pattr_initialize [:notification_type!, :user!, :account!, :primary_actor!, :secondary_actor]
|
pattr_initialize [:notification_type!, :user!, :account!, :primary_actor!]
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
|
return unless user_subscribed_to_notification?
|
||||||
|
|
||||||
build_notification
|
build_notification
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def current_user
|
def secondary_actor
|
||||||
Current.user
|
Current.user
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_subscribed_to_notification?
|
def user_subscribed_to_notification?
|
||||||
notification_setting = user.notification_settings.find_by(account_id: account.id)
|
notification_setting = user.notification_settings.find_by(account_id: account.id)
|
||||||
# added for the case where an assignee might be removed from the account but remains in conversation
|
# added for the case where an assignee might be removed from the account but remains in conversation
|
||||||
return false if notification_setting.blank?
|
return if notification_setting.blank?
|
||||||
|
|
||||||
return true if notification_setting.public_send("email_#{notification_type}?")
|
return true if notification_setting.public_send("email_#{notification_type}?")
|
||||||
return true if notification_setting.public_send("push_#{notification_type}?")
|
return true if notification_setting.public_send("push_#{notification_type}?")
|
||||||
@@ -23,15 +25,11 @@ class NotificationBuilder
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_notification
|
def build_notification
|
||||||
# Create conversation_creation notification only if user is subscribed to it
|
|
||||||
return if notification_type == 'conversation_creation' && !user_subscribed_to_notification?
|
|
||||||
|
|
||||||
user.notifications.create!(
|
user.notifications.create!(
|
||||||
notification_type: notification_type,
|
notification_type: notification_type,
|
||||||
account: account,
|
account: account,
|
||||||
primary_actor: primary_actor,
|
primary_actor: primary_actor,
|
||||||
# secondary_actor is secondary_actor if present, else current_user
|
secondary_actor: secondary_actor
|
||||||
secondary_actor: secondary_actor || current_user
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -46,14 +46,6 @@ class V2::ReportBuilder
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def short_summary
|
|
||||||
{
|
|
||||||
conversations_count: conversations.count,
|
|
||||||
avg_first_response_time: avg_first_response_time_summary,
|
|
||||||
avg_resolution_time: avg_resolution_time_summary
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def conversation_metrics
|
def conversation_metrics
|
||||||
if params[:type].equal?(:account)
|
if params[:type].equal?(:account)
|
||||||
live_conversations
|
live_conversations
|
||||||
|
|||||||
@@ -10,18 +10,11 @@ class Api::V1::Accounts::AgentBotsController < Api::V1::Accounts::BaseController
|
|||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@agent_bot = Current.account.agent_bots.create!(permitted_params.except(:avatar_url))
|
@agent_bot = Current.account.agent_bots.create!(permitted_params)
|
||||||
process_avatar_from_url
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@agent_bot.update!(permitted_params.except(:avatar_url))
|
@agent_bot.update!(permitted_params)
|
||||||
process_avatar_from_url
|
|
||||||
end
|
|
||||||
|
|
||||||
def avatar
|
|
||||||
@agent_bot.avatar.purge if @agent_bot.avatar.attached?
|
|
||||||
@agent_bot
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@@ -37,10 +30,6 @@ class Api::V1::Accounts::AgentBotsController < Api::V1::Accounts::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params
|
||||||
params.permit(:name, :description, :outgoing_url, :avatar, :avatar_url, :bot_type, bot_config: [:csml_content])
|
params.permit(:name, :description, :outgoing_url, :bot_type, bot_config: [:csml_content])
|
||||||
end
|
|
||||||
|
|
||||||
def process_avatar_from_url
|
|
||||||
::Avatar::AvatarFromUrlJob.perform_later(@agent_bot, params[:avatar_url]) if params[:avatar_url].present?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -83,14 +83,14 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
@contact = Current.account.contacts.new(permitted_params.except(:avatar_url))
|
@contact = Current.account.contacts.new(permitted_params.except(:avatar_url))
|
||||||
@contact.save!
|
@contact.save!
|
||||||
@contact_inbox = build_contact_inbox
|
@contact_inbox = build_contact_inbox
|
||||||
process_avatar_from_url
|
process_avatar
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@contact.assign_attributes(contact_update_params)
|
@contact.assign_attributes(contact_update_params)
|
||||||
@contact.save!
|
@contact.save!
|
||||||
process_avatar_from_url
|
process_avatar if permitted_params[:avatar].present? || permitted_params[:avatar_url].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@@ -174,7 +174,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
@contact = Current.account.contacts.includes(contact_inboxes: [:inbox]).find(params[:id])
|
@contact = Current.account.contacts.includes(contact_inboxes: [:inbox]).find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_avatar_from_url
|
def process_avatar
|
||||||
::Avatar::AvatarFromUrlJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present?
|
::Avatar::AvatarFromUrlJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ class Api::V1::Accounts::Conversations::AssignmentsController < Api::V1::Account
|
|||||||
|
|
||||||
def set_agent
|
def set_agent
|
||||||
@agent = Current.account.users.find_by(id: params[:assignee_id])
|
@agent = Current.account.users.find_by(id: params[:assignee_id])
|
||||||
@conversation.assignee = @agent
|
@conversation.update_assignee(@agent)
|
||||||
@conversation.save!
|
|
||||||
render_agent
|
render_agent
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -18,15 +18,6 @@ class Api::V1::Accounts::Conversations::MessagesController < Api::V1::Accounts::
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def retry
|
|
||||||
return if message.blank?
|
|
||||||
|
|
||||||
message.update!(status: :sent, content_attributes: {})
|
|
||||||
::SendReplyJob.perform_later(message.id)
|
|
||||||
rescue StandardError => e
|
|
||||||
render_could_not_create_error(e.message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def translate
|
def translate
|
||||||
return head :ok if already_translated_content_available?
|
return head :ok if already_translated_content_available?
|
||||||
|
|
||||||
|
|||||||
@@ -60,26 +60,13 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
|||||||
end
|
end
|
||||||
|
|
||||||
def toggle_status
|
def toggle_status
|
||||||
# FIXME: move this logic into a service object
|
if params[:status].present?
|
||||||
if pending_to_open_by_bot?
|
|
||||||
@conversation.bot_handoff!
|
|
||||||
elsif params[:status].present?
|
|
||||||
set_conversation_status
|
set_conversation_status
|
||||||
@status = @conversation.save!
|
@status = @conversation.save!
|
||||||
else
|
else
|
||||||
@status = @conversation.toggle_status
|
@status = @conversation.toggle_status
|
||||||
end
|
end
|
||||||
assign_conversation if should_assign_conversation?
|
assign_conversation if @conversation.status == 'open' && Current.user.is_a?(User) && Current.user&.agent?
|
||||||
end
|
|
||||||
|
|
||||||
def pending_to_open_by_bot?
|
|
||||||
return false unless Current.user.is_a?(AgentBot)
|
|
||||||
|
|
||||||
@conversation.status == 'pending' && params[:status] == 'open'
|
|
||||||
end
|
|
||||||
|
|
||||||
def should_assign_conversation?
|
|
||||||
@conversation.status == 'open' && Current.user.is_a?(User) && Current.user&.agent?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def toggle_priority
|
def toggle_priority
|
||||||
@@ -123,8 +110,8 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
|||||||
end
|
end
|
||||||
|
|
||||||
def assign_conversation
|
def assign_conversation
|
||||||
@conversation.assignee = current_user
|
@agent = Current.account.users.find(current_user.id)
|
||||||
@conversation.save!
|
@conversation.update_assignee(@agent)
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation
|
def conversation
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseController
|
||||||
RESULTS_PER_PAGE = 15
|
RESULTS_PER_PAGE = 15
|
||||||
include DateRangeHelper
|
|
||||||
|
|
||||||
before_action :fetch_notification, only: [:update, :destroy, :snooze]
|
before_action :fetch_notification, only: [:update]
|
||||||
before_action :set_primary_actor, only: [:read_all]
|
before_action :set_primary_actor, only: [:read_all]
|
||||||
before_action :set_current_page, only: [:index]
|
before_action :set_current_page, only: [:index]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@unread_count = notification_finder.unread_count
|
@unread_count = current_user.notifications.where(account_id: current_account.id, read_at: nil).count
|
||||||
@notifications = notification_finder.perform
|
@count = notifications.count
|
||||||
@count = notification_finder.count
|
@notifications = notifications.page(@current_page).per(RESULTS_PER_PAGE)
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_all
|
def read_all
|
||||||
@@ -29,21 +28,11 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
|
|||||||
render json: @notification
|
render json: @notification
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
|
||||||
@notification.destroy
|
|
||||||
head :ok
|
|
||||||
end
|
|
||||||
|
|
||||||
def unread_count
|
def unread_count
|
||||||
@unread_count = notification_finder.unread_count
|
@unread_count = current_user.notifications.where(account_id: current_account.id, read_at: nil).count
|
||||||
render json: @unread_count
|
render json: @unread_count
|
||||||
end
|
end
|
||||||
|
|
||||||
def snooze
|
|
||||||
@notification.update(snoozed_until: parse_date_time(params[:snoozed_until].to_s)) if params[:snoozed_until]
|
|
||||||
render json: @notification
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_primary_actor
|
def set_primary_actor
|
||||||
@@ -61,7 +50,7 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
|
|||||||
@current_page = params[:page] || 1
|
@current_page = params[:page] || 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def notification_finder
|
def notifications
|
||||||
@notification_finder ||= NotificationFinder.new(Current.user, Current.account, params)
|
@notifications ||= current_user.notifications.where(account_id: current_account.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,14 +14,7 @@ module AccessTokenAuthHelper
|
|||||||
render_unauthorized('Invalid Access Token') && return if @access_token.blank?
|
render_unauthorized('Invalid Access Token') && return if @access_token.blank?
|
||||||
|
|
||||||
@resource = @access_token.owner
|
@resource = @access_token.owner
|
||||||
Current.user = @resource if allowed_current_user_type?(@resource)
|
Current.user = @resource if current_user.is_a?(User)
|
||||||
end
|
|
||||||
|
|
||||||
def allowed_current_user_type?(resource)
|
|
||||||
return true if resource.is_a?(User)
|
|
||||||
return true if resource.is_a?(AgentBot)
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_bot_access_token!
|
def validate_bot_access_token!
|
||||||
|
|||||||
@@ -9,15 +9,13 @@ class Platform::Api::V1::AgentBotsController < PlatformController
|
|||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@resource = AgentBot.new(agent_bot_params.except(:avatar_url))
|
@resource = AgentBot.new(agent_bot_params)
|
||||||
@resource.save!
|
@resource.save!
|
||||||
process_avatar_from_url
|
|
||||||
@platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource)
|
@platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@resource.update!(agent_bot_params.except(:avatar_url))
|
@resource.update!(agent_bot_params)
|
||||||
process_avatar_from_url
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@@ -25,11 +23,6 @@ class Platform::Api::V1::AgentBotsController < PlatformController
|
|||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def avatar
|
|
||||||
@resource.avatar.purge if @resource.avatar.attached?
|
|
||||||
@resource
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_resource
|
def set_resource
|
||||||
@@ -37,10 +30,6 @@ class Platform::Api::V1::AgentBotsController < PlatformController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def agent_bot_params
|
def agent_bot_params
|
||||||
params.permit(:name, :description, :account_id, :outgoing_url, :avatar, :avatar_url)
|
params.permit(:name, :description, :account_id, :outgoing_url)
|
||||||
end
|
|
||||||
|
|
||||||
def process_avatar_from_url
|
|
||||||
::Avatar::AvatarFromUrlJob.perform_later(@resource, params[:avatar_url]) if params[:avatar_url].present?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
class Public::Api::V1::Portals::BaseController < PublicController
|
class Public::Api::V1::Portals::BaseController < PublicController
|
||||||
before_action :show_plain_layout
|
before_action :show_plain_layout
|
||||||
before_action :set_color_scheme
|
before_action :set_color_scheme
|
||||||
before_action :set_global_config
|
|
||||||
around_action :set_locale
|
around_action :set_locale
|
||||||
after_action :allow_iframe_requests
|
after_action :allow_iframe_requests
|
||||||
|
|
||||||
@@ -12,7 +11,11 @@ class Public::Api::V1::Portals::BaseController < PublicController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_color_scheme
|
def set_color_scheme
|
||||||
@theme_from_params = params[:theme] if %w[dark light].include?(params[:theme])
|
@theme = if %w[dark light].include?(params[:theme])
|
||||||
|
params[:theme]
|
||||||
|
else
|
||||||
|
'system'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def portal
|
def portal
|
||||||
@@ -61,8 +64,4 @@ class Public::Api::V1::Portals::BaseController < PublicController
|
|||||||
portal
|
portal
|
||||||
render 'public/api/v1/portals/error/404', status: :not_found
|
render 'public/api/v1/portals/error/404', status: :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_global_config
|
|
||||||
@global_config = GlobalConfig.get('LOGO_THUMBNAIL', 'BRAND_NAME', 'BRAND_URL')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,41 +1,21 @@
|
|||||||
class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController
|
class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController
|
||||||
before_action :set_config
|
|
||||||
before_action :allowed_configs
|
|
||||||
def show
|
def show
|
||||||
|
@allowed_configs = %w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET]
|
||||||
# ref: https://github.com/rubocop/rubocop/issues/7767
|
# ref: https://github.com/rubocop/rubocop/issues/7767
|
||||||
# rubocop:disable Style/HashTransformValues
|
# rubocop:disable Style/HashTransformValues
|
||||||
@app_config = InstallationConfig.where(name: @allowed_configs)
|
@fb_config = InstallationConfig.where(name: @allowed_configs)
|
||||||
.pluck(:name, :serialized_value)
|
.pluck(:name, :serialized_value)
|
||||||
.map { |name, serialized_value| [name, serialized_value['value']] }
|
.map { |name, serialized_value| [name, serialized_value['value']] }
|
||||||
.to_h
|
.to_h
|
||||||
# rubocop:enable Style/HashTransformValues
|
# rubocop:enable Style/HashTransformValues
|
||||||
@installation_configs = ConfigLoader.new.general_configs.each_with_object({}) do |config_hash, result|
|
|
||||||
result[config_hash['name']] = config_hash.except('name')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
params['app_config'].each do |key, value|
|
params['app_config'].each do |key, value|
|
||||||
next unless @allowed_configs.include?(key)
|
|
||||||
|
|
||||||
i = InstallationConfig.where(name: key).first_or_create(value: value, locked: false)
|
i = InstallationConfig.where(name: key).first_or_create(value: value, locked: false)
|
||||||
i.value = value
|
i.value = value
|
||||||
i.save!
|
i.save!
|
||||||
end
|
end
|
||||||
# rubocop:disable Rails/I18nLocaleTexts
|
redirect_to super_admin_app_config_url
|
||||||
redirect_to super_admin_settings_path, notice: 'App Configs updated successfully'
|
|
||||||
# rubocop:enable Rails/I18nLocaleTexts
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_config
|
|
||||||
@config = params[:config]
|
|
||||||
end
|
|
||||||
|
|
||||||
def allowed_configs
|
|
||||||
@allowed_configs = %w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
SuperAdmin::AppConfigsController.prepend_mod_with('SuperAdmin::AppConfigsController')
|
|
||||||
|
|||||||
@@ -20,13 +20,4 @@ class SuperAdmin::ApplicationController < Administrate::ApplicationController
|
|||||||
params.fetch(resource_name, {}).fetch(:direction, 'desc')
|
params.fetch(resource_name, {}).fetch(:direction, 'desc')
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def invalid_action_perfomed
|
|
||||||
# rubocop:disable Rails/I18nLocaleTexts
|
|
||||||
flash[:error] = 'Invalid action performed'
|
|
||||||
# rubocop:enable Rails/I18nLocaleTexts
|
|
||||||
redirect_back(fallback_location: root_path)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ class SuperAdmin::Devise::SessionsController < Devise::SessionsController
|
|||||||
|
|
||||||
true
|
true
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
Rails.logger.error e.message
|
@error_message = e.message
|
||||||
@error_message = 'Invalid credentials. Please try again.'
|
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
class SuperAdmin::InstallationConfigsController < SuperAdmin::ApplicationController
|
class SuperAdmin::InstallationConfigsController < SuperAdmin::ApplicationController
|
||||||
rescue_from ActiveRecord::RecordNotUnique, :with => :invalid_action_perfomed
|
|
||||||
# Overwrite any of the RESTful controller actions to implement custom behavior
|
# 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.
|
# For example, you may want to send an email after a foo is updated.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
class SuperAdmin::SettingsController < SuperAdmin::ApplicationController
|
|
||||||
def show; end
|
|
||||||
|
|
||||||
def refresh
|
|
||||||
Internal::CheckNewVersionsJob.perform_now
|
|
||||||
# rubocop:disable Rails/I18nLocaleTexts
|
|
||||||
redirect_to super_admin_settings_path, notice: 'Instance status refreshed'
|
|
||||||
# rubocop:enable Rails/I18nLocaleTexts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -30,7 +30,11 @@ class AccessTokenDashboard < Administrate::BaseDashboard
|
|||||||
# SHOW_PAGE_ATTRIBUTES
|
# SHOW_PAGE_ATTRIBUTES
|
||||||
# an array of attributes that will be displayed on the model's show page.
|
# an array of attributes that will be displayed on the model's show page.
|
||||||
SHOW_PAGE_ATTRIBUTES = %i[
|
SHOW_PAGE_ATTRIBUTES = %i[
|
||||||
|
owner
|
||||||
|
id
|
||||||
token
|
token
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
# FORM_ATTRIBUTES
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ class AgentBotDashboard < Administrate::BaseDashboard
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
outgoing_url
|
outgoing_url
|
||||||
access_token
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
# FORM_ATTRIBUTES
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ class PlatformAppDashboard < Administrate::BaseDashboard
|
|||||||
name
|
name
|
||||||
created_at
|
created_at
|
||||||
updated_at
|
updated_at
|
||||||
access_token
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
# FORM_ATTRIBUTES
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ class UserDashboard < Administrate::BaseDashboard
|
|||||||
updated_at: Field::DateTime,
|
updated_at: Field::DateTime,
|
||||||
pubsub_token: Field::String,
|
pubsub_token: Field::String,
|
||||||
type: Field::Select.with_options(collection: [nil, 'SuperAdmin']),
|
type: Field::Select.with_options(collection: [nil, 'SuperAdmin']),
|
||||||
accounts: CountField,
|
accounts: CountField
|
||||||
access_token: Field::HasOne
|
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
# COLLECTION_ATTRIBUTES
|
# COLLECTION_ATTRIBUTES
|
||||||
@@ -68,7 +67,6 @@ class UserDashboard < Administrate::BaseDashboard
|
|||||||
updated_at
|
updated_at
|
||||||
confirmed_at
|
confirmed_at
|
||||||
account_users
|
account_users
|
||||||
access_token
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
# FORM_ATTRIBUTES
|
||||||
|
|||||||
@@ -18,9 +18,4 @@ class ContactDrop < BaseDrop
|
|||||||
def last_name
|
def last_name
|
||||||
@obj.try(:name).try(:split).try(:last).try(:capitalize) if @obj.try(:name).try(:split).try(:size) > 1
|
@obj.try(:name).try(:split).try(:last).try(:capitalize) if @obj.try(:name).try(:split).try(:size) > 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_attribute
|
|
||||||
custom_attributes = @obj.try(:custom_attributes) || {}
|
|
||||||
custom_attributes.transform_keys(&:to_s)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,11 +19,6 @@ class ConversationDrop < BaseDrop
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_attribute
|
|
||||||
custom_attributes = @obj.try(:custom_attributes) || {}
|
|
||||||
custom_attributes.transform_keys(&:to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def message_sender_name(sender)
|
def message_sender_name(sender)
|
||||||
|
|||||||
@@ -3,21 +3,13 @@ class ConversationFinder
|
|||||||
|
|
||||||
DEFAULT_STATUS = 'open'.freeze
|
DEFAULT_STATUS = 'open'.freeze
|
||||||
SORT_OPTIONS = {
|
SORT_OPTIONS = {
|
||||||
'last_activity_at_asc' => %w[sort_on_last_activity_at asc],
|
latest: 'latest',
|
||||||
'last_activity_at_desc' => %w[sort_on_last_activity_at desc],
|
sort_on_created_at: 'sort_on_created_at',
|
||||||
'created_at_asc' => %w[sort_on_created_at asc],
|
last_user_message_at: 'last_user_message_at',
|
||||||
'created_at_desc' => %w[sort_on_created_at desc],
|
sort_on_priority: 'sort_on_priority',
|
||||||
'priority_asc' => %w[sort_on_priority asc],
|
sort_on_waiting_since: 'sort_on_waiting_since'
|
||||||
'priority_desc' => %w[sort_on_priority desc],
|
|
||||||
'waiting_since_asc' => %w[sort_on_waiting_since asc],
|
|
||||||
'waiting_since_desc' => %w[sort_on_waiting_since desc],
|
|
||||||
|
|
||||||
# To be removed in v3.5.0
|
|
||||||
'latest' => %w[sort_on_last_activity_at desc],
|
|
||||||
'sort_on_created_at' => %w[sort_on_created_at asc],
|
|
||||||
'sort_on_priority' => %w[sort_on_priority desc],
|
|
||||||
'sort_on_waiting_since' => %w[sort_on_waiting_since asc]
|
|
||||||
}.with_indifferent_access
|
}.with_indifferent_access
|
||||||
|
|
||||||
# assumptions
|
# assumptions
|
||||||
# inbox_id if not given, take from all conversations, else specific to inbox
|
# inbox_id if not given, take from all conversations, else specific to inbox
|
||||||
# assignee_type if not given, take 'all'
|
# assignee_type if not given, take 'all'
|
||||||
@@ -167,8 +159,7 @@ class ConversationFinder
|
|||||||
@conversations = @conversations.includes(
|
@conversations = @conversations.includes(
|
||||||
:taggings, :inbox, { assignee: { avatar_attachment: [:blob] } }, { contact: { avatar_attachment: [:blob] } }, :team, :contact_inbox
|
:taggings, :inbox, { assignee: { avatar_attachment: [:blob] } }, { contact: { avatar_attachment: [:blob] } }, :team, :contact_inbox
|
||||||
)
|
)
|
||||||
|
sort_by = SORT_OPTIONS[params[:sort_by]] || SORT_OPTIONS['latest']
|
||||||
sort_by, sort_order = SORT_OPTIONS[params[:sort_by]] || SORT_OPTIONS['last_activity_at_desc']
|
@conversations.send(sort_by).page(current_page)
|
||||||
@conversations.send(sort_by, sort_order).page(current_page).per(ENV.fetch('CONVERSATION_RESULTS_PER_PAGE', '25').to_i)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
class NotificationFinder
|
|
||||||
attr_reader :current_user, :current_account, :params
|
|
||||||
|
|
||||||
RESULTS_PER_PAGE = 15
|
|
||||||
|
|
||||||
def initialize(current_user, current_account, params = {})
|
|
||||||
@current_user = current_user
|
|
||||||
@current_account = current_account
|
|
||||||
@params = params
|
|
||||||
set_up
|
|
||||||
end
|
|
||||||
|
|
||||||
def perform
|
|
||||||
notifications
|
|
||||||
end
|
|
||||||
|
|
||||||
def unread_count
|
|
||||||
@notifications.where(read_at: nil).count
|
|
||||||
end
|
|
||||||
|
|
||||||
def count
|
|
||||||
@notifications.count
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_up
|
|
||||||
find_all_notifications
|
|
||||||
filter_by_status
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_all_notifications
|
|
||||||
@notifications = current_user.notifications.where(account_id: @current_account.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_by_status
|
|
||||||
@notifications = @notifications.where('snoozed_until > ?', DateTime.now.utc) if params[:status] == 'snoozed'
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_page
|
|
||||||
params[:page] || 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def notifications
|
|
||||||
@notifications.page(current_page).per(RESULTS_PER_PAGE).order(last_activity_at: :desc)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -53,7 +53,7 @@ module Api::V1::InboxesHelper
|
|||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
raise StandardError, e.message
|
raise StandardError, e.message
|
||||||
ensure
|
ensure
|
||||||
Rails.logger.error "[Api::V1::InboxesHelper] check_imap_connection failed with #{e.message}" if e.present?
|
ChatwootExceptionTracker.new(e).capture_exception if e.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_smtp_connection(channel_data, smtp)
|
def check_smtp_connection(channel_data, smtp)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ module Api::V2::Accounts::ReportsHelper
|
|||||||
business_hours: ActiveModel::Type::Boolean.new.cast(params[:business_hours])
|
business_hours: ActiveModel::Type::Boolean.new.cast(params[:business_hours])
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).short_summary
|
).summary
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -6,17 +6,7 @@ module PortalHelper
|
|||||||
|
|
||||||
def generate_portal_bg(portal_color, theme)
|
def generate_portal_bg(portal_color, theme)
|
||||||
bg_image = theme == 'dark' ? 'hexagon-dark.svg' : 'hexagon-light.svg'
|
bg_image = theme == 'dark' ? 'hexagon-dark.svg' : 'hexagon-light.svg'
|
||||||
"url(/assets/images/hc/#{bg_image}) #{generate_portal_bg_color(portal_color, theme)}"
|
"background: url(/assets/images/hc/#{bg_image}) #{generate_portal_bg_color(portal_color, theme)}"
|
||||||
end
|
|
||||||
|
|
||||||
def generate_gradient_to_bottom(theme)
|
|
||||||
base_color = theme == 'dark' ? '#151718' : 'white'
|
|
||||||
"linear-gradient(to bottom, transparent, #{base_color})"
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate_portal_hover_color(portal_color, theme)
|
|
||||||
base_color = theme == 'dark' ? '#1B1B1B' : '#F9F9F9'
|
|
||||||
"color-mix(in srgb, #{portal_color} 5%, #{base_color})"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def language_name(locale)
|
def language_name(locale)
|
||||||
@@ -24,48 +14,35 @@ module PortalHelper
|
|||||||
language_map[locale] || locale
|
language_map[locale] || locale
|
||||||
end
|
end
|
||||||
|
|
||||||
def theme_query_string(theme)
|
def get_theme_names(theme)
|
||||||
theme.present? && theme != 'system' ? "?theme=#{theme}" : ''
|
if theme == 'light'
|
||||||
end
|
I18n.t('public_portal.header.appearance.light')
|
||||||
|
elsif theme == 'dark'
|
||||||
def generate_home_link(portal_slug, portal_locale, theme, is_plain_layout_enabled)
|
I18n.t('public_portal.header.appearance.dark')
|
||||||
if is_plain_layout_enabled
|
|
||||||
"/hc/#{portal_slug}/#{portal_locale}#{theme_query_string(theme)}"
|
|
||||||
else
|
else
|
||||||
"/hc/#{portal_slug}/#{portal_locale}"
|
I18n.t('public_portal.header.appearance.system')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_category_link(params)
|
def get_theme_icon(theme)
|
||||||
portal_slug = params[:portal_slug]
|
if theme == 'light'
|
||||||
category_locale = params[:category_locale]
|
'icons/sun'
|
||||||
category_slug = params[:category_slug]
|
elsif theme == 'dark'
|
||||||
theme = params[:theme]
|
'icons/moon'
|
||||||
is_plain_layout_enabled = params[:is_plain_layout_enabled]
|
|
||||||
|
|
||||||
if is_plain_layout_enabled
|
|
||||||
"/hc/#{portal_slug}/#{category_locale}/categories/#{category_slug}#{theme_query_string(theme)}"
|
|
||||||
else
|
else
|
||||||
"/hc/#{portal_slug}/#{category_locale}/categories/#{category_slug}"
|
'icons/monitor'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_article_link(portal_slug, article_slug, theme, is_plain_layout_enabled)
|
def generate_gradient_to_bottom(theme)
|
||||||
if is_plain_layout_enabled
|
"background-image: linear-gradient(to bottom, transparent, #{theme == 'dark' ? '#151718' : 'white'})"
|
||||||
"/hc/#{portal_slug}/articles/#{article_slug}#{theme_query_string(theme)}"
|
end
|
||||||
else
|
|
||||||
"/hc/#{portal_slug}/articles/#{article_slug}"
|
def generate_article_link(portal_slug, article_slug, theme)
|
||||||
end
|
"/hc/#{portal_slug}/articles/#{article_slug}#{theme.present? && theme != 'system' ? "?theme=#{theme}" : ''}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_category_content(content)
|
def render_category_content(content)
|
||||||
ChatwootMarkdownRenderer.new(content).render_markdown_to_plain_text
|
ChatwootMarkdownRenderer.new(content).render_markdown_to_plain_text
|
||||||
end
|
end
|
||||||
|
|
||||||
def thumbnail_bg_color(username)
|
|
||||||
colors = ['#6D95BA', '#A4C3C3', '#E19191']
|
|
||||||
return colors.sample if username.blank?
|
|
||||||
|
|
||||||
colors[username.length % colors.size]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -86,12 +86,6 @@ class MessageApi extends ApiClient {
|
|||||||
return axios.delete(`${this.url}/${conversationID}/messages/${messageId}`);
|
return axios.delete(`${this.url}/${conversationID}/messages/${messageId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
retry(conversationID, messageId) {
|
|
||||||
return axios.post(
|
|
||||||
`${this.url}/${conversationID}/messages/${messageId}/retry`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPreviousMessages({ conversationId, after, before }) {
|
getPreviousMessages({ conversationId, after, before }) {
|
||||||
const params = { before };
|
const params = { before };
|
||||||
if (after && Number(after) !== Number(before)) {
|
if (after && Number(after) !== Number(before)) {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 16 KiB |
@@ -1,8 +1,39 @@
|
|||||||
@import 'shared/assets/fonts/inter';
|
@import '../variables';
|
||||||
@import 'tailwindcss/base';
|
|
||||||
@import 'tailwindcss/components';
|
|
||||||
@import 'tailwindcss/utilities';
|
|
||||||
|
|
||||||
body {
|
.superadmin-body {
|
||||||
font-family: Inter, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
background: var(--color-background);
|
||||||
|
|
||||||
|
.hero--title {
|
||||||
|
font-size: var(--font-size-mega);
|
||||||
|
font-weight: var(--font-weight-light);
|
||||||
|
margin-top: var(--space-large);
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-subscription--checkbox {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
input {
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-right: var(--space-one);
|
||||||
|
margin-top: var(--space-smaller);
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: var(--space-normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.alert-box {
|
||||||
|
background-color: var(--r-500);
|
||||||
|
border-radius: 5px;
|
||||||
|
color: var(--color-white);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@import 'shared/assets/fonts/plus-jakarta';
|
||||||
|
@import '../variables';
|
||||||
|
@import '~shared/assets/stylesheets/ionicons';
|
||||||
@@ -76,11 +76,7 @@
|
|||||||
|
|
||||||
&.left {
|
&.left {
|
||||||
.bubble {
|
.bubble {
|
||||||
@apply rounded-r-lg rounded-l mr-auto break-words;
|
@apply border border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-700 text-black-900 dark:text-slate-50 rounded-r-lg rounded-l mr-auto break-words;
|
||||||
|
|
||||||
&:not(.is-unsupported) {
|
|
||||||
@apply border border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-700 text-black-900 dark:text-slate-50
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-image {
|
&.is-image {
|
||||||
@apply rounded-lg;
|
@apply rounded-lg;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
.modal-mask {
|
.modal-mask {
|
||||||
// @include flex;
|
// @include flex;
|
||||||
// @include flex-align(center, middle);
|
// @include flex-align(center, middle);
|
||||||
@apply flex items-center justify-center bg-modal-backdrop-light dark:bg-modal-backdrop-dark z-[9990] h-full left-0 fixed top-0 w-full;
|
@apply flex items-center justify-center bg-modal dark:bg-modal z-[9990] h-full left-0 fixed top-0 w-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-top-bar {
|
.page-top-bar {
|
||||||
|
|||||||
@@ -126,33 +126,49 @@
|
|||||||
@assign-team="onAssignTeamsForBulk"
|
@assign-team="onAssignTeamsForBulk"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
ref="conversationList"
|
ref="activeConversation"
|
||||||
class="conversations-list flex-1"
|
class="conversations-list flex-1"
|
||||||
:class="{ 'overflow-hidden': isContextMenuOpen }"
|
:class="{ 'overflow-hidden': isContextMenuOpen }"
|
||||||
>
|
>
|
||||||
<virtual-list
|
<div>
|
||||||
ref="conversationVirtualList"
|
<conversation-card
|
||||||
:data-key="'id'"
|
v-for="chat in conversationList"
|
||||||
:data-sources="conversationList"
|
:key="chat.id"
|
||||||
:data-component="itemComponent"
|
:active-label="label"
|
||||||
:extra-props="virtualListExtraProps"
|
:team-id="teamId"
|
||||||
class="w-full overflow-auto h-full"
|
:folders-id="foldersId"
|
||||||
footer-tag="div"
|
:chat="chat"
|
||||||
|
:conversation-type="conversationType"
|
||||||
|
:show-assignee="showAssigneeInConversationCard"
|
||||||
|
:selected="isConversationSelected(chat.id)"
|
||||||
|
@select-conversation="selectConversation"
|
||||||
|
@de-select-conversation="deSelectConversation"
|
||||||
|
@assign-agent="onAssignAgent"
|
||||||
|
@assign-team="onAssignTeam"
|
||||||
|
@assign-label="onAssignLabels"
|
||||||
|
@update-conversation-status="toggleConversationStatus"
|
||||||
|
@context-menu-toggle="onContextMenuToggle"
|
||||||
|
@mark-as-unread="markAsUnread"
|
||||||
|
@assign-priority="assignPriority"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="chatListLoading" class="text-center">
|
||||||
|
<span class="spinner mt-4 mb-4" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<woot-button
|
||||||
|
v-if="!hasCurrentPageEndReached && !chatListLoading"
|
||||||
|
variant="clear"
|
||||||
|
size="expanded"
|
||||||
|
class="load-more--button"
|
||||||
|
@click="loadMoreConversations"
|
||||||
>
|
>
|
||||||
<template #footer>
|
{{ $t('CHAT_LIST.LOAD_MORE_CONVERSATIONS') }}
|
||||||
<div v-if="chatListLoading" class="text-center">
|
</woot-button>
|
||||||
<span class="spinner mt-4 mb-4" />
|
|
||||||
</div>
|
<p v-if="showEndOfListMessage" class="text-center text-muted p-4">
|
||||||
<p v-if="showEndOfListMessage" class="text-center text-muted p-4">
|
{{ $t('CHAT_LIST.EOF') }}
|
||||||
{{ $t('CHAT_LIST.EOF') }}
|
</p>
|
||||||
</p>
|
|
||||||
<intersection-observer
|
|
||||||
v-if="!showEndOfListMessage && !chatListLoading"
|
|
||||||
:options="infiniteLoaderOptions"
|
|
||||||
@observed="loadMoreConversations"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</virtual-list>
|
|
||||||
</div>
|
</div>
|
||||||
<woot-modal
|
<woot-modal
|
||||||
:show.sync="showAdvancedFilters"
|
:show.sync="showAdvancedFilters"
|
||||||
@@ -175,12 +191,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import VirtualList from 'vue-virtual-scroll-list';
|
|
||||||
|
|
||||||
import ConversationAdvancedFilter from './widgets/conversation/ConversationAdvancedFilter.vue';
|
import ConversationAdvancedFilter from './widgets/conversation/ConversationAdvancedFilter.vue';
|
||||||
import ConversationBasicFilter from './widgets/conversation/ConversationBasicFilter.vue';
|
import ConversationBasicFilter from './widgets/conversation/ConversationBasicFilter.vue';
|
||||||
import ChatTypeTabs from './widgets/ChatTypeTabs.vue';
|
import ChatTypeTabs from './widgets/ChatTypeTabs.vue';
|
||||||
import ConversationItem from './ConversationItem.vue';
|
import ConversationCard from './widgets/conversation/ConversationCard.vue';
|
||||||
import timeMixin from '../mixins/time';
|
import timeMixin from '../mixins/time';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||||
import conversationMixin from '../mixins/conversations';
|
import conversationMixin from '../mixins/conversations';
|
||||||
@@ -207,20 +222,16 @@ import {
|
|||||||
isOnUnattendedView,
|
isOnUnattendedView,
|
||||||
} from '../store/modules/conversations/helpers/actionHelpers';
|
} from '../store/modules/conversations/helpers/actionHelpers';
|
||||||
import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events';
|
import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events';
|
||||||
import IntersectionObserver from './IntersectionObserver.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AddCustomViews,
|
AddCustomViews,
|
||||||
ChatTypeTabs,
|
ChatTypeTabs,
|
||||||
// eslint-disable-next-line vue/no-unused-components
|
ConversationCard,
|
||||||
ConversationItem,
|
|
||||||
ConversationAdvancedFilter,
|
ConversationAdvancedFilter,
|
||||||
DeleteCustomViews,
|
DeleteCustomViews,
|
||||||
ConversationBulkActions,
|
ConversationBulkActions,
|
||||||
ConversationBasicFilter,
|
ConversationBasicFilter,
|
||||||
IntersectionObserver,
|
|
||||||
VirtualList,
|
|
||||||
},
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
timeMixin,
|
timeMixin,
|
||||||
@@ -230,20 +241,6 @@ export default {
|
|||||||
filterMixin,
|
filterMixin,
|
||||||
uiSettingsMixin,
|
uiSettingsMixin,
|
||||||
],
|
],
|
||||||
provide() {
|
|
||||||
return {
|
|
||||||
// Actions to be performed on virtual list item and context menu.
|
|
||||||
selectConversation: this.selectConversation,
|
|
||||||
deSelectConversation: this.deSelectConversation,
|
|
||||||
assignAgent: this.onAssignAgent,
|
|
||||||
assignTeam: this.onAssignTeam,
|
|
||||||
assignLabels: this.onAssignLabels,
|
|
||||||
updateConversationStatus: this.toggleConversationStatus,
|
|
||||||
toggleContextMenu: this.onContextMenuToggle,
|
|
||||||
markAsUnread: this.markAsUnread,
|
|
||||||
assignPriority: this.assignPriority,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
conversationInbox: {
|
conversationInbox: {
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
@@ -278,7 +275,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
activeAssigneeTab: wootConstants.ASSIGNEE_TYPE.ME,
|
activeAssigneeTab: wootConstants.ASSIGNEE_TYPE.ME,
|
||||||
activeStatus: wootConstants.STATUS_TYPE.OPEN,
|
activeStatus: wootConstants.STATUS_TYPE.OPEN,
|
||||||
activeSortBy: wootConstants.SORT_BY_TYPE.LAST_ACTIVITY_AT_DESC,
|
activeSortBy: wootConstants.SORT_BY_TYPE.LATEST,
|
||||||
showAdvancedFilters: false,
|
showAdvancedFilters: false,
|
||||||
advancedFilterTypes: advancedFilterTypes.map(filter => ({
|
advancedFilterTypes: advancedFilterTypes.map(filter => ({
|
||||||
...filter,
|
...filter,
|
||||||
@@ -294,21 +291,6 @@ export default {
|
|||||||
selectedInboxes: [],
|
selectedInboxes: [],
|
||||||
isContextMenuOpen: false,
|
isContextMenuOpen: false,
|
||||||
appliedFilter: [],
|
appliedFilter: [],
|
||||||
infiniteLoaderOptions: {
|
|
||||||
root: this.$refs.conversationList,
|
|
||||||
rootMargin: '100px 0px 100px 0px',
|
|
||||||
},
|
|
||||||
|
|
||||||
itemComponent: ConversationItem,
|
|
||||||
// virtualListExtraProps is to pass the props to the conversationItem component.
|
|
||||||
virtualListExtraProps: {
|
|
||||||
label: this.label,
|
|
||||||
teamId: this.teamId,
|
|
||||||
foldersId: this.foldersId,
|
|
||||||
conversationType: this.conversationType,
|
|
||||||
showAssignee: false,
|
|
||||||
isConversationSelected: this.isConversationSelected,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -519,9 +501,6 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
teamId() {
|
|
||||||
this.updateVirtualListProps('teamId', this.teamId);
|
|
||||||
},
|
|
||||||
activeTeam() {
|
activeTeam() {
|
||||||
this.resetAndFetchData();
|
this.resetAndFetchData();
|
||||||
},
|
},
|
||||||
@@ -530,22 +509,16 @@ export default {
|
|||||||
},
|
},
|
||||||
label() {
|
label() {
|
||||||
this.resetAndFetchData();
|
this.resetAndFetchData();
|
||||||
this.updateVirtualListProps('label', this.label);
|
|
||||||
},
|
},
|
||||||
conversationType() {
|
conversationType() {
|
||||||
this.resetAndFetchData();
|
this.resetAndFetchData();
|
||||||
this.updateVirtualListProps('conversationType', this.conversationType);
|
|
||||||
},
|
},
|
||||||
activeFolder() {
|
activeFolder() {
|
||||||
this.resetAndFetchData();
|
this.resetAndFetchData();
|
||||||
this.updateVirtualListProps('foldersId', this.foldersId);
|
|
||||||
},
|
},
|
||||||
chatLists() {
|
chatLists() {
|
||||||
this.chatsOnView = this.conversationList;
|
this.chatsOnView = this.conversationList;
|
||||||
},
|
},
|
||||||
showAssigneeInConversationCard(newVal) {
|
|
||||||
this.updateVirtualListProps('showAssignee', newVal);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setFiltersFromUISettings();
|
this.setFiltersFromUISettings();
|
||||||
@@ -562,12 +535,6 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateVirtualListProps(key, value) {
|
|
||||||
this.virtualListExtraProps = {
|
|
||||||
...this.virtualListExtraProps,
|
|
||||||
[key]: value,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onApplyFilter(payload) {
|
onApplyFilter(payload) {
|
||||||
this.resetBulkActions();
|
this.resetBulkActions();
|
||||||
this.foldersQuery = filterQueryGenerator(payload);
|
this.foldersQuery = filterQueryGenerator(payload);
|
||||||
@@ -588,10 +555,7 @@ export default {
|
|||||||
const { conversations_filter_by: filterBy = {} } = this.uiSettings;
|
const { conversations_filter_by: filterBy = {} } = this.uiSettings;
|
||||||
const { status, order_by: orderBy } = filterBy;
|
const { status, order_by: orderBy } = filterBy;
|
||||||
this.activeStatus = status || wootConstants.STATUS_TYPE.OPEN;
|
this.activeStatus = status || wootConstants.STATUS_TYPE.OPEN;
|
||||||
this.activeSortBy =
|
this.activeSortBy = orderBy || wootConstants.SORT_BY_TYPE.LATEST;
|
||||||
Object.keys(wootConstants.SORT_BY_TYPE).find(
|
|
||||||
sortField => sortField === orderBy
|
|
||||||
) || wootConstants.SORT_BY_TYPE.LAST_ACTIVITY_AT_DESC;
|
|
||||||
},
|
},
|
||||||
onClickOpenAddFoldersModal() {
|
onClickOpenAddFoldersModal() {
|
||||||
this.showAddFoldersModal = true;
|
this.showAddFoldersModal = true;
|
||||||
@@ -671,10 +635,10 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
getKeyboardListenerParams() {
|
getKeyboardListenerParams() {
|
||||||
const allConversations = this.$refs.conversationList.querySelectorAll(
|
const allConversations = this.$refs.activeConversation.querySelectorAll(
|
||||||
'div.conversations-list div.conversation'
|
'div.conversations-list div.conversation'
|
||||||
);
|
);
|
||||||
const activeConversation = this.$refs.conversationList.querySelector(
|
const activeConversation = this.$refs.activeConversation.querySelector(
|
||||||
'div.conversations-list div.conversation.active'
|
'div.conversations-list div.conversation.active'
|
||||||
);
|
);
|
||||||
const activeConversationIndex = [...allConversations].indexOf(
|
const activeConversationIndex = [...allConversations].indexOf(
|
||||||
@@ -730,12 +694,9 @@ export default {
|
|||||||
fetchConversations() {
|
fetchConversations() {
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch('fetchAllConversations', this.conversationFilters)
|
.dispatch('fetchAllConversations', this.conversationFilters)
|
||||||
.then(this.emitConversationLoaded);
|
.then(() => this.$emit('conversation-load'));
|
||||||
},
|
},
|
||||||
loadMoreConversations() {
|
loadMoreConversations() {
|
||||||
if (this.hasCurrentPageEndReached || this.chatListLoading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.hasAppliedFiltersOrActiveFolders) {
|
if (!this.hasAppliedFiltersOrActiveFolders) {
|
||||||
this.fetchConversations();
|
this.fetchConversations();
|
||||||
}
|
}
|
||||||
@@ -754,7 +715,7 @@ export default {
|
|||||||
queryData: filterQueryGenerator(payload),
|
queryData: filterQueryGenerator(payload),
|
||||||
page,
|
page,
|
||||||
})
|
})
|
||||||
.then(this.emitConversationLoaded);
|
.then(() => this.$emit('conversation-load'));
|
||||||
this.showAdvancedFilters = false;
|
this.showAdvancedFilters = false;
|
||||||
},
|
},
|
||||||
fetchSavedFilteredConversations(payload) {
|
fetchSavedFilteredConversations(payload) {
|
||||||
@@ -764,7 +725,7 @@ export default {
|
|||||||
queryData: payload,
|
queryData: payload,
|
||||||
page,
|
page,
|
||||||
})
|
})
|
||||||
.then(this.emitConversationLoaded);
|
.then(() => this.$emit('conversation-load'));
|
||||||
},
|
},
|
||||||
updateAssigneeTab(selectedTab) {
|
updateAssigneeTab(selectedTab) {
|
||||||
if (this.activeAssigneeTab !== selectedTab) {
|
if (this.activeAssigneeTab !== selectedTab) {
|
||||||
@@ -776,20 +737,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emitConversationLoaded() {
|
|
||||||
this.$emit('conversation-load');
|
|
||||||
this.$nextTick(() => {
|
|
||||||
// Addressing a known issue in the virtual list library where dynamically added items
|
|
||||||
// might not render correctly. This workaround involves a slight manual adjustment
|
|
||||||
// to the scroll position, triggering the list to refresh its rendering.
|
|
||||||
const virtualList = this.$refs.conversationVirtualList;
|
|
||||||
const scrollToOffset = virtualList?.scrollToOffset;
|
|
||||||
const currentOffset = virtualList?.getOffset() || 0;
|
|
||||||
if (scrollToOffset) {
|
|
||||||
scrollToOffset(currentOffset + 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
resetBulkActions() {
|
resetBulkActions() {
|
||||||
this.selectedConversations = [];
|
this.selectedConversations = [];
|
||||||
this.selectedInboxes = [];
|
this.selectedInboxes = [];
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
<template>
|
|
||||||
<conversation-card
|
|
||||||
:key="source.id"
|
|
||||||
:active-label="label"
|
|
||||||
:team-id="teamId"
|
|
||||||
:folders-id="foldersId"
|
|
||||||
:chat="source"
|
|
||||||
:conversation-type="conversationType"
|
|
||||||
:selected="isConversationSelected(source.id)"
|
|
||||||
:show-assignee="showAssignee"
|
|
||||||
:enable-context-menu="true"
|
|
||||||
@select-conversation="selectConversation"
|
|
||||||
@de-select-conversation="deSelectConversation"
|
|
||||||
@assign-agent="assignAgent"
|
|
||||||
@assign-team="assignTeam"
|
|
||||||
@assign-label="assignLabels"
|
|
||||||
@update-conversation-status="updateConversationStatus"
|
|
||||||
@context-menu-toggle="toggleContextMenu"
|
|
||||||
@mark-as-unread="markAsUnread"
|
|
||||||
@assign-priority="assignPriority"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ConversationCard from './widgets/conversation/ConversationCard.vue';
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ConversationCard,
|
|
||||||
},
|
|
||||||
inject: [
|
|
||||||
'selectConversation',
|
|
||||||
'deSelectConversation',
|
|
||||||
'assignAgent',
|
|
||||||
'assignTeam',
|
|
||||||
'assignLabels',
|
|
||||||
'updateConversationStatus',
|
|
||||||
'toggleContextMenu',
|
|
||||||
'markAsUnread',
|
|
||||||
'assignPriority',
|
|
||||||
],
|
|
||||||
props: {
|
|
||||||
source: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
teamId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
conversationType: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
foldersId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
isConversationSelected: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {},
|
|
||||||
},
|
|
||||||
showAssignee: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div ref="observedElement" class="h-6 w-full" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
options: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({ root: document, rootMargin: '100px 0 100px 0)' }),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.intersectionObserver = null;
|
|
||||||
this.registerInfiniteLoader();
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.unobserveInfiniteLoadObserver();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
registerInfiniteLoader() {
|
|
||||||
this.intersectionObserver = new IntersectionObserver(entries => {
|
|
||||||
if (entries && entries[0].isIntersecting) {
|
|
||||||
this.$emit('observed');
|
|
||||||
}
|
|
||||||
}, this.options);
|
|
||||||
this.intersectionObserver.observe(this.$refs.observedElement);
|
|
||||||
},
|
|
||||||
unobserveInfiniteLoadObserver() {
|
|
||||||
this.intersectionObserver.unobserve(this.$refs.observedElement);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,33 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="network-notification-fade" tag="div">
|
<transition name="network-notification-fade" tag="div">
|
||||||
<div v-show="showNotification" class="fixed top-4 left-2 z-50 group">
|
<div v-show="showNotification" class="ui-notification-container">
|
||||||
<div
|
<div class="ui-notification">
|
||||||
class="flex items-center justify-between py-1 px-2 w-full rounded-lg shadow-lg bg-yellow-200 dark:bg-yellow-700 relative"
|
<fluent-icon icon="wifi-off" />
|
||||||
>
|
<p class="ui-notification-text">
|
||||||
<fluent-icon
|
{{
|
||||||
icon="wifi-off"
|
useInstallationName(
|
||||||
class="text-yellow-700/50 dark:text-yellow-50"
|
$t('NETWORK.NOTIFICATION.TEXT'),
|
||||||
size="18"
|
globalConfig.installationName
|
||||||
/>
|
)
|
||||||
<span
|
}}
|
||||||
class="text-xs tracking-wide font-medium px-2 text-yellow-700/70 dark:text-yellow-50"
|
</p>
|
||||||
>
|
<woot-button variant="clear" size="small" @click="refreshPage">
|
||||||
{{ $t('NETWORK.NOTIFICATION.OFFLINE') }}
|
{{ $t('NETWORK.BUTTON.REFRESH') }}
|
||||||
</span>
|
</woot-button>
|
||||||
<woot-button
|
<woot-button
|
||||||
:title="$t('NETWORK.BUTTON.REFRESH')"
|
variant="smooth"
|
||||||
variant="clear"
|
|
||||||
size="small"
|
size="small"
|
||||||
color-scheme="warning"
|
color-scheme="warning"
|
||||||
icon="arrow-clockwise"
|
icon="dismiss-circle"
|
||||||
class="visible transition-all duration-500 ease-in-out ml-1"
|
|
||||||
@click="refreshPage"
|
|
||||||
/>
|
|
||||||
<woot-button
|
|
||||||
variant="clear"
|
|
||||||
size="small"
|
|
||||||
color-scheme="warning"
|
|
||||||
icon="dismiss"
|
|
||||||
@click="closeNotification"
|
@click="closeNotification"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,12 +47,7 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
window.addEventListener('offline', this.updateOnlineStatus);
|
window.addEventListener('offline', this.updateOnlineStatus);
|
||||||
window.bus.$on(BUS_EVENTS.WEBSOCKET_DISCONNECT, () => {
|
window.bus.$on(BUS_EVENTS.WEBSOCKET_DISCONNECT, () => {
|
||||||
// TODO: Remove this after completing the conversation list refetching
|
this.updateOnlineStatus({ type: 'offline' });
|
||||||
// TODO: DIRTY FIX : CLEAN UP THIS WITH PROPER FIX, DELAYING THE RECONNECT FOR NOW
|
|
||||||
// THE CABLE IS FIRING IS VERY COMMON AND THUS INTERFERING USER EXPERIENCE
|
|
||||||
setTimeout(() => {
|
|
||||||
this.updateOnlineStatus({ type: 'offline' });
|
|
||||||
}, 4000);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -86,3 +72,33 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '~dashboard/assets/scss/mixins';
|
||||||
|
|
||||||
|
.ui-notification-container {
|
||||||
|
max-width: 25rem;
|
||||||
|
position: absolute;
|
||||||
|
right: var(--space-normal);
|
||||||
|
top: var(--space-normal);
|
||||||
|
z-index: var(--z-index-very-high);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-notification {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
background-color: var(--y-100);
|
||||||
|
border-radius: var(--border-radius-medium);
|
||||||
|
box-shadow: var(--shadow-large);
|
||||||
|
|
||||||
|
min-width: 15rem;
|
||||||
|
padding: var(--space-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-notification-text {
|
||||||
|
margin: 0 var(--space-small);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -84,10 +84,7 @@ import {
|
|||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
||||||
import {
|
import { replaceVariablesInMessage } from '@chatwoot/utils';
|
||||||
replaceVariablesInMessage,
|
|
||||||
createTypingIndicator,
|
|
||||||
} from '@chatwoot/utils';
|
|
||||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||||
import { uploadFile } from 'dashboard/helper/uploadHelper';
|
import { uploadFile } from 'dashboard/helper/uploadHelper';
|
||||||
@@ -143,15 +140,6 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
typingIndicator: createTypingIndicator(
|
|
||||||
() => {
|
|
||||||
this.$emit('typing-on');
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.$emit('typing-off');
|
|
||||||
},
|
|
||||||
TYPING_INDICATOR_IDLE_TIME
|
|
||||||
),
|
|
||||||
showUserMentions: false,
|
showUserMentions: false,
|
||||||
showCannedMenu: false,
|
showCannedMenu: false,
|
||||||
showVariables: false,
|
showVariables: false,
|
||||||
@@ -650,6 +638,15 @@ export default {
|
|||||||
hideMentions() {
|
hideMentions() {
|
||||||
this.showUserMentions = false;
|
this.showUserMentions = false;
|
||||||
},
|
},
|
||||||
|
resetTyping() {
|
||||||
|
this.$emit('typing-off');
|
||||||
|
this.idleTimer = null;
|
||||||
|
},
|
||||||
|
turnOffIdleTimer() {
|
||||||
|
if (this.idleTimer) {
|
||||||
|
clearTimeout(this.idleTimer);
|
||||||
|
}
|
||||||
|
},
|
||||||
handleLineBreakWhenEnterToSendEnabled(event) {
|
handleLineBreakWhenEnterToSendEnabled(event) {
|
||||||
if (
|
if (
|
||||||
hasPressedEnterAndNotCmdOrShift(event) &&
|
hasPressedEnterAndNotCmdOrShift(event) &&
|
||||||
@@ -669,7 +666,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onKeyup() {
|
onKeyup() {
|
||||||
this.typingIndicator.start();
|
if (!this.idleTimer) {
|
||||||
|
this.$emit('typing-on');
|
||||||
|
}
|
||||||
|
this.turnOffIdleTimer();
|
||||||
|
this.idleTimer = setTimeout(
|
||||||
|
() => this.resetTyping(),
|
||||||
|
TYPING_INDICATOR_IDLE_TIME
|
||||||
|
);
|
||||||
this.updateImgToolbarOnDelete();
|
this.updateImgToolbarOnDelete();
|
||||||
},
|
},
|
||||||
onKeydown(event) {
|
onKeydown(event) {
|
||||||
@@ -681,7 +685,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onBlur() {
|
onBlur() {
|
||||||
this.typingIndicator.stop();
|
this.turnOffIdleTimer();
|
||||||
|
this.resetTyping();
|
||||||
this.$emit('blur');
|
this.$emit('blur');
|
||||||
},
|
},
|
||||||
onFocus() {
|
onFocus() {
|
||||||
|
|||||||
@@ -101,10 +101,10 @@
|
|||||||
<transition name="modal-fade">
|
<transition name="modal-fade">
|
||||||
<div
|
<div
|
||||||
v-show="$refs.upload && $refs.upload.dropActive"
|
v-show="$refs.upload && $refs.upload.dropActive"
|
||||||
class="fixed top-0 bottom-0 left-0 right-0 z-20 flex flex-col items-center justify-center w-full h-full gap-2 text-slate-900 dark:text-slate-50 bg-modal-backdrop-light dark:bg-modal-backdrop-dark"
|
class="fixed top-0 bottom-0 left-0 right-0 z-20 flex flex-col items-center justify-center w-full h-full gap-2 text-slate-600 dark:text-slate-200 bg-white_transparent dark:bg-black_transparent"
|
||||||
>
|
>
|
||||||
<fluent-icon icon="cloud-backup" size="40" />
|
<fluent-icon icon="cloud-backup" size="40" />
|
||||||
<h4 class="page-sub-title text-slate-900 dark:text-slate-50">
|
<h4 class="page-sub-title text-slate-600 dark:text-slate-200">
|
||||||
{{ $t('CONVERSATION.REPLYBOX.DRAG_DROP') }}
|
{{ $t('CONVERSATION.REPLYBOX.DRAG_DROP') }}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,7 +144,6 @@ import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
|||||||
import {
|
import {
|
||||||
ALLOWED_FILE_TYPES,
|
ALLOWED_FILE_TYPES,
|
||||||
ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP,
|
ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP,
|
||||||
ALLOWED_FILE_TYPES_FOR_LINE,
|
|
||||||
} from 'shared/constants/messages';
|
} from 'shared/constants/messages';
|
||||||
import VideoCallButton from '../VideoCallButton.vue';
|
import VideoCallButton from '../VideoCallButton.vue';
|
||||||
import AIAssistanceButton from '../AIAssistanceButton.vue';
|
import AIAssistanceButton from '../AIAssistanceButton.vue';
|
||||||
@@ -271,9 +270,6 @@ export default {
|
|||||||
return this.showFileUpload || this.isNote;
|
return this.showFileUpload || this.isNote;
|
||||||
},
|
},
|
||||||
showAudioRecorderButton() {
|
showAudioRecorderButton() {
|
||||||
if (this.isALineChannel) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Disable audio recorder for safari browser as recording is not supported
|
// Disable audio recorder for safari browser as recording is not supported
|
||||||
const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(
|
const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(
|
||||||
navigator.userAgent
|
navigator.userAgent
|
||||||
@@ -295,9 +291,6 @@ export default {
|
|||||||
if (this.isATwilioWhatsAppChannel) {
|
if (this.isATwilioWhatsAppChannel) {
|
||||||
return ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP;
|
return ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP;
|
||||||
}
|
}
|
||||||
if (this.isALineChannel) {
|
|
||||||
return ALLOWED_FILE_TYPES_FOR_LINE;
|
|
||||||
}
|
|
||||||
return ALLOWED_FILE_TYPES;
|
return ALLOWED_FILE_TYPES;
|
||||||
},
|
},
|
||||||
enableDragAndDrop() {
|
enableDragAndDrop() {
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<mention-box
|
<mention-box :items="items" @mention-select="handleMentionClick">
|
||||||
v-if="items.length"
|
|
||||||
:items="items"
|
|
||||||
@mention-select="handleMentionClick"
|
|
||||||
>
|
|
||||||
<template slot-scope="{ item }">
|
<template slot-scope="{ item }">
|
||||||
<strong>{{ item.label }}</strong> - {{ item.description }}
|
<strong>{{ item.label }}</strong> - {{ item.description }}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+3
-5
@@ -34,7 +34,7 @@
|
|||||||
type="sort"
|
type="sort"
|
||||||
:selected-value="sortFilter"
|
:selected-value="sortFilter"
|
||||||
:items="chatSortItems"
|
:items="chatSortItems"
|
||||||
path-prefix="CHAT_LIST.SORT_ORDER_ITEMS"
|
path-prefix="CHAT_LIST.CHAT_SORT_FILTER_ITEMS"
|
||||||
@onChangeFilter="onChangeFilter"
|
@onChangeFilter="onChangeFilter"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,7 +58,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
showActionsDropdown: false,
|
showActionsDropdown: false,
|
||||||
chatStatusItems: this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS'),
|
chatStatusItems: this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS'),
|
||||||
chatSortItems: this.$t('CHAT_LIST.SORT_ORDER_ITEMS'),
|
chatSortItems: this.$t('CHAT_LIST.CHAT_SORT_FILTER_ITEMS'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -70,9 +70,7 @@ export default {
|
|||||||
return this.chatStatusFilter || wootConstants.STATUS_TYPE.OPEN;
|
return this.chatStatusFilter || wootConstants.STATUS_TYPE.OPEN;
|
||||||
},
|
},
|
||||||
sortFilter() {
|
sortFilter() {
|
||||||
return (
|
return this.chatSortFilter || wootConstants.SORT_BY_TYPE.LATEST;
|
||||||
this.chatSortFilter || wootConstants.SORT_BY_TYPE.LAST_ACTIVITY_AT_DESC
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
size="40px"
|
size="40px"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="px-0 py-3 border-b group-hover:border-transparent border-slate-50 dark:border-slate-800/75 columns"
|
class="px-0 py-3 border-b group-last:border-transparent group-hover:border-transparent border-slate-50 dark:border-slate-800/75 columns"
|
||||||
>
|
>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<inbox-name v-if="showInboxName" :inbox="inbox" />
|
<inbox-name v-if="showInboxName" :inbox="inbox" />
|
||||||
@@ -175,10 +175,6 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
enableContextMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -293,7 +289,6 @@ export default {
|
|||||||
this.$emit(action, this.chat.id, this.inbox.id);
|
this.$emit(action, this.chat.id, this.inbox.id);
|
||||||
},
|
},
|
||||||
openContextMenu(e) {
|
openContextMenu(e) {
|
||||||
if (!this.enableContextMenu) return;
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.$emit('context-menu-toggle', true);
|
this.$emit('context-menu-toggle', true);
|
||||||
this.contextMenu.x = e.pageX || e.clientX;
|
this.contextMenu.x = e.pageX || e.clientX;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<li v-if="shouldRenderMessage" :id="`message${data.id}`" :class="alignBubble">
|
<li v-if="shouldRenderMessage" :id="`message${data.id}`" :class="alignBubble">
|
||||||
<div :class="wrapClass">
|
<div :class="wrapClass">
|
||||||
<div v-if="isFailed && !hasOneDayPassed" 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')"
|
||||||
size="tiny"
|
size="tiny"
|
||||||
@@ -29,19 +29,8 @@
|
|||||||
:message-type="data.message_type"
|
:message-type="data.message_type"
|
||||||
:parent-has-attachments="hasAttachments"
|
:parent-has-attachments="hasAttachments"
|
||||||
/>
|
/>
|
||||||
<div v-if="isUnsupported">
|
|
||||||
<template v-if="isAFacebookInbox && isInstagram">
|
|
||||||
{{ $t('CONVERSATION.UNSUPPORTED_MESSAGE_INSTAGRAM') }}
|
|
||||||
</template>
|
|
||||||
<template v-else-if="isAFacebookInbox">
|
|
||||||
{{ $t('CONVERSATION.UNSUPPORTED_MESSAGE_FACEBOOK') }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ $t('CONVERSATION.UNSUPPORTED_MESSAGE') }}
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<bubble-text
|
<bubble-text
|
||||||
v-else-if="data.content"
|
v-if="data.content"
|
||||||
:message="message"
|
:message="message"
|
||||||
:is-email="isEmailContentType"
|
:is-email="isEmailContentType"
|
||||||
:display-quoted-button="displayQuotedButton"
|
:display-quoted-button="displayQuotedButton"
|
||||||
@@ -159,7 +148,6 @@ import { BUS_EVENTS } from 'shared/constants/busEvents';
|
|||||||
import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||||
import { getDayDifferenceFromNow } from 'shared/helpers/DateHelper';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -187,14 +175,6 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
isAFacebookInbox: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
isInstagram: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
isAWhatsAppChannel: {
|
isAWhatsAppChannel: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
@@ -229,16 +209,11 @@ export default {
|
|||||||
created_at: this.data.created_at || '',
|
created_at: this.data.created_at || '',
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
hasOneDayPassed() {
|
|
||||||
// Disable retry button if the message is failed and the message is older than 24 hours
|
|
||||||
return getDayDifferenceFromNow(new Date(), this.data?.created_at) >= 1;
|
|
||||||
},
|
|
||||||
shouldRenderMessage() {
|
shouldRenderMessage() {
|
||||||
return (
|
return (
|
||||||
this.hasAttachments ||
|
this.hasAttachments ||
|
||||||
this.data.content ||
|
this.data.content ||
|
||||||
this.isEmailContentType ||
|
this.isEmailContentType ||
|
||||||
this.isUnsupported ||
|
|
||||||
this.isAnIntegrationMessage
|
this.isAnIntegrationMessage
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -427,7 +402,6 @@ export default {
|
|||||||
return {
|
return {
|
||||||
bubble: this.isBubble,
|
bubble: this.isBubble,
|
||||||
'is-private': this.data.private,
|
'is-private': this.data.private,
|
||||||
'is-unsupported': this.isUnsupported,
|
|
||||||
'is-image': this.hasMediaAttachment('image'),
|
'is-image': this.hasMediaAttachment('image'),
|
||||||
'is-video': this.hasMediaAttachment('video'),
|
'is-video': this.hasMediaAttachment('video'),
|
||||||
'is-text': this.hasText,
|
'is-text': this.hasText,
|
||||||
@@ -436,9 +410,6 @@ export default {
|
|||||||
'is-email': this.isEmailContentType,
|
'is-email': this.isEmailContentType,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
isUnsupported() {
|
|
||||||
return this.contentAttributes.is_unsupported ?? false;
|
|
||||||
},
|
|
||||||
isPending() {
|
isPending() {
|
||||||
return this.data.status === MESSAGE_STATUS.PROGRESS;
|
return this.data.status === MESSAGE_STATUS.PROGRESS;
|
||||||
},
|
},
|
||||||
@@ -450,7 +421,11 @@ export default {
|
|||||||
return !this.sender.type || this.sender.type === 'agent_bot';
|
return !this.sender.type || this.sender.type === 'agent_bot';
|
||||||
},
|
},
|
||||||
shouldShowContextMenu() {
|
shouldShowContextMenu() {
|
||||||
return !(this.isFailed || this.isPending || this.isUnsupported);
|
return !(this.isFailed || this.isPending);
|
||||||
|
},
|
||||||
|
errorMessage() {
|
||||||
|
const { meta } = this.data;
|
||||||
|
return meta ? meta.error : '';
|
||||||
},
|
},
|
||||||
showAvatar() {
|
showAvatar() {
|
||||||
if (this.isOutgoing || this.isTemplate) {
|
if (this.isOutgoing || this.isTemplate) {
|
||||||
@@ -552,14 +527,6 @@ export default {
|
|||||||
> .bubble {
|
> .bubble {
|
||||||
@apply min-w-[128px];
|
@apply min-w-[128px];
|
||||||
|
|
||||||
&.is-unsupported {
|
|
||||||
@apply text-xs max-w-[300px] border-dashed border border-slate-200 text-slate-600 dark:text-slate-200 bg-slate-50 dark:bg-slate-700 dark:border-slate-500;
|
|
||||||
|
|
||||||
.message-text--metadata .time {
|
|
||||||
@apply text-slate-400 dark:text-slate-300;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-image,
|
&.is-image,
|
||||||
&.is-video {
|
&.is-video {
|
||||||
@apply p-0 overflow-hidden;
|
@apply p-0 overflow-hidden;
|
||||||
@@ -572,12 +539,10 @@ export default {
|
|||||||
> video {
|
> video {
|
||||||
@apply rounded-lg;
|
@apply rounded-lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
> video {
|
> video {
|
||||||
@apply h-full w-full object-cover;
|
@apply h-full w-full object-cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video {
|
.video {
|
||||||
@apply h-[11.25rem];
|
@apply h-[11.25rem];
|
||||||
}
|
}
|
||||||
@@ -592,11 +557,9 @@ export default {
|
|||||||
.file--icon {
|
.file--icon {
|
||||||
@apply text-woot-400 dark:text-woot-400;
|
@apply text-woot-400 dark:text-woot-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-block-title {
|
.text-block-title {
|
||||||
@apply text-slate-700 dark:text-slate-700;
|
@apply text-slate-700 dark:text-slate-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download.button {
|
.download.button {
|
||||||
@apply text-woot-400 dark:text-woot-400;
|
@apply text-woot-400 dark:text-woot-400;
|
||||||
}
|
}
|
||||||
@@ -605,7 +568,6 @@ export default {
|
|||||||
&.is-private.is-text > .message-text__wrap .link {
|
&.is-private.is-text > .message-text__wrap .link {
|
||||||
@apply text-woot-600 dark:text-woot-200;
|
@apply text-woot-600 dark:text-woot-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-private.is-text > .message-text__wrap .prosemirror-mention-node {
|
&.is-private.is-text > .message-text__wrap .prosemirror-mention-node {
|
||||||
@apply font-bold bg-none rounded-sm p-0 bg-yellow-100 dark:bg-yellow-700 text-slate-700 dark:text-slate-25 underline;
|
@apply font-bold bg-none rounded-sm p-0 bg-yellow-100 dark:bg-yellow-700 text-slate-700 dark:text-slate-25 underline;
|
||||||
}
|
}
|
||||||
@@ -616,7 +578,6 @@ export default {
|
|||||||
.message-text--metadata .time {
|
.message-text--metadata .time {
|
||||||
@apply text-violet-50 dark:text-violet-50;
|
@apply text-violet-50 dark:text-violet-50;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-private .message-text--metadata .time {
|
&.is-private .message-text--metadata .time {
|
||||||
@apply text-slate-400 dark:text-slate-400;
|
@apply text-slate-400 dark:text-slate-400;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,7 @@
|
|||||||
icon="info"
|
icon="info"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span v-if="message.content && isMessageSticker">
|
<span v-if="message.content">
|
||||||
<fluent-icon
|
|
||||||
size="16"
|
|
||||||
class="-mt-0.5 align-middle inline-block text-slate-600 dark:text-slate-300"
|
|
||||||
icon="image"
|
|
||||||
/>
|
|
||||||
{{ $t('CHAT_LIST.ATTACHMENTS.image.CONTENT') }}
|
|
||||||
</span>
|
|
||||||
<span v-else-if="message.content">
|
|
||||||
{{ parsedLastMessage }}
|
{{ parsedLastMessage }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="message.attachments">
|
<span v-else-if="message.attachments">
|
||||||
@@ -96,9 +88,6 @@ export default {
|
|||||||
attachmentMessageContent() {
|
attachmentMessageContent() {
|
||||||
return `CHAT_LIST.ATTACHMENTS.${this.lastMessageFileType}.CONTENT`;
|
return `CHAT_LIST.ATTACHMENTS.${this.lastMessageFileType}.CONTENT`;
|
||||||
},
|
},
|
||||||
isMessageSticker() {
|
|
||||||
return this.message && this.message.content_type === 'sticker';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
:is-a-tweet="isATweet"
|
:is-a-tweet="isATweet"
|
||||||
:is-a-whatsapp-channel="isAWhatsAppChannel"
|
:is-a-whatsapp-channel="isAWhatsAppChannel"
|
||||||
:is-web-widget-inbox="isAWebWidgetInbox"
|
:is-web-widget-inbox="isAWebWidgetInbox"
|
||||||
:is-a-facebook-inbox="isAFacebookInbox"
|
|
||||||
:is-instagram="isInstagramDM"
|
|
||||||
:inbox-supports-reply-to="inboxSupportsReplyTo"
|
:inbox-supports-reply-to="inboxSupportsReplyTo"
|
||||||
:in-reply-to="getInReplyToMessage(message)"
|
:in-reply-to="getInReplyToMessage(message)"
|
||||||
/>
|
/>
|
||||||
@@ -56,8 +54,6 @@
|
|||||||
:is-a-tweet="isATweet"
|
:is-a-tweet="isATweet"
|
||||||
:is-a-whatsapp-channel="isAWhatsAppChannel"
|
:is-a-whatsapp-channel="isAWhatsAppChannel"
|
||||||
:is-web-widget-inbox="isAWebWidgetInbox"
|
:is-web-widget-inbox="isAWebWidgetInbox"
|
||||||
:is-a-facebook-inbox="isAFacebookInbox"
|
|
||||||
:is-instagram-dm="isInstagramDM"
|
|
||||||
:inbox-supports-reply-to="inboxSupportsReplyTo"
|
:inbox-supports-reply-to="inboxSupportsReplyTo"
|
||||||
:in-reply-to="getInReplyToMessage(message)"
|
:in-reply-to="getInReplyToMessage(message)"
|
||||||
/>
|
/>
|
||||||
@@ -287,16 +283,11 @@ export default {
|
|||||||
unreadMessageCount() {
|
unreadMessageCount() {
|
||||||
return this.currentChat.unread_count || 0;
|
return this.currentChat.unread_count || 0;
|
||||||
},
|
},
|
||||||
isInstagramDM() {
|
|
||||||
return this.conversationType === 'instagram_direct_message';
|
|
||||||
},
|
|
||||||
inboxSupportsReplyTo() {
|
inboxSupportsReplyTo() {
|
||||||
const incoming = this.inboxHasFeature(INBOX_FEATURES.REPLY_TO);
|
return {
|
||||||
const outgoing =
|
incoming: this.inboxHasFeature(INBOX_FEATURES.REPLY_TO),
|
||||||
this.inboxHasFeature(INBOX_FEATURES.REPLY_TO_OUTGOING) &&
|
outgoing: this.inboxHasFeature(INBOX_FEATURES.REPLY_TO_OUTGOING),
|
||||||
!this.is360DialogWhatsAppChannel;
|
};
|
||||||
|
|
||||||
return { incoming, outgoing };
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -271,17 +271,11 @@ export default {
|
|||||||
accountId: 'getCurrentAccountId',
|
accountId: 'getCurrentAccountId',
|
||||||
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
|
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
|
||||||
}),
|
}),
|
||||||
currentContact() {
|
|
||||||
return this.$store.getters['contacts/getContact'](
|
|
||||||
this.currentChat.meta.sender.id
|
|
||||||
);
|
|
||||||
},
|
|
||||||
shouldShowReplyToMessage() {
|
shouldShowReplyToMessage() {
|
||||||
return (
|
return (
|
||||||
this.inReplyTo?.id &&
|
this.inReplyTo?.id &&
|
||||||
!this.isPrivate &&
|
!this.isPrivate &&
|
||||||
this.inboxHasFeature(INBOX_FEATURES.REPLY_TO) &&
|
this.inboxHasFeature(INBOX_FEATURES.REPLY_TO)
|
||||||
!this.is360DialogWhatsAppChannel
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
showRichContentEditor() {
|
showRichContentEditor() {
|
||||||
@@ -398,8 +392,7 @@ export default {
|
|||||||
this.isAPIInbox ||
|
this.isAPIInbox ||
|
||||||
this.isAnEmailChannel ||
|
this.isAnEmailChannel ||
|
||||||
this.isASmsInbox ||
|
this.isASmsInbox ||
|
||||||
this.isATelegramChannel ||
|
this.isATelegramChannel
|
||||||
this.isALineChannel
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
replyButtonLabel() {
|
replyButtonLabel() {
|
||||||
@@ -502,11 +495,7 @@ export default {
|
|||||||
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
||||||
},
|
},
|
||||||
audioRecordFormat() {
|
audioRecordFormat() {
|
||||||
if (
|
if (this.isAWhatsAppChannel || this.isAPIInbox) {
|
||||||
this.isAWhatsAppChannel ||
|
|
||||||
this.isAPIInbox ||
|
|
||||||
this.isATelegramChannel
|
|
||||||
) {
|
|
||||||
return AUDIO_FORMATS.OGG;
|
return AUDIO_FORMATS.OGG;
|
||||||
}
|
}
|
||||||
return AUDIO_FORMATS.WAV;
|
return AUDIO_FORMATS.WAV;
|
||||||
@@ -514,7 +503,6 @@ export default {
|
|||||||
messageVariables() {
|
messageVariables() {
|
||||||
const variables = getMessageVariables({
|
const variables = getMessageVariables({
|
||||||
conversation: this.currentChat,
|
conversation: this.currentChat,
|
||||||
contact: this.currentContact,
|
|
||||||
});
|
});
|
||||||
return variables;
|
return variables;
|
||||||
},
|
},
|
||||||
@@ -636,8 +624,6 @@ export default {
|
|||||||
`${this.$t('CONVERSATION.REPLYBOX.INSERT_READ_MORE')} ${url}`
|
`${this.$t('CONVERSATION.REPLYBOX.INSERT_READ_MORE')} ${url}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$track(CONVERSATION_EVENTS.INSERT_ARTICLE_LINK);
|
|
||||||
},
|
},
|
||||||
toggleRichContentEditor() {
|
toggleRichContentEditor() {
|
||||||
this.updateUISettings({
|
this.updateUISettings({
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<mention-box
|
<mention-box :items="items" @mention-select="handleVariableClick">
|
||||||
v-if="items.length"
|
|
||||||
type="variable"
|
|
||||||
:items="items"
|
|
||||||
@mention-select="handleVariableClick"
|
|
||||||
>
|
|
||||||
<template slot-scope="{ item }">
|
<template slot-scope="{ item }">
|
||||||
<span class="text-capitalize variable--list-label">
|
<span class="text-capitalize variable--list-label">
|
||||||
{{ item.description }}
|
{{ item.description }}
|
||||||
@@ -15,7 +10,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { MESSAGE_VARIABLES } from 'shared/constants/messages';
|
import { MESSAGE_VARIABLES } from 'shared/constants/messages';
|
||||||
import MentionBox from '../mentions/MentionBox.vue';
|
import MentionBox from '../mentions/MentionBox.vue';
|
||||||
|
|
||||||
@@ -28,16 +22,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
|
||||||
customAttributes: 'attributes/getAttributes',
|
|
||||||
}),
|
|
||||||
items() {
|
items() {
|
||||||
return [
|
|
||||||
...this.standardAttributeVariables,
|
|
||||||
...this.customAttributeVariables,
|
|
||||||
];
|
|
||||||
},
|
|
||||||
standardAttributeVariables() {
|
|
||||||
return MESSAGE_VARIABLES.filter(variable => {
|
return MESSAGE_VARIABLES.filter(variable => {
|
||||||
return (
|
return (
|
||||||
variable.label.includes(this.searchKey) ||
|
variable.label.includes(this.searchKey) ||
|
||||||
@@ -49,20 +34,6 @@ export default {
|
|||||||
description: variable.label,
|
description: variable.label,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
customAttributeVariables() {
|
|
||||||
return this.customAttributes.map(attribute => {
|
|
||||||
const attributePrefix =
|
|
||||||
attribute.attribute_model === 'conversation_attribute'
|
|
||||||
? 'conversation'
|
|
||||||
: 'contact';
|
|
||||||
|
|
||||||
return {
|
|
||||||
label: `${attributePrefix}.custom_attribute.${attribute.attribute_key}`,
|
|
||||||
key: `${attributePrefix}.custom_attribute.${attribute.attribute_key}`,
|
|
||||||
description: attribute.attribute_description,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleVariableClick(item = {}) {
|
handleVariableClick(item = {}) {
|
||||||
|
|||||||
@@ -7,11 +7,7 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div v-if="!isEmail" v-dompurify-html="message" class="text-content" />
|
<div v-if="!isEmail" v-dompurify-html="message" class="text-content" />
|
||||||
<letter
|
<letter v-else class="text-content" :html="message" />
|
||||||
v-else
|
|
||||||
class="text-content bg-white dark:bg-white text-slate-900 dark:text-slate-900 p-2 rounded-[4px]"
|
|
||||||
:html="message"
|
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
v-if="showQuoteToggle"
|
v-if="showQuoteToggle"
|
||||||
class="text-slate-300 dark:text-slate-300 cursor-pointer text-xs py-1"
|
class="text-slate-300 dark:text-slate-300 cursor-pointer text-xs py-1"
|
||||||
|
|||||||
+1
-39
@@ -46,25 +46,9 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="items-center flex gap-2 justify-end min-w-[8rem] sm:min-w-[15rem]"
|
class="items-center flex gap-2 justify-end min-w-[15rem]"
|
||||||
@click.stop
|
@click.stop
|
||||||
>
|
>
|
||||||
<woot-button
|
|
||||||
v-if="isImage"
|
|
||||||
size="large"
|
|
||||||
color-scheme="secondary"
|
|
||||||
variant="clear"
|
|
||||||
icon="arrow-rotate-counter-clockwise"
|
|
||||||
@click="onRotate('counter-clockwise')"
|
|
||||||
/>
|
|
||||||
<woot-button
|
|
||||||
v-if="isImage"
|
|
||||||
size="large"
|
|
||||||
color-scheme="secondary"
|
|
||||||
variant="clear"
|
|
||||||
icon="arrow-rotate-clockwise"
|
|
||||||
@click="onRotate('clockwise')"
|
|
||||||
/>
|
|
||||||
<woot-button
|
<woot-button
|
||||||
size="large"
|
size="large"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
@@ -105,7 +89,6 @@
|
|||||||
:key="activeAttachment.message_id"
|
:key="activeAttachment.message_id"
|
||||||
:src="activeAttachment.data_url"
|
:src="activeAttachment.data_url"
|
||||||
class="modal-image skip-context-menu my-0 mx-auto"
|
class="modal-image skip-context-menu my-0 mx-auto"
|
||||||
:style="imageRotationStyle"
|
|
||||||
@click.stop
|
@click.stop
|
||||||
/>
|
/>
|
||||||
<video
|
<video
|
||||||
@@ -203,7 +186,6 @@ export default {
|
|||||||
this.allAttachments.findIndex(
|
this.allAttachments.findIndex(
|
||||||
attachment => attachment.message_id === this.attachment.message_id
|
attachment => attachment.message_id === this.attachment.message_id
|
||||||
) || 0,
|
) || 0,
|
||||||
activeImageRotation: 0,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -250,11 +232,6 @@ export default {
|
|||||||
const fileName = dataUrl?.split('/').pop();
|
const fileName = dataUrl?.split('/').pop();
|
||||||
return fileName || '';
|
return fileName || '';
|
||||||
},
|
},
|
||||||
imageRotationStyle() {
|
|
||||||
return {
|
|
||||||
transform: `rotate(${this.activeImageRotation}deg)`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setImageAndVideoSrc(this.attachment);
|
this.setImageAndVideoSrc(this.attachment);
|
||||||
@@ -269,7 +246,6 @@ export default {
|
|||||||
}
|
}
|
||||||
this.activeImageIndex = index;
|
this.activeImageIndex = index;
|
||||||
this.setImageAndVideoSrc(attachment);
|
this.setImageAndVideoSrc(attachment);
|
||||||
this.activeImageRotation = 0;
|
|
||||||
},
|
},
|
||||||
setImageAndVideoSrc(attachment) {
|
setImageAndVideoSrc(attachment) {
|
||||||
const { file_type: type } = attachment;
|
const { file_type: type } = attachment;
|
||||||
@@ -304,20 +280,6 @@ export default {
|
|||||||
link.download = `attachment.${type}`;
|
link.download = `attachment.${type}`;
|
||||||
link.click();
|
link.click();
|
||||||
},
|
},
|
||||||
onRotate(type) {
|
|
||||||
if (!this.isImage) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rotation = type === 'clockwise' ? 90 : -90;
|
|
||||||
|
|
||||||
// Reset rotation if it is 360
|
|
||||||
if (Math.abs(this.activeImageRotation) === 360) {
|
|
||||||
this.activeImageRotation = rotation;
|
|
||||||
} else {
|
|
||||||
this.activeImageRotation += rotation;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
id="file"
|
id="file"
|
||||||
ref="file"
|
ref="file"
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/png, image/jpeg, image/jpg, image/gif, image/webp"
|
accept="image/png, image/jpeg, image/gif"
|
||||||
@change="handleImageUpload"
|
@change="handleImageUpload"
|
||||||
/>
|
/>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -1,40 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div v-if="items.length" ref="mentionsListContainer" class="mention--box">
|
||||||
ref="mentionsListContainer"
|
|
||||||
class="bg-white dark:bg-slate-800 rounded-md overflow-auto absolute w-full z-20 pb-0 shadow-md left-0 bottom-full max-h-[9.75rem] border border-solid border-slate-100 dark:border-slate-700 mention--box"
|
|
||||||
>
|
|
||||||
<ul class="vertical dropdown menu">
|
<ul class="vertical dropdown menu">
|
||||||
<woot-dropdown-item
|
<woot-dropdown-item
|
||||||
v-for="(item, index) in items"
|
v-for="(item, index) in items"
|
||||||
:id="`mention-item-${index}`"
|
:id="`mention-item-${index}`"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
class="!mb-0"
|
|
||||||
@mouseover="onHover(index)"
|
@mouseover="onHover(index)"
|
||||||
>
|
>
|
||||||
<button
|
<woot-button
|
||||||
class="flex group flex-col gap-0.5 overflow-hidden cursor-pointer items-start py-2.5 px-2.5 justify-center w-full h-full text-left hover:bg-woot-50 dark:hover:bg-woot-800 border-b border-solid border-slate-100 dark:border-slate-700"
|
class="canned-item__button"
|
||||||
:class="{
|
:variant="index === selectedIndex ? '' : 'clear'"
|
||||||
' bg-woot-25 dark:bg-woot-800': index === selectedIndex,
|
:class="{ active: index === selectedIndex }"
|
||||||
}"
|
|
||||||
@click="onListItemSelection(index)"
|
@click="onListItemSelection(index)"
|
||||||
>
|
>
|
||||||
<p
|
<strong>{{ item.label }}</strong> - {{ item.description }}
|
||||||
class="text-slate-900 dark:text-slate-100 group-hover:text-woot-500 dark:group-hover:text-woot-500 font-medium mb-0 text-sm overflow-hidden text-ellipsis whitespace-nowrap min-w-0 max-w-full"
|
</woot-button>
|
||||||
:class="{
|
|
||||||
'text-woot-500 dark:text-woot-500': index === selectedIndex,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ item.description }}
|
|
||||||
</p>
|
|
||||||
<p
|
|
||||||
class="text-slate-500 dark:text-slate-300 group-hover:text-woot-500 dark:group-hover:text-woot-500 mb-0 text-xs overflow-hidden text-ellipsis whitespace-nowrap min-w-0 max-w-full"
|
|
||||||
:class="{
|
|
||||||
'text-woot-500 dark:text-woot-500': index === selectedIndex,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ variableKey(item) }}
|
|
||||||
</p>
|
|
||||||
</button>
|
|
||||||
</woot-dropdown-item>
|
</woot-dropdown-item>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,10 +29,6 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: 'canned',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -97,17 +73,28 @@ export default {
|
|||||||
onSelect() {
|
onSelect() {
|
||||||
this.$emit('mention-select', this.items[this.selectedIndex]);
|
this.$emit('mention-select', this.items[this.selectedIndex]);
|
||||||
},
|
},
|
||||||
variableKey(item = {}) {
|
|
||||||
return this.type === 'variable' ? `{{${item.label}}}` : `/${item.label}`;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.mention--box {
|
.mention--box {
|
||||||
.dropdown-menu__item:last-child > button {
|
@apply bg-white dark:bg-slate-700 rounded-md overflow-auto absolute w-full z-20 pt-2 px-2 pb-0 shadow-md left-0 bottom-full max-h-[9.75rem] border-t border-solid border-slate-75 dark:border-slate-800;
|
||||||
@apply border-0;
|
|
||||||
|
.dropdown-menu__item:last-child {
|
||||||
|
@apply pb-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
@apply text-white dark:text-white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply bg-woot-700 dark:bg-woot-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
@apply transition-none h-8 leading-[1.4];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,10 @@ export default {
|
|||||||
ALL: 'all',
|
ALL: 'all',
|
||||||
},
|
},
|
||||||
SORT_BY_TYPE: {
|
SORT_BY_TYPE: {
|
||||||
LAST_ACTIVITY_AT_ASC: 'last_activity_at_asc',
|
LATEST: 'latest',
|
||||||
LAST_ACTIVITY_AT_DESC: 'last_activity_at_desc',
|
CREATED_AT: 'sort_on_created_at',
|
||||||
CREATED_AT_ASC: 'created_at_asc',
|
PRIORITY: 'sort_on_priority',
|
||||||
CREATED_AT_DESC: 'created_at_desc',
|
WATIING_SINCE: 'waiting_since',
|
||||||
PRIORITY_ASC: 'priority_asc',
|
|
||||||
PRIORITY_DESC: 'priority_desc',
|
|
||||||
WAITING_SINCE_ASC: 'waiting_since_asc',
|
|
||||||
WAITING_SINCE_DESC: 'waiting_since_desc',
|
|
||||||
},
|
},
|
||||||
ARTICLE_STATUS_TYPES: {
|
ARTICLE_STATUS_TYPES: {
|
||||||
DRAFT: 0,
|
DRAFT: 0,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export const CONVERSATION_EVENTS = Object.freeze({
|
|||||||
SEARCH_CONVERSATION: 'Searched conversations',
|
SEARCH_CONVERSATION: 'Searched conversations',
|
||||||
APPLY_FILTER: 'Applied filters in the conversation list',
|
APPLY_FILTER: 'Applied filters in the conversation list',
|
||||||
CHANGE_PRIORITY: 'Assigned priority to a conversation',
|
CHANGE_PRIORITY: 'Assigned priority to a conversation',
|
||||||
INSERT_ARTICLE_LINK: 'Inserted article into reply via article search',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ACCOUNT_EVENTS = Object.freeze({
|
export const ACCOUNT_EVENTS = Object.freeze({
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export class DataManager {
|
|||||||
|
|
||||||
async initDb() {
|
async initDb() {
|
||||||
if (this.db) return this.db;
|
if (this.db) return this.db;
|
||||||
const dbName = `cw-store-${this.accountId}`;
|
|
||||||
this.db = await openDB(`cw-store-${this.accountId}`, DATA_VERSION, {
|
this.db = await openDB(`cw-store-${this.accountId}`, DATA_VERSION, {
|
||||||
upgrade(db) {
|
upgrade(db) {
|
||||||
db.createObjectStore('cache-keys');
|
db.createObjectStore('cache-keys');
|
||||||
@@ -20,13 +19,6 @@ export class DataManager {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store the database name in LocalStorage
|
|
||||||
const dbNames = JSON.parse(localStorage.getItem('cw-idb-names') || '[]');
|
|
||||||
if (!dbNames.includes(dbName)) {
|
|
||||||
dbNames.push(dbName);
|
|
||||||
localStorage.setItem('cw-idb-names', JSON.stringify(dbNames));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.db;
|
return this.db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||||||
'contact.updated': this.onContactUpdate,
|
'contact.updated': this.onContactUpdate,
|
||||||
'conversation.mentioned': this.onConversationMentioned,
|
'conversation.mentioned': this.onConversationMentioned,
|
||||||
'notification.created': this.onNotificationCreated,
|
'notification.created': this.onNotificationCreated,
|
||||||
'notification.deleted': this.onNotificationDeleted,
|
|
||||||
'first.reply.created': this.onFirstReplyCreated,
|
'first.reply.created': this.onFirstReplyCreated,
|
||||||
'conversation.read': this.onConversationRead,
|
'conversation.read': this.onConversationRead,
|
||||||
'conversation.updated': this.onConversationUpdated,
|
'conversation.updated': this.onConversationUpdated,
|
||||||
@@ -196,10 +195,6 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||||||
this.app.$store.dispatch('notifications/addNotification', data);
|
this.app.$store.dispatch('notifications/addNotification', data);
|
||||||
};
|
};
|
||||||
|
|
||||||
onNotificationDeleted = data => {
|
|
||||||
this.app.$store.dispatch('notifications/deleteNotification', data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
onFirstReplyCreated = () => {
|
onFirstReplyCreated = () => {
|
||||||
bus.$emit('fetch_overview_reports');
|
bus.$emit('fetch_overview_reports');
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"FILTER": {
|
"FILTER": {
|
||||||
"TITLE": "Filter conversations",
|
"TITLE": "Filter Conversations",
|
||||||
"SUBTITLE": "Add your filters below and hit 'Apply filters' to cut through the chat clutter.",
|
"SUBTITLE": "Add filters below and hit 'Apply filters' to filter conversations.",
|
||||||
"EDIT_CUSTOM_FILTER": "Edit Folder",
|
"EDIT_CUSTOM_FILTER": "Edit Folder",
|
||||||
"CUSTOM_VIEWS_SUBTITLE": "Add or remove filters and update your folder.",
|
"CUSTOM_VIEWS_SUBTITLE": "Add or remove filters and update your folder.",
|
||||||
"ADD_NEW_FILTER": "Add filter",
|
"ADD_NEW_FILTER": "Add Filter",
|
||||||
"FILTER_DELETE_ERROR": "Oops, looks like we can't save nothing! Please add at least one filter to save it.",
|
"FILTER_DELETE_ERROR": "You should have atleast one filter to save",
|
||||||
"SUBMIT_BUTTON_LABEL": "Apply filters",
|
"SUBMIT_BUTTON_LABEL": "Apply filters",
|
||||||
"UPDATE_BUTTON_LABEL": "Update folder",
|
"UPDATE_BUTTON_LABEL": "Update folder",
|
||||||
"CANCEL_BUTTON_LABEL": "Cancel",
|
"CANCEL_BUTTON_LABEL": "Cancel",
|
||||||
"CLEAR_BUTTON_LABEL": "Clear filters",
|
"CLEAR_BUTTON_LABEL": "Clear Filters",
|
||||||
|
"EMPTY_VALUE_ERROR": "Value is required",
|
||||||
"FOLDER_LABEL": "Folder Name",
|
"FOLDER_LABEL": "Folder Name",
|
||||||
"FOLDER_QUERY_LABEL": "Folder Query",
|
"FOLDER_QUERY_LABEL": "Folder Query",
|
||||||
"EMPTY_VALUE_ERROR": "Value is required.",
|
|
||||||
"TOOLTIP_LABEL": "Filter conversations",
|
"TOOLTIP_LABEL": "Filter conversations",
|
||||||
"QUERY_DROPDOWN_LABELS": {
|
"QUERY_DROPDOWN_LABELS": {
|
||||||
"AND": "AND",
|
"AND": "AND",
|
||||||
@@ -36,44 +36,44 @@
|
|||||||
},
|
},
|
||||||
"ATTRIBUTES": {
|
"ATTRIBUTES": {
|
||||||
"STATUS": "Status",
|
"STATUS": "Status",
|
||||||
"ASSIGNEE_NAME": "Assignee name",
|
"ASSIGNEE_NAME": "Assignee Name",
|
||||||
"INBOX_NAME": "Inbox name",
|
"INBOX_NAME": "Inbox Name",
|
||||||
"TEAM_NAME": "Team name",
|
"TEAM_NAME": "Team Name",
|
||||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
"CONVERSATION_IDENTIFIER": "Conversation Identifier",
|
||||||
"CAMPAIGN_NAME": "Campaign name",
|
"CAMPAIGN_NAME": "Campaign Name",
|
||||||
"LABELS": "Labels",
|
"LABELS": "Labels",
|
||||||
"BROWSER_LANGUAGE": "Browser language",
|
"BROWSER_LANGUAGE": "Browser Language",
|
||||||
"PRIORITY": "Priority",
|
"PRIORITY": "Priority",
|
||||||
"COUNTRY_NAME": "Country name",
|
"COUNTRY_NAME": "Country Name",
|
||||||
"REFERER_LINK": "Referer link",
|
"REFERER_LINK": "Referer link",
|
||||||
"CUSTOM_ATTRIBUTE_LIST": "List",
|
"CUSTOM_ATTRIBUTE_LIST": "List",
|
||||||
"CUSTOM_ATTRIBUTE_TEXT": "Text",
|
"CUSTOM_ATTRIBUTE_TEXT": "Text",
|
||||||
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
|
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
|
||||||
"CUSTOM_ATTRIBUTE_LINK": "Link",
|
"CUSTOM_ATTRIBUTE_LINK": "Link",
|
||||||
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox",
|
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox",
|
||||||
"CREATED_AT": "Created at",
|
"CREATED_AT": "Created At",
|
||||||
"LAST_ACTIVITY": "Last activity"
|
"LAST_ACTIVITY": "Last Activity"
|
||||||
},
|
},
|
||||||
"GROUPS": {
|
"GROUPS": {
|
||||||
"STANDARD_FILTERS": "Standard filters",
|
"STANDARD_FILTERS": "Standard Filters",
|
||||||
"ADDITIONAL_FILTERS": "Additional filters",
|
"ADDITIONAL_FILTERS": "Additional Filters",
|
||||||
"CUSTOM_ATTRIBUTES": "Custom attributes"
|
"CUSTOM_ATTRIBUTES": "Custom Attributes"
|
||||||
},
|
},
|
||||||
"CUSTOM_VIEWS": {
|
"CUSTOM_VIEWS": {
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "Do you want to save this filter?",
|
"TITLE": "Do you want to save this filter?",
|
||||||
"LABEL": "Name this filter",
|
"LABEL": "Name this filter",
|
||||||
"PLACEHOLDER": "Name your filter to refer it later.",
|
"PLACEHOLDER": "Enter a name for this filter",
|
||||||
"ERROR_MESSAGE": "Name is required.",
|
"ERROR_MESSAGE": "Name is required",
|
||||||
"SAVE_BUTTON": "Save filter",
|
"SAVE_BUTTON": "Save filter",
|
||||||
"CANCEL_BUTTON": "Cancel",
|
"CANCEL_BUTTON": "Cancel",
|
||||||
"API_FOLDERS": {
|
"API_FOLDERS": {
|
||||||
"SUCCESS_MESSAGE": "Folder created successfully.",
|
"SUCCESS_MESSAGE": "Folder created successfully",
|
||||||
"ERROR_MESSAGE": "Error while creating folder."
|
"ERROR_MESSAGE": "Error while creating folder"
|
||||||
},
|
},
|
||||||
"API_SEGMENTS": {
|
"API_SEGMENTS": {
|
||||||
"SUCCESS_MESSAGE": "Segment created successfully.",
|
"SUCCESS_MESSAGE": "Segment created successfully",
|
||||||
"ERROR_MESSAGE": "Error while creating segment."
|
"ERROR_MESSAGE": "Error while creating segment"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
@@ -83,19 +83,19 @@
|
|||||||
"DELETE_BUTTON": "Delete filter",
|
"DELETE_BUTTON": "Delete filter",
|
||||||
"MODAL": {
|
"MODAL": {
|
||||||
"CONFIRM": {
|
"CONFIRM": {
|
||||||
"TITLE": "Confirm deletion",
|
"TITLE": "Confirm Deletion",
|
||||||
"MESSAGE": "Are you sure to delete the filter ",
|
"MESSAGE": "Are you sure to delete the filter ",
|
||||||
"YES": "Yes, delete",
|
"YES": "Yes, Delete",
|
||||||
"NO": "No, keep it"
|
"NO": "No, Keep it"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"API_FOLDERS": {
|
"API_FOLDERS": {
|
||||||
"SUCCESS_MESSAGE": "Folder deleted successfully.",
|
"SUCCESS_MESSAGE": "Folder deleted successfully",
|
||||||
"ERROR_MESSAGE": "Error while deleting folder."
|
"ERROR_MESSAGE": "Error while deleting folder"
|
||||||
},
|
},
|
||||||
"API_SEGMENTS": {
|
"API_SEGMENTS": {
|
||||||
"SUCCESS_MESSAGE": "Segment deleted successfully.",
|
"SUCCESS_MESSAGE": "Segment deleted successfully",
|
||||||
"ERROR_MESSAGE": "Error while deleting segment."
|
"ERROR_MESSAGE": "Error while deleting segment"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
{
|
{
|
||||||
"AGENT_BOTS": {
|
"AGENT_BOTS": {
|
||||||
"HEADER": "Bots",
|
"HEADER": "Bots",
|
||||||
"LOADING_EDITOR": "Loading editor...",
|
"LOADING_EDITOR": "Loading Editor...",
|
||||||
"HEADER_BTN_TXT": "Add bot configuration",
|
"HEADER_BTN_TXT": "Add Bot Configuration",
|
||||||
"SIDEBAR_TXT": "<p><b>Agent Bots</b> <p>Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.</p> <p> You can manage your bots from this page or create new ones using the 'Add bot configuraton' button.</p> <p> Open the <a href=\"https://www.chatwoot.com/hc/chatwoot-user-guide-cloud-version/articles/1677497472-how-to-use-agent-bots\" target=\"blank\">Agent bots handbook</a> in another tab for a helping hand.</p>",
|
"SIDEBAR_TXT": "<p><b>Agent Bots</b> <p>Agent bots allows you to automate the conversations</p>",
|
||||||
"CSML_BOT_EDITOR": {
|
"CSML_BOT_EDITOR": {
|
||||||
"NAME": {
|
"NAME": {
|
||||||
"LABEL": "Bot name",
|
"LABEL": "Bot Name",
|
||||||
"PLACEHOLDER": "Name your bot.",
|
"PLACEHOLDER": "Give your bot a name",
|
||||||
"ERROR": "Bot name is required."
|
"ERROR": "Bot name is required"
|
||||||
},
|
},
|
||||||
"DESCRIPTION": {
|
"DESCRIPTION": {
|
||||||
"LABEL": "Bot description",
|
"LABEL": "Bot Description",
|
||||||
"PLACEHOLDER": "What does this bot do?"
|
"PLACEHOLDER": "What does this bot do?"
|
||||||
},
|
},
|
||||||
"BOT_CONFIG": {
|
"BOT_CONFIG": {
|
||||||
"ERROR": "Please enter your CSML bot configuration above.",
|
"ERROR": "Please enter your CSML bot configuration above",
|
||||||
"API_ERROR": "Your CSML configuration is invalid. Please fix it and try again."
|
"API_ERROR": "Your CSML configuration is invalid, please fix it and try again."
|
||||||
},
|
},
|
||||||
"SUBMIT": "Validate and save"
|
"SUBMIT": "Validate and save"
|
||||||
},
|
},
|
||||||
"BOT_CONFIGURATION": {
|
"BOT_CONFIGURATION": {
|
||||||
"TITLE": "Select an agent bot",
|
"TITLE": "Select an agent bot",
|
||||||
"DESC": "Assign an Agent Bot to your inbox. They can handle initial conversations and transfer them to a live agent when necessary.",
|
"DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.",
|
||||||
"SUBMIT": "Update",
|
"SUBMIT": "Update",
|
||||||
"DISCONNECT": "Disconnect bot",
|
"DISCONNECT": "Disconnect Bot",
|
||||||
"SUCCESS_MESSAGE": "Successfully updated the agent bot.",
|
"SUCCESS_MESSAGE": "Successfully updated the agent bot",
|
||||||
"DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot.",
|
"DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot",
|
||||||
"ERROR_MESSAGE": "Could not update the agent bot. Please try again.",
|
"ERROR_MESSAGE": "Could not update the agent bot, please try again later",
|
||||||
"DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot. Please try again.",
|
"DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later",
|
||||||
"SELECT_PLACEHOLDER": "Select bot"
|
"SELECT_PLACEHOLDER": "Select Bot"
|
||||||
},
|
},
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "Configure new bot",
|
"TITLE": "Configure new bot",
|
||||||
"CANCEL_BUTTON_TEXT": "Cancel",
|
"CANCEL_BUTTON_TEXT": "Cancel",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot added successfully.",
|
"SUCCESS_MESSAGE": "Bot added successfully",
|
||||||
"ERROR_MESSAGE": "Could not add bot. Please try again later."
|
"ERROR_MESSAGE": "Could not add bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LIST": {
|
"LIST": {
|
||||||
"404": "No bots found. You can create a bot by clicking the 'Configure new bot' button ↗",
|
"404": "No Bots found, you can create a bot by clicking the 'Configure new bot' Button ↗",
|
||||||
"LOADING": "Fetching bots...",
|
"LOADING": "Fetching Bots...",
|
||||||
"TYPE": "Bot type"
|
"TYPE": "Bot Type"
|
||||||
},
|
},
|
||||||
"DELETE": {
|
"DELETE": {
|
||||||
"BUTTON_TEXT": "Delete",
|
"BUTTON_TEXT": "Delete",
|
||||||
"TITLE": "Delete bot",
|
"TITLE": "Delete Bot",
|
||||||
"SUBMIT": "Delete",
|
"SUBMIT": "Delete",
|
||||||
"CANCEL_BUTTON_TEXT": "Cancel",
|
"CANCEL_BUTTON_TEXT": "Cancel",
|
||||||
"DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible.",
|
"DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot deleted successfully.",
|
"SUCCESS_MESSAGE": "Bot deleted successfully",
|
||||||
"ERROR_MESSAGE": "Could not delete bot. Please try again."
|
"ERROR_MESSAGE": "Could not able to delete bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
"BUTTON_TEXT": "Edit",
|
"BUTTON_TEXT": "Edit",
|
||||||
"LOADING": "Fetching bots...",
|
"LOADING": "Fetching Bots...",
|
||||||
"TITLE": "Edit bot",
|
"TITLE": "Edit Bot",
|
||||||
"CANCEL_BUTTON_TEXT": "Cancel",
|
"CANCEL_BUTTON_TEXT": "Cancel",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot updated successfully.",
|
"SUCCESS_MESSAGE": "Bot updated successfully",
|
||||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
"ERROR_MESSAGE": "Could not update bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TYPES": {
|
"TYPES": {
|
||||||
"WEBHOOK": "Webhook bot",
|
"WEBHOOK": "Webhook Bot",
|
||||||
"CSML": "CSML bot"
|
"CSML": "CSML Bot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,40 @@
|
|||||||
{
|
{
|
||||||
"BULK_ACTION": {
|
"BULK_ACTION": {
|
||||||
"CONVERSATIONS_SELECTED": "%{conversationCount} conversations selected",
|
"CONVERSATIONS_SELECTED": "%{conversationCount} conversations selected",
|
||||||
"AGENT_SELECT_LABEL": "Select agent",
|
"AGENT_SELECT_LABEL": "Select Agent",
|
||||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign %{conversationCount} %{conversationLabel} to",
|
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign %{conversationCount} %{conversationLabel} to",
|
||||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign %{conversationCount} %{conversationLabel}?",
|
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign %{conversationCount} %{conversationLabel}?",
|
||||||
"GO_BACK_LABEL": "Go back",
|
"GO_BACK_LABEL": "Go back",
|
||||||
"ASSIGN_LABEL": "Assign",
|
"ASSIGN_LABEL": "Assign",
|
||||||
"YES": "Yes",
|
"YES": "Yes",
|
||||||
"ASSIGN_AGENT_TOOLTIP": "Assign agent",
|
"ASSIGN_AGENT_TOOLTIP": "Assign Agent",
|
||||||
"ASSIGN_TEAM_TOOLTIP": "Assign team",
|
"ASSIGN_TEAM_TOOLTIP": "Assign team",
|
||||||
"ASSIGN_SUCCESFUL": "Conversations assigned successfully.",
|
"ASSIGN_SUCCESFUL": "Conversations assigned successfully",
|
||||||
"ASSIGN_FAILED": "Failed to assign conversations. Please try again.",
|
"ASSIGN_FAILED": "Failed to assign conversations, please try again",
|
||||||
"RESOLVE_SUCCESFUL": "Conversations resolved successfully.",
|
"RESOLVE_SUCCESFUL": "Conversations resolved successfully",
|
||||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
"RESOLVE_FAILED": "Failed to resolve conversations, please try again",
|
||||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
||||||
"AGENT_LIST_LOADING": "Loading agents",
|
"AGENT_LIST_LOADING": "Loading Agents",
|
||||||
"UPDATE": {
|
"UPDATE": {
|
||||||
"CHANGE_STATUS": "Change status",
|
"CHANGE_STATUS": "Change status",
|
||||||
"SNOOZE_UNTIL_NEXT_REPLY": "Snooze until next reply.",
|
"SNOOZE_UNTIL_NEXT_REPLY": "Snooze until next reply",
|
||||||
"UPDATE_SUCCESFUL": "Conversation status updated successfully.",
|
"UPDATE_SUCCESFUL": "Conversation status updated successfully.",
|
||||||
"UPDATE_FAILED": "Failed to update conversations. Please try again."
|
"UPDATE_FAILED": "Failed to update conversations, please try again"
|
||||||
},
|
},
|
||||||
"LABELS": {
|
"LABELS": {
|
||||||
"ASSIGN_LABELS": "Assign labels",
|
"ASSIGN_LABELS": "Assign Labels",
|
||||||
"NO_LABELS_FOUND": "No labels found for",
|
"NO_LABELS_FOUND": "No labels found for",
|
||||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
"ASSIGN_SUCCESFUL": "Labels assigned successfully",
|
||||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
"ASSIGN_FAILED": "Failed to assign labels, please try again"
|
||||||
},
|
},
|
||||||
"TEAMS": {
|
"TEAMS": {
|
||||||
"TEAM_SELECT_LABEL": "Select team",
|
"TEAM_SELECT_LABEL": "Select Team",
|
||||||
"NONE": "None",
|
"NONE": "None",
|
||||||
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
||||||
"ASSIGN_SELECTED_TEAMS": "Assign selected team.",
|
"ASSIGN_SELECTED_TEAMS": "Assign selected team",
|
||||||
"ASSIGN_SUCCESFUL": "Teams assiged successfully.",
|
"ASSIGN_SUCCESFUL": "Teams assiged successfully",
|
||||||
"ASSIGN_FAILED": "Failed to assign team. Please try again."
|
"ASSIGN_FAILED": "Failed to assign team, please try again"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,75 @@
|
|||||||
{
|
{
|
||||||
"CANNED_MGMT": {
|
"CANNED_MGMT": {
|
||||||
"HEADER": "Canned Responses",
|
"HEADER": "Canned Responses",
|
||||||
"HEADER_BTN_TXT": "Add canned response",
|
"HEADER_BTN_TXT": "Add Canned Response",
|
||||||
"LOADING": "Fetching canned responses...",
|
"LOADING": "Fetching Canned Responses",
|
||||||
"SEARCH_404": "There are no items matching this query.",
|
"SEARCH_404": "There are no items matching this query",
|
||||||
"SIDEBAR_TXT": "<p><b>Canned Responses</b> </p><p> Canned Responses are pre-written reply templates that help you quickly respond to a conversation. To insert a canned response during a chat, agents can type a short code preceded by a '/' character. </p><p> You can manage your canned responses from this page or create new ones using the \"Add canned response\" button.</p><p>Open the <a target=\"_blank\" href=\"https://www.chatwoot.com/hc/chatwoot-user-guide-cloud-version/articles/1677501325-how-to-create-saved-reply-templates-with-canned-responses\">Canned Responses handbook</a> in another tab for a helping hand.</p><p>Also, check out the all-new <a href=\"https://www.chatwoot.com/tools/canned-responses-library\" target=\"_blank\">Canned Responses Library</a>.</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": "There are no canned responses available in this account.",
|
"404": "There are no canned responses available in this account.",
|
||||||
"TITLE": "Manage canned responses",
|
"TITLE": "Manage canned responses",
|
||||||
"DESC": "Canned Responses are predefined reply templates which can be used to quickly send out replies to conversations.",
|
"DESC": "Canned Responses are predefined reply templates which can be used to quickly send out replies to tickets.",
|
||||||
"TABLE_HEADER": [
|
"TABLE_HEADER": [
|
||||||
"Short code",
|
"Short Code",
|
||||||
"Content",
|
"Content",
|
||||||
"Actions"
|
"Actions"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "Add canned response",
|
"TITLE": "Add Canned Response",
|
||||||
"DESC": "Canned Responses are predefined reply templates which can be used to quickly send out replies to conversations.",
|
"DESC": "Canned Responses are saved reply templates which can be used to quickly send out reply to conversation.",
|
||||||
"CANCEL_BUTTON_TEXT": "Cancel",
|
"CANCEL_BUTTON_TEXT": "Cancel",
|
||||||
"FORM": {
|
"FORM": {
|
||||||
"SHORT_CODE": {
|
"SHORT_CODE": {
|
||||||
"LABEL": "Short code",
|
"LABEL": "Short Code",
|
||||||
"PLACEHOLDER": "Please enter a short code.",
|
"PLACEHOLDER": "Please enter a short code",
|
||||||
"ERROR": "Short Code is required."
|
"ERROR": "Short Code is required"
|
||||||
},
|
},
|
||||||
"CONTENT": {
|
"CONTENT": {
|
||||||
"LABEL": "Message",
|
"LABEL": "Content",
|
||||||
"PLACEHOLDER": "Please write the message you want to save as a template to use later.",
|
"PLACEHOLDER": "Please enter a content",
|
||||||
"ERROR": "Message is required."
|
"ERROR": "Content is required"
|
||||||
},
|
},
|
||||||
"SUBMIT": "Submit"
|
"SUBMIT": "Submit"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response added successfully.",
|
"SUCCESS_MESSAGE": "Canned Response added successfully",
|
||||||
"ERROR_MESSAGE": "Could not connect to Woot server. Please try again."
|
"ERROR_MESSAGE": "Could not create canned response. Please try again later."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
"TITLE": "Edit canned response",
|
"TITLE": "Edit Canned Response",
|
||||||
"CANCEL_BUTTON_TEXT": "Cancel",
|
"CANCEL_BUTTON_TEXT": "Cancel",
|
||||||
"FORM": {
|
"FORM": {
|
||||||
"SHORT_CODE": {
|
"SHORT_CODE": {
|
||||||
"LABEL": "Short code",
|
"LABEL": "Short Code",
|
||||||
"PLACEHOLDER": "Please enter a shortcode.",
|
"PLACEHOLDER": "Please enter a shortcode",
|
||||||
"ERROR": "Short code is required."
|
"ERROR": "Short Code is required"
|
||||||
},
|
},
|
||||||
"CONTENT": {
|
"CONTENT": {
|
||||||
"LABEL": "Message",
|
"LABEL": "Content",
|
||||||
"PLACEHOLDER": "Please write the message you want to save as a template to use later.",
|
"PLACEHOLDER": "Please enter a content",
|
||||||
"ERROR": "Message is required."
|
"ERROR": "Content is required"
|
||||||
},
|
},
|
||||||
"SUBMIT": "Submit"
|
"SUBMIT": "Submit"
|
||||||
},
|
},
|
||||||
"BUTTON_TEXT": "Edit",
|
"BUTTON_TEXT": "Edit",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response is updated successfully.",
|
"SUCCESS_MESSAGE": "Canned Response updated successfully",
|
||||||
"ERROR_MESSAGE": "Could not connect to Woot server. Please try again."
|
"ERROR_MESSAGE": "Could not update canned response. Please try again later."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DELETE": {
|
"DELETE": {
|
||||||
"BUTTON_TEXT": "Delete",
|
"BUTTON_TEXT": "Delete",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response deleted successfully.",
|
"SUCCESS_MESSAGE": "Canned response deleted successfully",
|
||||||
"ERROR_MESSAGE": "Could not connect to Woot server. Please try again."
|
"ERROR_MESSAGE": "Could not delete canned response. Please try again later."
|
||||||
},
|
},
|
||||||
"CONFIRM": {
|
"CONFIRM": {
|
||||||
"TITLE": "Confirm deletion",
|
"TITLE": "Confirm Deletion",
|
||||||
"MESSAGE": "Are you sure to delete ",
|
"MESSAGE": "Are you sure to delete ",
|
||||||
"YES": "Yes, delete ",
|
"YES": "Yes, Delete ",
|
||||||
"NO": "No, keep "
|
"NO": "No, Keep "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,30 +51,18 @@
|
|||||||
"ACTIVE": "Last activity"
|
"ACTIVE": "Last activity"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SORT_ORDER_ITEMS": {
|
"CHAT_SORT_FILTER_ITEMS": {
|
||||||
"last_activity_at_asc": {
|
"latest": {
|
||||||
"TEXT": "Last activity: Oldest first"
|
"TEXT": "Last activity"
|
||||||
},
|
},
|
||||||
"last_activity_at_desc": {
|
"sort_on_created_at": {
|
||||||
"TEXT": "Last activity: Newest first"
|
"TEXT": "Created at"
|
||||||
},
|
},
|
||||||
"created_at_desc": {
|
"sort_on_priority": {
|
||||||
"TEXT": "Created at: Newest first"
|
"TEXT": "Priority"
|
||||||
},
|
},
|
||||||
"created_at_asc": {
|
"sort_on_waiting_since": {
|
||||||
"TEXT": "Created at: Oldest first"
|
"TEXT": "Pending Response"
|
||||||
},
|
|
||||||
"priority_desc": {
|
|
||||||
"TEXT": "Priority: Highest first"
|
|
||||||
},
|
|
||||||
"priority_asc": {
|
|
||||||
"TEXT": "Priority: Lowest first"
|
|
||||||
},
|
|
||||||
"waiting_since_asc": {
|
|
||||||
"TEXT": "Pending Response: Longest first"
|
|
||||||
},
|
|
||||||
"waiting_since_desc": {
|
|
||||||
"TEXT": "Pending Response: Shortest first"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ATTACHMENTS": {
|
"ATTACHMENTS": {
|
||||||
|
|||||||
@@ -41,9 +41,6 @@
|
|||||||
"SAVE_CONTACT": "Save",
|
"SAVE_CONTACT": "Save",
|
||||||
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
||||||
"REPLIED_TO_STORY": "Replied to your story",
|
"REPLIED_TO_STORY": "Replied to your story",
|
||||||
"UNSUPPORTED_MESSAGE": "This message is unsupported.",
|
|
||||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "This message is unsupported. You can view this message on the Facebook Messenger app.",
|
|
||||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "This message is unsupported. You can view this message on the Instagram app.",
|
|
||||||
"SUCCESS_DELETE_MESSAGE": "Message deleted successfully",
|
"SUCCESS_DELETE_MESSAGE": "Message deleted successfully",
|
||||||
"FAIL_DELETE_MESSSAGE": "Couldn't delete message! Try again",
|
"FAIL_DELETE_MESSSAGE": "Couldn't delete message! Try again",
|
||||||
"NO_RESPONSE": "No response",
|
"NO_RESPONSE": "No response",
|
||||||
@@ -142,7 +139,6 @@
|
|||||||
"PRIVATE_NOTE": "Private Note",
|
"PRIVATE_NOTE": "Private Note",
|
||||||
"SEND": "Send",
|
"SEND": "Send",
|
||||||
"CREATE": "Add Note",
|
"CREATE": "Add Note",
|
||||||
"INSERT_READ_MORE": "Read more",
|
|
||||||
"DISMISS_REPLY": "Dismiss reply",
|
"DISMISS_REPLY": "Dismiss reply",
|
||||||
"REPLYING_TO": "Replying to:",
|
"REPLYING_TO": "Replying to:",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
},
|
},
|
||||||
"NETWORK": {
|
"NETWORK": {
|
||||||
"NOTIFICATION": {
|
"NOTIFICATION": {
|
||||||
"OFFLINE": "Offline"
|
"TEXT": "Disconnected from Chatwoot"
|
||||||
},
|
},
|
||||||
"BUTTON": {
|
"BUTTON": {
|
||||||
"REFRESH": "Refresh"
|
"REFRESH": "Refresh"
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
"HEADER": {
|
"HEADER": {
|
||||||
"FILTER": "Filter by",
|
"FILTER": "Filter by",
|
||||||
"SORT": "Sort by",
|
"SORT": "Sort by",
|
||||||
"LOCALE": "Locale",
|
|
||||||
"SETTINGS_BUTTON": "Settings",
|
"SETTINGS_BUTTON": "Settings",
|
||||||
"NEW_BUTTON": "New Article",
|
"NEW_BUTTON": "New Article",
|
||||||
"DROPDOWN_OPTIONS": {
|
"DROPDOWN_OPTIONS": {
|
||||||
@@ -16,12 +15,6 @@
|
|||||||
"MINE": "My Articles",
|
"MINE": "My Articles",
|
||||||
"DRAFT": "Draft Articles",
|
"DRAFT": "Draft Articles",
|
||||||
"ARCHIVED": "Archived Articles"
|
"ARCHIVED": "Archived Articles"
|
||||||
},
|
|
||||||
"LOCALE_SELECT": {
|
|
||||||
"TITLE": "Select locale",
|
|
||||||
"PLACEHOLDER": "Select locale",
|
|
||||||
"NO_RESULT": "No locale found",
|
|
||||||
"SEARCH_PLACEHOLDER": "Search locale"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT_HEADER": {
|
"EDIT_HEADER": {
|
||||||
@@ -83,9 +76,6 @@
|
|||||||
},
|
},
|
||||||
"ARTICLE_SEARCH_RESULT": {
|
"ARTICLE_SEARCH_RESULT": {
|
||||||
"UNCATEGORIZED": "Uncategorized",
|
"UNCATEGORIZED": "Uncategorized",
|
||||||
"SEARCH_RESULTS": "Search results for %{query}",
|
|
||||||
"EMPTY_TEXT": "Search for articles to insert into replies.",
|
|
||||||
"SEARCH_LOADER": "Searching...",
|
|
||||||
"INSERT_ARTICLE": "Insert",
|
"INSERT_ARTICLE": "Insert",
|
||||||
"NO_RESULT": "No articles found",
|
"NO_RESULT": "No articles found",
|
||||||
"COPY_LINK": "Copy article link to clipboard",
|
"COPY_LINK": "Copy article link to clipboard",
|
||||||
@@ -230,10 +220,7 @@
|
|||||||
"LOGO": {
|
"LOGO": {
|
||||||
"LABEL": "Logo",
|
"LABEL": "Logo",
|
||||||
"UPLOAD_BUTTON": "Upload logo",
|
"UPLOAD_BUTTON": "Upload logo",
|
||||||
"HELP_TEXT": "This logo will be displayed on the portal header.",
|
"HELP_TEXT": "This logo will be displayed on the portal header."
|
||||||
"IMAGE_UPLOAD_SUCCESS": "Logo uploaded successfully",
|
|
||||||
"IMAGE_UPLOAD_ERROR": "Logo deleted successfully",
|
|
||||||
"IMAGE_DELETE_ERROR": "Error while deleting logo"
|
|
||||||
},
|
},
|
||||||
"NAME": {
|
"NAME": {
|
||||||
"LABEL": "Name",
|
"LABEL": "Name",
|
||||||
@@ -439,21 +426,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ARTICLE_SEARCH": {
|
|
||||||
"TITLE": "Search articles",
|
|
||||||
"PLACEHOLDER": "Search articles",
|
|
||||||
"NO_RESULT": "No articles found",
|
|
||||||
"SEARCHING": "Searching...",
|
|
||||||
"SEARCH_BUTTON": "Search",
|
|
||||||
"INSERT_ARTICLE": "Insert link",
|
|
||||||
"IFRAME_ERROR": "URL is empty or invalid. Unable to display content.",
|
|
||||||
"OPEN_ARTICLE_SEARCH": "Insert article from Help Center",
|
|
||||||
"SUCCESS_ARTICLE_INSERTED": "Article inserted successfully",
|
|
||||||
"PREVIEW_LINK": "Preview article",
|
|
||||||
"CANCEL": "Close",
|
|
||||||
"BACK": "Back",
|
|
||||||
"BACK_RESULTS": "Back to results"
|
|
||||||
},
|
|
||||||
"UPGRADE_PAGE": {
|
"UPGRADE_PAGE": {
|
||||||
"TITLE": "Help Center",
|
"TITLE": "Help Center",
|
||||||
"DESCRIPTION": "Create user-friendly self-service portals. Help your users to access the articles and get support 24/7. Upgrade your subscription to enable this feature.",
|
"DESCRIPTION": "Create user-friendly self-service portals. Help your users to access the articles and get support 24/7. Upgrade your subscription to enable this feature.",
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"FILTER": {
|
"FILTER": {
|
||||||
"TITLE": "تصفية المحادثات",
|
"TITLE": "تصفية المحادثات",
|
||||||
"SUBTITLE": "Add your filters below and hit 'Apply filters' to cut through the chat clutter.",
|
"SUBTITLE": "إضافة فلاتر أدناه واضغط على 'إرسال' لتصفية المحادثات.",
|
||||||
"EDIT_CUSTOM_FILTER": "Edit Folder",
|
"EDIT_CUSTOM_FILTER": "Edit Folder",
|
||||||
"CUSTOM_VIEWS_SUBTITLE": "Add or remove filters and update your folder.",
|
"CUSTOM_VIEWS_SUBTITLE": "Add or remove filters and update your folder.",
|
||||||
"ADD_NEW_FILTER": "Add filter",
|
"ADD_NEW_FILTER": "إضافة عامل تصفية",
|
||||||
"FILTER_DELETE_ERROR": "Oops, looks like we can't save nothing! Please add at least one filter to save it.",
|
"FILTER_DELETE_ERROR": "يجب أن يكون لديك عامل تصفية واحد على الأقل للحفظ",
|
||||||
"SUBMIT_BUTTON_LABEL": "تطبيق عامل التصفية",
|
"SUBMIT_BUTTON_LABEL": "تطبيق عامل التصفية",
|
||||||
"UPDATE_BUTTON_LABEL": "Update folder",
|
"UPDATE_BUTTON_LABEL": "Update folder",
|
||||||
"CANCEL_BUTTON_LABEL": "إلغاء",
|
"CANCEL_BUTTON_LABEL": "إلغاء",
|
||||||
"CLEAR_BUTTON_LABEL": "Clear filters",
|
"CLEAR_BUTTON_LABEL": "مسح عامل التصفية",
|
||||||
|
"EMPTY_VALUE_ERROR": "القيمة مطلوبة",
|
||||||
"FOLDER_LABEL": "Folder Name",
|
"FOLDER_LABEL": "Folder Name",
|
||||||
"FOLDER_QUERY_LABEL": "Folder Query",
|
"FOLDER_QUERY_LABEL": "Folder Query",
|
||||||
"EMPTY_VALUE_ERROR": "القيمة مطلوبة.",
|
|
||||||
"TOOLTIP_LABEL": "تصفية المحادثات",
|
"TOOLTIP_LABEL": "تصفية المحادثات",
|
||||||
"QUERY_DROPDOWN_LABELS": {
|
"QUERY_DROPDOWN_LABELS": {
|
||||||
"AND": "و",
|
"AND": "و",
|
||||||
@@ -36,15 +36,15 @@
|
|||||||
},
|
},
|
||||||
"ATTRIBUTES": {
|
"ATTRIBUTES": {
|
||||||
"STATUS": "الحالة",
|
"STATUS": "الحالة",
|
||||||
"ASSIGNEE_NAME": "Assignee name",
|
"ASSIGNEE_NAME": "اسم المحال إليه",
|
||||||
"INBOX_NAME": "اسم صندوق الوارد",
|
"INBOX_NAME": "اسم صندوق الوارد لقناة التواصل",
|
||||||
"TEAM_NAME": "اسم الفريق",
|
"TEAM_NAME": "اسم الفريق",
|
||||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
"CONVERSATION_IDENTIFIER": "معرف المحادثة",
|
||||||
"CAMPAIGN_NAME": "Campaign name",
|
"CAMPAIGN_NAME": "اسم الحملة",
|
||||||
"LABELS": "الوسوم",
|
"LABELS": "الوسوم",
|
||||||
"BROWSER_LANGUAGE": "Browser language",
|
"BROWSER_LANGUAGE": "لغة المتصفح",
|
||||||
"PRIORITY": "الأولوية",
|
"PRIORITY": "الأولوية",
|
||||||
"COUNTRY_NAME": "Country name",
|
"COUNTRY_NAME": "اسم الدولة",
|
||||||
"REFERER_LINK": "رابط المرجع",
|
"REFERER_LINK": "رابط المرجع",
|
||||||
"CUSTOM_ATTRIBUTE_LIST": "القائمة",
|
"CUSTOM_ATTRIBUTE_LIST": "القائمة",
|
||||||
"CUSTOM_ATTRIBUTE_TEXT": "النص",
|
"CUSTOM_ATTRIBUTE_TEXT": "النص",
|
||||||
@@ -52,28 +52,28 @@
|
|||||||
"CUSTOM_ATTRIBUTE_LINK": "الرابط",
|
"CUSTOM_ATTRIBUTE_LINK": "الرابط",
|
||||||
"CUSTOM_ATTRIBUTE_CHECKBOX": "مربع",
|
"CUSTOM_ATTRIBUTE_CHECKBOX": "مربع",
|
||||||
"CREATED_AT": "تم إنشاؤها في",
|
"CREATED_AT": "تم إنشاؤها في",
|
||||||
"LAST_ACTIVITY": "النشاط الأخير"
|
"LAST_ACTIVITY": "آخر نشاط"
|
||||||
},
|
},
|
||||||
"GROUPS": {
|
"GROUPS": {
|
||||||
"STANDARD_FILTERS": "Standard filters",
|
"STANDARD_FILTERS": "الفلاتر القياسية",
|
||||||
"ADDITIONAL_FILTERS": "Additional filters",
|
"ADDITIONAL_FILTERS": "فلاتر إضافية",
|
||||||
"CUSTOM_ATTRIBUTES": "Custom attributes"
|
"CUSTOM_ATTRIBUTES": "سمات مخصصة"
|
||||||
},
|
},
|
||||||
"CUSTOM_VIEWS": {
|
"CUSTOM_VIEWS": {
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "هل تريد حفظ هذا الفلتر؟",
|
"TITLE": "هل تريد حفظ هذا الفلتر؟",
|
||||||
"LABEL": "تسمية هذا الفلتر",
|
"LABEL": "تسمية هذا الفلتر",
|
||||||
"PLACEHOLDER": "Name your filter to refer it later.",
|
"PLACEHOLDER": "أدخل اسم لهذا الفلتر",
|
||||||
"ERROR_MESSAGE": "الاسم مطلوب.",
|
"ERROR_MESSAGE": "الاسم مطلوب",
|
||||||
"SAVE_BUTTON": "حفظ الفلتر",
|
"SAVE_BUTTON": "حفظ الفلتر",
|
||||||
"CANCEL_BUTTON": "إلغاء",
|
"CANCEL_BUTTON": "إلغاء",
|
||||||
"API_FOLDERS": {
|
"API_FOLDERS": {
|
||||||
"SUCCESS_MESSAGE": "تم إنشاء طريقة عرض مخصصة بنجاح.",
|
"SUCCESS_MESSAGE": "تم إنشاء طريقة عرض مخصصة بنجاح",
|
||||||
"ERROR_MESSAGE": "خطأ أثناء إنشاء طريقة عرض مخصصة."
|
"ERROR_MESSAGE": "خطأ أثناء إنشاء طريقة عرض مخصصة"
|
||||||
},
|
},
|
||||||
"API_SEGMENTS": {
|
"API_SEGMENTS": {
|
||||||
"SUCCESS_MESSAGE": "تم إنشاء طريقة عرض مخصصة بنجاح.",
|
"SUCCESS_MESSAGE": "تم إنشاء طريقة عرض مخصصة بنجاح",
|
||||||
"ERROR_MESSAGE": "خطأ أثناء إنشاء طريقة عرض مخصصة."
|
"ERROR_MESSAGE": "خطأ أثناء إنشاء طريقة عرض مخصصة"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
@@ -85,17 +85,17 @@
|
|||||||
"CONFIRM": {
|
"CONFIRM": {
|
||||||
"TITLE": "تأكيد الحذف",
|
"TITLE": "تأكيد الحذف",
|
||||||
"MESSAGE": "هل أنت متأكد من حذف الفلتر ",
|
"MESSAGE": "هل أنت متأكد من حذف الفلتر ",
|
||||||
"YES": "Yes, delete",
|
"YES": "نعم، احذف",
|
||||||
"NO": "لا، احتفظ به"
|
"NO": "لا، احتفظ به"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"API_FOLDERS": {
|
"API_FOLDERS": {
|
||||||
"SUCCESS_MESSAGE": "تم حذف طريقة عرض مخصصة بنجاح.",
|
"SUCCESS_MESSAGE": "تم حذف طريقة عرض مخصصة بنجاح",
|
||||||
"ERROR_MESSAGE": "حدث خطأ أثناء حذف المجلد."
|
"ERROR_MESSAGE": "حدث خطأ أثناء حذف المجلد"
|
||||||
},
|
},
|
||||||
"API_SEGMENTS": {
|
"API_SEGMENTS": {
|
||||||
"SUCCESS_MESSAGE": "تم حذف العرض المخصص بنجاح.",
|
"SUCCESS_MESSAGE": "تم حذف العرض المخصص بنجاح",
|
||||||
"ERROR_MESSAGE": "حدث خطأ أثناء حذف طريقة عرض مخصصة."
|
"ERROR_MESSAGE": "حدث خطأ أثناء حذف طريقة عرض مخصصة"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
{
|
{
|
||||||
"AGENT_BOTS": {
|
"AGENT_BOTS": {
|
||||||
"HEADER": "Bots",
|
"HEADER": "Bots",
|
||||||
"LOADING_EDITOR": "Loading editor...",
|
"LOADING_EDITOR": "Loading Editor...",
|
||||||
"HEADER_BTN_TXT": "Add bot configuration",
|
"HEADER_BTN_TXT": "Add Bot Configuration",
|
||||||
"SIDEBAR_TXT": "<p><b>Agent Bots</b> <p>Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.</p> <p> You can manage your bots from this page or create new ones using the 'Add bot configuraton' button.</p> <p> Open the <a href=\"https://www.chatwoot.com/hc/chatwoot-user-guide-cloud-version/articles/1677497472-how-to-use-agent-bots\" target=\"blank\">Agent bots handbook</a> in another tab for a helping hand.</p>",
|
"SIDEBAR_TXT": "<p><b>Agent Bots</b> <p>Agent bots allows you to automate the conversations</p>",
|
||||||
"CSML_BOT_EDITOR": {
|
"CSML_BOT_EDITOR": {
|
||||||
"NAME": {
|
"NAME": {
|
||||||
"LABEL": "Bot name",
|
"LABEL": "Bot Name",
|
||||||
"PLACEHOLDER": "Name your bot.",
|
"PLACEHOLDER": "Give your bot a name",
|
||||||
"ERROR": "Bot name is required."
|
"ERROR": "Bot name is required"
|
||||||
},
|
},
|
||||||
"DESCRIPTION": {
|
"DESCRIPTION": {
|
||||||
"LABEL": "Bot description",
|
"LABEL": "Bot Description",
|
||||||
"PLACEHOLDER": "What does this bot do?"
|
"PLACEHOLDER": "What does this bot do?"
|
||||||
},
|
},
|
||||||
"BOT_CONFIG": {
|
"BOT_CONFIG": {
|
||||||
"ERROR": "Please enter your CSML bot configuration above.",
|
"ERROR": "Please enter your CSML bot configuration above",
|
||||||
"API_ERROR": "Your CSML configuration is invalid. Please fix it and try again."
|
"API_ERROR": "Your CSML configuration is invalid, please fix it and try again."
|
||||||
},
|
},
|
||||||
"SUBMIT": "Validate and save"
|
"SUBMIT": "Validate and save"
|
||||||
},
|
},
|
||||||
"BOT_CONFIGURATION": {
|
"BOT_CONFIGURATION": {
|
||||||
"TITLE": "Select an agent bot",
|
"TITLE": "Select an agent bot",
|
||||||
"DESC": "Assign an Agent Bot to your inbox. They can handle initial conversations and transfer them to a live agent when necessary.",
|
"DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.",
|
||||||
"SUBMIT": "تحديث",
|
"SUBMIT": "تحديث",
|
||||||
"DISCONNECT": "Disconnect bot",
|
"DISCONNECT": "Disconnect Bot",
|
||||||
"SUCCESS_MESSAGE": "Successfully updated the agent bot.",
|
"SUCCESS_MESSAGE": "Successfully updated the agent bot",
|
||||||
"DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot.",
|
"DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot",
|
||||||
"ERROR_MESSAGE": "Could not update the agent bot. Please try again.",
|
"ERROR_MESSAGE": "Could not update the agent bot, please try again later",
|
||||||
"DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot. Please try again.",
|
"DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later",
|
||||||
"SELECT_PLACEHOLDER": "Select bot"
|
"SELECT_PLACEHOLDER": "Select Bot"
|
||||||
},
|
},
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "Configure new bot",
|
"TITLE": "Configure new bot",
|
||||||
"CANCEL_BUTTON_TEXT": "إلغاء",
|
"CANCEL_BUTTON_TEXT": "إلغاء",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot added successfully.",
|
"SUCCESS_MESSAGE": "Bot added successfully",
|
||||||
"ERROR_MESSAGE": "Could not add bot. Please try again later."
|
"ERROR_MESSAGE": "Could not add bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LIST": {
|
"LIST": {
|
||||||
"404": "No bots found. You can create a bot by clicking the 'Configure new bot' button ↗",
|
"404": "No Bots found, you can create a bot by clicking the 'Configure new bot' Button ↗",
|
||||||
"LOADING": "Fetching bots...",
|
"LOADING": "Fetching Bots...",
|
||||||
"TYPE": "Bot type"
|
"TYPE": "Bot Type"
|
||||||
},
|
},
|
||||||
"DELETE": {
|
"DELETE": {
|
||||||
"BUTTON_TEXT": "حذف",
|
"BUTTON_TEXT": "حذف",
|
||||||
"TITLE": "Delete bot",
|
"TITLE": "Delete Bot",
|
||||||
"SUBMIT": "حذف",
|
"SUBMIT": "حذف",
|
||||||
"CANCEL_BUTTON_TEXT": "إلغاء",
|
"CANCEL_BUTTON_TEXT": "إلغاء",
|
||||||
"DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible.",
|
"DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot deleted successfully.",
|
"SUCCESS_MESSAGE": "Bot deleted successfully",
|
||||||
"ERROR_MESSAGE": "Could not delete bot. Please try again."
|
"ERROR_MESSAGE": "Could not able to delete bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
"BUTTON_TEXT": "تعديل",
|
"BUTTON_TEXT": "تعديل",
|
||||||
"LOADING": "Fetching bots...",
|
"LOADING": "Fetching Bots...",
|
||||||
"TITLE": "Edit bot",
|
"TITLE": "Edit Bot",
|
||||||
"CANCEL_BUTTON_TEXT": "إلغاء",
|
"CANCEL_BUTTON_TEXT": "إلغاء",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot updated successfully.",
|
"SUCCESS_MESSAGE": "Bot updated successfully",
|
||||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
"ERROR_MESSAGE": "Could not update bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TYPES": {
|
"TYPES": {
|
||||||
"WEBHOOK": "Webhook bot",
|
"WEBHOOK": "Webhook Bot",
|
||||||
"CSML": "CSML bot"
|
"CSML": "CSML Bot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,34 +7,34 @@
|
|||||||
"GO_BACK_LABEL": "العودة للخلف",
|
"GO_BACK_LABEL": "العودة للخلف",
|
||||||
"ASSIGN_LABEL": "تكليف",
|
"ASSIGN_LABEL": "تكليف",
|
||||||
"YES": "نعم",
|
"YES": "نعم",
|
||||||
"ASSIGN_AGENT_TOOLTIP": "تعيين وكيل",
|
"ASSIGN_AGENT_TOOLTIP": "إسناد وكيل",
|
||||||
"ASSIGN_TEAM_TOOLTIP": "تعيين فريق",
|
"ASSIGN_TEAM_TOOLTIP": "تعيين فريق",
|
||||||
"ASSIGN_SUCCESFUL": "تم تعيين المحادثات بنجاح.",
|
"ASSIGN_SUCCESFUL": "تم تعيين المحادثات بنجاح",
|
||||||
"ASSIGN_FAILED": "Failed to assign conversations. Please try again.",
|
"ASSIGN_FAILED": "فشل في تعيين المحادثات، الرجاء المحاولة مرة أخرى",
|
||||||
"RESOLVE_SUCCESFUL": "تم تسوية المحادثات بنجاح.",
|
"RESOLVE_SUCCESFUL": "تم تسوية المحادثات بنجاح",
|
||||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
"RESOLVE_FAILED": "فشل في حل المحادثات، يرجى المحاولة مرة أخرى",
|
||||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "المحادثات المرئية في هذه الصفحة هي المحددة فقط.",
|
"ALL_CONVERSATIONS_SELECTED_ALERT": "المحادثات المرئية في هذه الصفحة هي المحددة فقط.",
|
||||||
"AGENT_LIST_LOADING": "جاري تحميل الوكلاء",
|
"AGENT_LIST_LOADING": "تحميل الوكلاء",
|
||||||
"UPDATE": {
|
"UPDATE": {
|
||||||
"CHANGE_STATUS": "تغيير الحالة",
|
"CHANGE_STATUS": "تغيير الحالة",
|
||||||
"SNOOZE_UNTIL_NEXT_REPLY": "غفوة حتى الرد القادم.",
|
"SNOOZE_UNTIL_NEXT_REPLY": "غفوة حتى الرد القادم",
|
||||||
"UPDATE_SUCCESFUL": "تم تحديث حالة المحادثة بنجاح.",
|
"UPDATE_SUCCESFUL": "تم تحديث حالة المحادثة بنجاح.",
|
||||||
"UPDATE_FAILED": "Failed to update conversations. Please try again."
|
"UPDATE_FAILED": "فشل تحديث المحادثات، الرجاء المحاولة مرة أخرى"
|
||||||
},
|
},
|
||||||
"LABELS": {
|
"LABELS": {
|
||||||
"ASSIGN_LABELS": "Assign labels",
|
"ASSIGN_LABELS": "تعيين التسميات",
|
||||||
"NO_LABELS_FOUND": "لم يتم العثور على تسميات لـ",
|
"NO_LABELS_FOUND": "لم يتم العثور على تسميات لـ",
|
||||||
"ASSIGN_SELECTED_LABELS": "تعيين التسميات المحددة",
|
"ASSIGN_SELECTED_LABELS": "تعيين التسميات المحددة",
|
||||||
"ASSIGN_SUCCESFUL": "تم تعيين التسميات بنجاح.",
|
"ASSIGN_SUCCESFUL": "تم تعيين التسميات بنجاح",
|
||||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
"ASSIGN_FAILED": "فشل في تعيين التسميات ، الرجاء المحاولة مرة أخرى"
|
||||||
},
|
},
|
||||||
"TEAMS": {
|
"TEAMS": {
|
||||||
"TEAM_SELECT_LABEL": "اختيار فريق",
|
"TEAM_SELECT_LABEL": "اختيار فريق",
|
||||||
"NONE": "لا شيء",
|
"NONE": "لا شيء",
|
||||||
"NO_TEAMS_AVAILABLE": "لا توجد فرق مضافة إلى هذا الحساب حتى الآن.",
|
"NO_TEAMS_AVAILABLE": "لا توجد فرق مضافة إلى هذا الحساب حتى الآن.",
|
||||||
"ASSIGN_SELECTED_TEAMS": "تعيين فريق محدد.",
|
"ASSIGN_SELECTED_TEAMS": "تعيين فريق محدد",
|
||||||
"ASSIGN_SUCCESFUL": "تم تعيين الفرق بنجاح.",
|
"ASSIGN_SUCCESFUL": "تم تعيين الفرق بنجاح",
|
||||||
"ASSIGN_FAILED": "Failed to assign team. Please try again."
|
"ASSIGN_FAILED": "فشل تعيين الفريق، الرجاء المحاولة مرة أخرى"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,75 @@
|
|||||||
{
|
{
|
||||||
"CANNED_MGMT": {
|
"CANNED_MGMT": {
|
||||||
"HEADER": "الردود السريعة",
|
"HEADER": "الردود السريعة",
|
||||||
"HEADER_BTN_TXT": "Add canned response",
|
"HEADER_BTN_TXT": "إضافة رد جاهز",
|
||||||
"LOADING": "Fetching canned responses...",
|
"LOADING": "جاري جلب الردود الجاهزة",
|
||||||
"SEARCH_404": "لا توجد عناصر مطابقة لهذا الاستعلام.",
|
"SEARCH_404": "لا توجد عناصر مطابقة لهذا الاستعلام",
|
||||||
"SIDEBAR_TXT": "<p><b>Canned Responses</b> </p><p> Canned Responses are pre-written reply templates that help you quickly respond to a conversation. To insert a canned response during a chat, agents can type a short code preceded by a '/' character. </p><p> You can manage your canned responses from this page or create new ones using the \"Add canned response\" button.</p><p>Open the <a target=\"_blank\" href=\"https://www.chatwoot.com/hc/chatwoot-user-guide-cloud-version/articles/1677501325-how-to-create-saved-reply-templates-with-canned-responses\">Canned Responses handbook</a> in another tab for a helping hand.</p><p>Also, check out the all-new <a href=\"https://www.chatwoot.com/tools/canned-responses-library\" target=\"_blank\">Canned Responses Library</a>.</p>",
|
"SIDEBAR_TXT": "<p><b>الردود الجاهزة</b> </p><p> الردود الجاهزة هي عبارة عن قوالب يتم إعدادها مسبقاً لاستخدامها في كتابة الرسائل والردود للإجابة على اكثر الاسئلة شيوعاً. </p><p> لإضافة قالب جديد، فقط انقر على <b>إضافة رد جاهز</b>. يمكنك أيضاً تعديل أو حذف الردود الجاهزة الأخرى بالنقر على زر التحرير أو الحذف </p><p> يتم تسهيل استخدام الردود المسبقة بواسطة <b>رموز مختصرة</b> لتسريع البحث والوصول إليها. يمكن للموظفين الوصول إلى قوالب الردود الجاهزة أثناء الدردشة عن طريق الضغط على زر <b>'/'</b> ومن ثم الاختصار الخاص بالقالب المراد إدخاله في مربع النص. </p>",
|
||||||
"LIST": {
|
"LIST": {
|
||||||
"404": "لا توجد ردود جاهزة متوفرة في هذا الحساب.",
|
"404": "لا توجد ردود جاهزة متوفرة في هذا الحساب.",
|
||||||
"TITLE": "إدارة الردود الجاهزة",
|
"TITLE": "إدارة الردود الجاهزة",
|
||||||
"DESC": "Canned Responses are predefined reply templates which can be used to quickly send out replies to conversations.",
|
"DESC": "الردود الجاهزة هي قوالب رسائل معدة مسبقاً يمكن استخدامها لتسريع كتابة الردود في المحادثات.",
|
||||||
"TABLE_HEADER": [
|
"TABLE_HEADER": [
|
||||||
"Short code",
|
"الرمز المختصر",
|
||||||
"المحتوى",
|
"المحتوى",
|
||||||
"الإجراءات"
|
"الإجراءات"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "Add canned response",
|
"TITLE": "إضافة رد جاهز",
|
||||||
"DESC": "Canned Responses are predefined reply templates which can be used to quickly send out replies to conversations.",
|
"DESC": "الردود الجاهزة هي قوالب رسائل معدة مسبقاً يمكن استخدامها لتسريع كتابة الردود في المحادثات.",
|
||||||
"CANCEL_BUTTON_TEXT": "إلغاء",
|
"CANCEL_BUTTON_TEXT": "إلغاء",
|
||||||
"FORM": {
|
"FORM": {
|
||||||
"SHORT_CODE": {
|
"SHORT_CODE": {
|
||||||
"LABEL": "Short code",
|
"LABEL": "الرمز المختصر",
|
||||||
"PLACEHOLDER": "Please enter a short code.",
|
"PLACEHOLDER": "الرجاء إدخال رمز مختصر",
|
||||||
"ERROR": "Short Code is required."
|
"ERROR": "الرمز المختصر مطلوب"
|
||||||
},
|
},
|
||||||
"CONTENT": {
|
"CONTENT": {
|
||||||
"LABEL": "رسالة",
|
"LABEL": "المحتوى",
|
||||||
"PLACEHOLDER": "Please write the message you want to save as a template to use later.",
|
"PLACEHOLDER": "الرجاء إدخال محتوى",
|
||||||
"ERROR": "Message is required."
|
"ERROR": "المحتوى مطلوب"
|
||||||
},
|
},
|
||||||
"SUBMIT": "إرسال"
|
"SUBMIT": "إرسال"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response added successfully.",
|
"SUCCESS_MESSAGE": "تم إضافة قالب الرد الجاهز بنجاح",
|
||||||
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
"TITLE": "Edit canned response",
|
"TITLE": "تعديل قالب الرد الجاهز",
|
||||||
"CANCEL_BUTTON_TEXT": "إلغاء",
|
"CANCEL_BUTTON_TEXT": "إلغاء",
|
||||||
"FORM": {
|
"FORM": {
|
||||||
"SHORT_CODE": {
|
"SHORT_CODE": {
|
||||||
"LABEL": "Short code",
|
"LABEL": "الرمز المختصر",
|
||||||
"PLACEHOLDER": "Please enter a shortcode.",
|
"PLACEHOLDER": "الرجاء إدخال رمز مختصر",
|
||||||
"ERROR": "Short code is required."
|
"ERROR": "الرمز المختصر مطلوب"
|
||||||
},
|
},
|
||||||
"CONTENT": {
|
"CONTENT": {
|
||||||
"LABEL": "رسالة",
|
"LABEL": "المحتوى",
|
||||||
"PLACEHOLDER": "Please write the message you want to save as a template to use later.",
|
"PLACEHOLDER": "الرجاء إدخال محتوى",
|
||||||
"ERROR": "الرسالة مطلوبة."
|
"ERROR": "المحتوى مطلوب"
|
||||||
},
|
},
|
||||||
"SUBMIT": "إرسال"
|
"SUBMIT": "إرسال"
|
||||||
},
|
},
|
||||||
"BUTTON_TEXT": "تعديل",
|
"BUTTON_TEXT": "تعديل",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response is updated successfully.",
|
"SUCCESS_MESSAGE": "تم تحديث قالب الرد الجاهز بنجاح",
|
||||||
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DELETE": {
|
"DELETE": {
|
||||||
"BUTTON_TEXT": "حذف",
|
"BUTTON_TEXT": "حذف",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response deleted successfully.",
|
"SUCCESS_MESSAGE": "تم حذف قالب الرد الجاهز بنجاح",
|
||||||
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
||||||
},
|
},
|
||||||
"CONFIRM": {
|
"CONFIRM": {
|
||||||
"TITLE": "تأكيد الحذف",
|
"TITLE": "تأكيد الحذف",
|
||||||
"MESSAGE": "هل أنت متأكد من الحذف ",
|
"MESSAGE": "هل أنت متأكد من الحذف ",
|
||||||
"YES": "Yes, delete ",
|
"YES": "نعم، احذف ",
|
||||||
"NO": "No, keep "
|
"NO": "لا، احتفظ "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,30 +51,18 @@
|
|||||||
"ACTIVE": "النشاط الأخير"
|
"ACTIVE": "النشاط الأخير"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SORT_ORDER_ITEMS": {
|
"CHAT_SORT_FILTER_ITEMS": {
|
||||||
"last_activity_at_asc": {
|
"latest": {
|
||||||
"TEXT": "Last activity: Oldest first"
|
"TEXT": "النشاط الأخير"
|
||||||
},
|
},
|
||||||
"last_activity_at_desc": {
|
"sort_on_created_at": {
|
||||||
"TEXT": "Last activity: Newest first"
|
"TEXT": "تم إنشاؤها في"
|
||||||
},
|
},
|
||||||
"created_at_desc": {
|
"sort_on_priority": {
|
||||||
"TEXT": "Created at: Newest first"
|
"TEXT": "الأولوية"
|
||||||
},
|
},
|
||||||
"created_at_asc": {
|
"sort_on_waiting_since": {
|
||||||
"TEXT": "Created at: Oldest first"
|
"TEXT": "Pending Response"
|
||||||
},
|
|
||||||
"priority_desc": {
|
|
||||||
"TEXT": "Priority: Highest first"
|
|
||||||
},
|
|
||||||
"priority_asc": {
|
|
||||||
"TEXT": "Priority: Lowest first"
|
|
||||||
},
|
|
||||||
"waiting_since_asc": {
|
|
||||||
"TEXT": "Pending Response: Longest first"
|
|
||||||
},
|
|
||||||
"waiting_since_desc": {
|
|
||||||
"TEXT": "Pending Response: Shortest first"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ATTACHMENTS": {
|
"ATTACHMENTS": {
|
||||||
|
|||||||
@@ -41,9 +41,6 @@
|
|||||||
"SAVE_CONTACT": "حفظ",
|
"SAVE_CONTACT": "حفظ",
|
||||||
"UPLOADING_ATTACHMENTS": "جاري تحميل المرفقات...",
|
"UPLOADING_ATTACHMENTS": "جاري تحميل المرفقات...",
|
||||||
"REPLIED_TO_STORY": "Replied to your story",
|
"REPLIED_TO_STORY": "Replied to your story",
|
||||||
"UNSUPPORTED_MESSAGE": "This message is unsupported.",
|
|
||||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "This message is unsupported. You can view this message on the Facebook Messenger app.",
|
|
||||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "This message is unsupported. You can view this message on the Instagram app.",
|
|
||||||
"SUCCESS_DELETE_MESSAGE": "تم حذف الرسالة بنجاح",
|
"SUCCESS_DELETE_MESSAGE": "تم حذف الرسالة بنجاح",
|
||||||
"FAIL_DELETE_MESSSAGE": "تعذر حذف الرسالة! حاول مرة أخرى",
|
"FAIL_DELETE_MESSSAGE": "تعذر حذف الرسالة! حاول مرة أخرى",
|
||||||
"NO_RESPONSE": "لا توجد استجابة",
|
"NO_RESPONSE": "لا توجد استجابة",
|
||||||
@@ -142,7 +139,6 @@
|
|||||||
"PRIVATE_NOTE": "إضافة ملاحظة خاصة",
|
"PRIVATE_NOTE": "إضافة ملاحظة خاصة",
|
||||||
"SEND": "إرسال",
|
"SEND": "إرسال",
|
||||||
"CREATE": "إضافة ملاحظة",
|
"CREATE": "إضافة ملاحظة",
|
||||||
"INSERT_READ_MORE": "Read more",
|
|
||||||
"DISMISS_REPLY": "Dismiss reply",
|
"DISMISS_REPLY": "Dismiss reply",
|
||||||
"REPLYING_TO": "Replying to:",
|
"REPLYING_TO": "Replying to:",
|
||||||
"TIP_FORMAT_ICON": "عرض محرر النصوص",
|
"TIP_FORMAT_ICON": "عرض محرر النصوص",
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
},
|
},
|
||||||
"NETWORK": {
|
"NETWORK": {
|
||||||
"NOTIFICATION": {
|
"NOTIFICATION": {
|
||||||
"OFFLINE": "غير متصل"
|
"TEXT": "قطع الاتصال من Chatwoot"
|
||||||
},
|
},
|
||||||
"BUTTON": {
|
"BUTTON": {
|
||||||
"REFRESH": "تحديث"
|
"REFRESH": "تحديث"
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
"HEADER": {
|
"HEADER": {
|
||||||
"FILTER": "تصفية حسب",
|
"FILTER": "تصفية حسب",
|
||||||
"SORT": "ترتيب حسب",
|
"SORT": "ترتيب حسب",
|
||||||
"LOCALE": "اللغة",
|
|
||||||
"SETTINGS_BUTTON": "الإعدادات",
|
"SETTINGS_BUTTON": "الإعدادات",
|
||||||
"NEW_BUTTON": "مقالة جديدة",
|
"NEW_BUTTON": "مقالة جديدة",
|
||||||
"DROPDOWN_OPTIONS": {
|
"DROPDOWN_OPTIONS": {
|
||||||
@@ -16,12 +15,6 @@
|
|||||||
"MINE": "مقالاتي",
|
"MINE": "مقالاتي",
|
||||||
"DRAFT": "مقالات مسودة",
|
"DRAFT": "مقالات مسودة",
|
||||||
"ARCHIVED": "المقالات المؤرشفة"
|
"ARCHIVED": "المقالات المؤرشفة"
|
||||||
},
|
|
||||||
"LOCALE_SELECT": {
|
|
||||||
"TITLE": "Select locale",
|
|
||||||
"PLACEHOLDER": "Select locale",
|
|
||||||
"NO_RESULT": "No locale found",
|
|
||||||
"SEARCH_PLACEHOLDER": "Search locale"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT_HEADER": {
|
"EDIT_HEADER": {
|
||||||
@@ -83,9 +76,6 @@
|
|||||||
},
|
},
|
||||||
"ARTICLE_SEARCH_RESULT": {
|
"ARTICLE_SEARCH_RESULT": {
|
||||||
"UNCATEGORIZED": "Uncategorized",
|
"UNCATEGORIZED": "Uncategorized",
|
||||||
"SEARCH_RESULTS": "Search results for %{query}",
|
|
||||||
"EMPTY_TEXT": "Search for articles to insert into replies.",
|
|
||||||
"SEARCH_LOADER": "يبحث...",
|
|
||||||
"INSERT_ARTICLE": "Insert",
|
"INSERT_ARTICLE": "Insert",
|
||||||
"NO_RESULT": "No articles found",
|
"NO_RESULT": "No articles found",
|
||||||
"COPY_LINK": "Copy article link to clipboard",
|
"COPY_LINK": "Copy article link to clipboard",
|
||||||
@@ -230,10 +220,7 @@
|
|||||||
"LOGO": {
|
"LOGO": {
|
||||||
"LABEL": "Logo",
|
"LABEL": "Logo",
|
||||||
"UPLOAD_BUTTON": "Upload logo",
|
"UPLOAD_BUTTON": "Upload logo",
|
||||||
"HELP_TEXT": "This logo will be displayed on the portal header.",
|
"HELP_TEXT": "This logo will be displayed on the portal header."
|
||||||
"IMAGE_UPLOAD_SUCCESS": "Logo uploaded successfully",
|
|
||||||
"IMAGE_UPLOAD_ERROR": "Logo deleted successfully",
|
|
||||||
"IMAGE_DELETE_ERROR": "Error while deleting logo"
|
|
||||||
},
|
},
|
||||||
"NAME": {
|
"NAME": {
|
||||||
"LABEL": "الاسم",
|
"LABEL": "الاسم",
|
||||||
@@ -439,21 +426,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ARTICLE_SEARCH": {
|
|
||||||
"TITLE": "Search articles",
|
|
||||||
"PLACEHOLDER": "Search articles",
|
|
||||||
"NO_RESULT": "No articles found",
|
|
||||||
"SEARCHING": "يبحث...",
|
|
||||||
"SEARCH_BUTTON": "بحث",
|
|
||||||
"INSERT_ARTICLE": "Insert link",
|
|
||||||
"IFRAME_ERROR": "URL is empty or invalid. Unable to display content.",
|
|
||||||
"OPEN_ARTICLE_SEARCH": "Insert article from Help Center",
|
|
||||||
"SUCCESS_ARTICLE_INSERTED": "Article inserted successfully",
|
|
||||||
"PREVIEW_LINK": "Preview article",
|
|
||||||
"CANCEL": "أغلق",
|
|
||||||
"BACK": "العودة",
|
|
||||||
"BACK_RESULTS": "Back to results"
|
|
||||||
},
|
|
||||||
"UPGRADE_PAGE": {
|
"UPGRADE_PAGE": {
|
||||||
"TITLE": "Help Center",
|
"TITLE": "Help Center",
|
||||||
"DESCRIPTION": "Create user-friendly self-service portals. Help your users to access the articles and get support 24/7. Upgrade your subscription to enable this feature.",
|
"DESCRIPTION": "Create user-friendly self-service portals. Help your users to access the articles and get support 24/7. Upgrade your subscription to enable this feature.",
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"FILTER": {
|
"FILTER": {
|
||||||
"TITLE": "Филтрирай разговорите",
|
"TITLE": "Филтрирай разговорите",
|
||||||
"SUBTITLE": "Add your filters below and hit 'Apply filters' to cut through the chat clutter.",
|
"SUBTITLE": "Добавете филтри по-долу и натиснете „Прилагане на филтри“, за да филтрирате разговорите.",
|
||||||
"EDIT_CUSTOM_FILTER": "Edit Folder",
|
"EDIT_CUSTOM_FILTER": "Edit Folder",
|
||||||
"CUSTOM_VIEWS_SUBTITLE": "Add or remove filters and update your folder.",
|
"CUSTOM_VIEWS_SUBTITLE": "Add or remove filters and update your folder.",
|
||||||
"ADD_NEW_FILTER": "Add filter",
|
"ADD_NEW_FILTER": "Добавяне на филтър",
|
||||||
"FILTER_DELETE_ERROR": "Oops, looks like we can't save nothing! Please add at least one filter to save it.",
|
"FILTER_DELETE_ERROR": "За да запазите, трябва да имате поне един филтър",
|
||||||
"SUBMIT_BUTTON_LABEL": "Прилагане на филтри",
|
"SUBMIT_BUTTON_LABEL": "Прилагане на филтри",
|
||||||
"UPDATE_BUTTON_LABEL": "Update folder",
|
"UPDATE_BUTTON_LABEL": "Update folder",
|
||||||
"CANCEL_BUTTON_LABEL": "Отмени",
|
"CANCEL_BUTTON_LABEL": "Отмени",
|
||||||
"CLEAR_BUTTON_LABEL": "Clear filters",
|
"CLEAR_BUTTON_LABEL": "Изчисти филтрите",
|
||||||
|
"EMPTY_VALUE_ERROR": "Изисква се стойност",
|
||||||
"FOLDER_LABEL": "Folder Name",
|
"FOLDER_LABEL": "Folder Name",
|
||||||
"FOLDER_QUERY_LABEL": "Folder Query",
|
"FOLDER_QUERY_LABEL": "Folder Query",
|
||||||
"EMPTY_VALUE_ERROR": "Изисква се стойност.",
|
|
||||||
"TOOLTIP_LABEL": "Филтрирай разговорите",
|
"TOOLTIP_LABEL": "Филтрирай разговорите",
|
||||||
"QUERY_DROPDOWN_LABELS": {
|
"QUERY_DROPDOWN_LABELS": {
|
||||||
"AND": "И",
|
"AND": "И",
|
||||||
@@ -36,44 +36,44 @@
|
|||||||
},
|
},
|
||||||
"ATTRIBUTES": {
|
"ATTRIBUTES": {
|
||||||
"STATUS": "Статус",
|
"STATUS": "Статус",
|
||||||
"ASSIGNEE_NAME": "Assignee name",
|
"ASSIGNEE_NAME": "Име на правоприемника",
|
||||||
"INBOX_NAME": "Inbox name",
|
"INBOX_NAME": "Име на входящата кутия",
|
||||||
"TEAM_NAME": "Team name",
|
"TEAM_NAME": "Име на екипа",
|
||||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
"CONVERSATION_IDENTIFIER": "Идентивикатор на разговор",
|
||||||
"CAMPAIGN_NAME": "Campaign name",
|
"CAMPAIGN_NAME": "Име на кампания",
|
||||||
"LABELS": "Етикети",
|
"LABELS": "Етикети",
|
||||||
"BROWSER_LANGUAGE": "Browser language",
|
"BROWSER_LANGUAGE": "Език на браузъра",
|
||||||
"PRIORITY": "Priority",
|
"PRIORITY": "Priority",
|
||||||
"COUNTRY_NAME": "Country name",
|
"COUNTRY_NAME": "Име на държавата",
|
||||||
"REFERER_LINK": "Референтна връзка",
|
"REFERER_LINK": "Референтна връзка",
|
||||||
"CUSTOM_ATTRIBUTE_LIST": "List",
|
"CUSTOM_ATTRIBUTE_LIST": "List",
|
||||||
"CUSTOM_ATTRIBUTE_TEXT": "Text",
|
"CUSTOM_ATTRIBUTE_TEXT": "Text",
|
||||||
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
|
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
|
||||||
"CUSTOM_ATTRIBUTE_LINK": "Link",
|
"CUSTOM_ATTRIBUTE_LINK": "Link",
|
||||||
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox",
|
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox",
|
||||||
"CREATED_AT": "Създаден в",
|
"CREATED_AT": "Created At",
|
||||||
"LAST_ACTIVITY": "Last activity"
|
"LAST_ACTIVITY": "Последна активност"
|
||||||
},
|
},
|
||||||
"GROUPS": {
|
"GROUPS": {
|
||||||
"STANDARD_FILTERS": "Standard filters",
|
"STANDARD_FILTERS": "Standard Filters",
|
||||||
"ADDITIONAL_FILTERS": "Additional filters",
|
"ADDITIONAL_FILTERS": "Additional Filters",
|
||||||
"CUSTOM_ATTRIBUTES": "Custom attributes"
|
"CUSTOM_ATTRIBUTES": "Персонализирани атрибути"
|
||||||
},
|
},
|
||||||
"CUSTOM_VIEWS": {
|
"CUSTOM_VIEWS": {
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "Do you want to save this filter?",
|
"TITLE": "Do you want to save this filter?",
|
||||||
"LABEL": "Name this filter",
|
"LABEL": "Name this filter",
|
||||||
"PLACEHOLDER": "Name your filter to refer it later.",
|
"PLACEHOLDER": "Enter a name for this filter",
|
||||||
"ERROR_MESSAGE": "Name is required.",
|
"ERROR_MESSAGE": "Name is required",
|
||||||
"SAVE_BUTTON": "Save filter",
|
"SAVE_BUTTON": "Save filter",
|
||||||
"CANCEL_BUTTON": "Отмени",
|
"CANCEL_BUTTON": "Отмени",
|
||||||
"API_FOLDERS": {
|
"API_FOLDERS": {
|
||||||
"SUCCESS_MESSAGE": "Folder created successfully.",
|
"SUCCESS_MESSAGE": "Folder created successfully",
|
||||||
"ERROR_MESSAGE": "Error while creating folder."
|
"ERROR_MESSAGE": "Error while creating folder"
|
||||||
},
|
},
|
||||||
"API_SEGMENTS": {
|
"API_SEGMENTS": {
|
||||||
"SUCCESS_MESSAGE": "Segment created successfully.",
|
"SUCCESS_MESSAGE": "Segment created successfully",
|
||||||
"ERROR_MESSAGE": "Error while creating segment."
|
"ERROR_MESSAGE": "Error while creating segment"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
@@ -85,17 +85,17 @@
|
|||||||
"CONFIRM": {
|
"CONFIRM": {
|
||||||
"TITLE": "Потвърди изтриването",
|
"TITLE": "Потвърди изтриването",
|
||||||
"MESSAGE": "Are you sure to delete the filter ",
|
"MESSAGE": "Are you sure to delete the filter ",
|
||||||
"YES": "Yes, delete",
|
"YES": "Да, изтрий",
|
||||||
"NO": "No, keep it"
|
"NO": "No, Keep it"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"API_FOLDERS": {
|
"API_FOLDERS": {
|
||||||
"SUCCESS_MESSAGE": "Folder deleted successfully.",
|
"SUCCESS_MESSAGE": "Folder deleted successfully",
|
||||||
"ERROR_MESSAGE": "Error while deleting folder."
|
"ERROR_MESSAGE": "Error while deleting folder"
|
||||||
},
|
},
|
||||||
"API_SEGMENTS": {
|
"API_SEGMENTS": {
|
||||||
"SUCCESS_MESSAGE": "Segment deleted successfully.",
|
"SUCCESS_MESSAGE": "Segment deleted successfully",
|
||||||
"ERROR_MESSAGE": "Error while deleting segment."
|
"ERROR_MESSAGE": "Error while deleting segment"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
{
|
{
|
||||||
"AGENT_BOTS": {
|
"AGENT_BOTS": {
|
||||||
"HEADER": "Bots",
|
"HEADER": "Bots",
|
||||||
"LOADING_EDITOR": "Loading editor...",
|
"LOADING_EDITOR": "Loading Editor...",
|
||||||
"HEADER_BTN_TXT": "Add bot configuration",
|
"HEADER_BTN_TXT": "Add Bot Configuration",
|
||||||
"SIDEBAR_TXT": "<p><b>Agent Bots</b> <p>Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.</p> <p> You can manage your bots from this page or create new ones using the 'Add bot configuraton' button.</p> <p> Open the <a href=\"https://www.chatwoot.com/hc/chatwoot-user-guide-cloud-version/articles/1677497472-how-to-use-agent-bots\" target=\"blank\">Agent bots handbook</a> in another tab for a helping hand.</p>",
|
"SIDEBAR_TXT": "<p><b>Agent Bots</b> <p>Agent bots allows you to automate the conversations</p>",
|
||||||
"CSML_BOT_EDITOR": {
|
"CSML_BOT_EDITOR": {
|
||||||
"NAME": {
|
"NAME": {
|
||||||
"LABEL": "Bot name",
|
"LABEL": "Bot Name",
|
||||||
"PLACEHOLDER": "Name your bot.",
|
"PLACEHOLDER": "Give your bot a name",
|
||||||
"ERROR": "Bot name is required."
|
"ERROR": "Bot name is required"
|
||||||
},
|
},
|
||||||
"DESCRIPTION": {
|
"DESCRIPTION": {
|
||||||
"LABEL": "Bot description",
|
"LABEL": "Bot Description",
|
||||||
"PLACEHOLDER": "What does this bot do?"
|
"PLACEHOLDER": "What does this bot do?"
|
||||||
},
|
},
|
||||||
"BOT_CONFIG": {
|
"BOT_CONFIG": {
|
||||||
"ERROR": "Please enter your CSML bot configuration above.",
|
"ERROR": "Please enter your CSML bot configuration above",
|
||||||
"API_ERROR": "Your CSML configuration is invalid. Please fix it and try again."
|
"API_ERROR": "Your CSML configuration is invalid, please fix it and try again."
|
||||||
},
|
},
|
||||||
"SUBMIT": "Validate and save"
|
"SUBMIT": "Validate and save"
|
||||||
},
|
},
|
||||||
"BOT_CONFIGURATION": {
|
"BOT_CONFIGURATION": {
|
||||||
"TITLE": "Select an agent bot",
|
"TITLE": "Select an agent bot",
|
||||||
"DESC": "Assign an Agent Bot to your inbox. They can handle initial conversations and transfer them to a live agent when necessary.",
|
"DESC": "You can set an agent bot from the list to this inbox. The bot can initially handle the conversation and transfer it to an agent when needed.",
|
||||||
"SUBMIT": "Обновяване",
|
"SUBMIT": "Обновяване",
|
||||||
"DISCONNECT": "Disconnect bot",
|
"DISCONNECT": "Disconnect Bot",
|
||||||
"SUCCESS_MESSAGE": "Successfully updated the agent bot.",
|
"SUCCESS_MESSAGE": "Successfully updated the agent bot",
|
||||||
"DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot.",
|
"DISCONNECTED_SUCCESS_MESSAGE": "Successfully disconnected the agent bot",
|
||||||
"ERROR_MESSAGE": "Could not update the agent bot. Please try again.",
|
"ERROR_MESSAGE": "Could not update the agent bot, please try again later",
|
||||||
"DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot. Please try again.",
|
"DISCONNECTED_ERROR_MESSAGE": "Could not disconnect the agent bot, please try again later",
|
||||||
"SELECT_PLACEHOLDER": "Select bot"
|
"SELECT_PLACEHOLDER": "Select Bot"
|
||||||
},
|
},
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "Configure new bot",
|
"TITLE": "Configure new bot",
|
||||||
"CANCEL_BUTTON_TEXT": "Отмени",
|
"CANCEL_BUTTON_TEXT": "Отмени",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot added successfully.",
|
"SUCCESS_MESSAGE": "Bot added successfully",
|
||||||
"ERROR_MESSAGE": "Could not add bot. Please try again later."
|
"ERROR_MESSAGE": "Could not add bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LIST": {
|
"LIST": {
|
||||||
"404": "No bots found. You can create a bot by clicking the 'Configure new bot' button ↗",
|
"404": "No Bots found, you can create a bot by clicking the 'Configure new bot' Button ↗",
|
||||||
"LOADING": "Fetching bots...",
|
"LOADING": "Fetching Bots...",
|
||||||
"TYPE": "Bot type"
|
"TYPE": "Bot Type"
|
||||||
},
|
},
|
||||||
"DELETE": {
|
"DELETE": {
|
||||||
"BUTTON_TEXT": "Изтрий",
|
"BUTTON_TEXT": "Изтрий",
|
||||||
"TITLE": "Delete bot",
|
"TITLE": "Delete Bot",
|
||||||
"SUBMIT": "Изтрий",
|
"SUBMIT": "Изтрий",
|
||||||
"CANCEL_BUTTON_TEXT": "Отмени",
|
"CANCEL_BUTTON_TEXT": "Отмени",
|
||||||
"DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible.",
|
"DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot deleted successfully.",
|
"SUCCESS_MESSAGE": "Bot deleted successfully",
|
||||||
"ERROR_MESSAGE": "Could not delete bot. Please try again."
|
"ERROR_MESSAGE": "Could not able to delete bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
"BUTTON_TEXT": "Редактирай",
|
"BUTTON_TEXT": "Редактирай",
|
||||||
"LOADING": "Fetching bots...",
|
"LOADING": "Fetching Bots...",
|
||||||
"TITLE": "Edit bot",
|
"TITLE": "Edit Bot",
|
||||||
"CANCEL_BUTTON_TEXT": "Отмени",
|
"CANCEL_BUTTON_TEXT": "Отмени",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Bot updated successfully.",
|
"SUCCESS_MESSAGE": "Bot updated successfully",
|
||||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
"ERROR_MESSAGE": "Could not update bot, Please try again later"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TYPES": {
|
"TYPES": {
|
||||||
"WEBHOOK": "Webhook bot",
|
"WEBHOOK": "Webhook Bot",
|
||||||
"CSML": "CSML bot"
|
"CSML": "CSML Bot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,40 @@
|
|||||||
{
|
{
|
||||||
"BULK_ACTION": {
|
"BULK_ACTION": {
|
||||||
"CONVERSATIONS_SELECTED": "%{conversationCount} conversations selected",
|
"CONVERSATIONS_SELECTED": "%{conversationCount} conversations selected",
|
||||||
"AGENT_SELECT_LABEL": "Изберете агент",
|
"AGENT_SELECT_LABEL": "Select Agent",
|
||||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign %{conversationCount} %{conversationLabel} to",
|
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign %{conversationCount} %{conversationLabel} to",
|
||||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign %{conversationCount} %{conversationLabel}?",
|
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign %{conversationCount} %{conversationLabel}?",
|
||||||
"GO_BACK_LABEL": "Go back",
|
"GO_BACK_LABEL": "Go back",
|
||||||
"ASSIGN_LABEL": "Assign",
|
"ASSIGN_LABEL": "Assign",
|
||||||
"YES": "Yes",
|
"YES": "Yes",
|
||||||
"ASSIGN_AGENT_TOOLTIP": "Assign agent",
|
"ASSIGN_AGENT_TOOLTIP": "Assign Agent",
|
||||||
"ASSIGN_TEAM_TOOLTIP": "Assign team",
|
"ASSIGN_TEAM_TOOLTIP": "Assign team",
|
||||||
"ASSIGN_SUCCESFUL": "Conversations assigned successfully.",
|
"ASSIGN_SUCCESFUL": "Conversations assigned successfully",
|
||||||
"ASSIGN_FAILED": "Failed to assign conversations. Please try again.",
|
"ASSIGN_FAILED": "Failed to assign conversations, please try again",
|
||||||
"RESOLVE_SUCCESFUL": "Conversations resolved successfully.",
|
"RESOLVE_SUCCESFUL": "Conversations resolved successfully",
|
||||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
"RESOLVE_FAILED": "Failed to resolve conversations, please try again",
|
||||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
||||||
"AGENT_LIST_LOADING": "Loading agents",
|
"AGENT_LIST_LOADING": "Loading Agents",
|
||||||
"UPDATE": {
|
"UPDATE": {
|
||||||
"CHANGE_STATUS": "Change status",
|
"CHANGE_STATUS": "Change status",
|
||||||
"SNOOZE_UNTIL_NEXT_REPLY": "Snooze until next reply.",
|
"SNOOZE_UNTIL_NEXT_REPLY": "Snooze until next reply",
|
||||||
"UPDATE_SUCCESFUL": "Conversation status updated successfully.",
|
"UPDATE_SUCCESFUL": "Conversation status updated successfully.",
|
||||||
"UPDATE_FAILED": "Failed to update conversations. Please try again."
|
"UPDATE_FAILED": "Failed to update conversations, please try again"
|
||||||
},
|
},
|
||||||
"LABELS": {
|
"LABELS": {
|
||||||
"ASSIGN_LABELS": "Assign labels",
|
"ASSIGN_LABELS": "Assign Labels",
|
||||||
"NO_LABELS_FOUND": "No labels found for",
|
"NO_LABELS_FOUND": "No labels found for",
|
||||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
"ASSIGN_SUCCESFUL": "Labels assigned successfully",
|
||||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
"ASSIGN_FAILED": "Failed to assign labels, please try again"
|
||||||
},
|
},
|
||||||
"TEAMS": {
|
"TEAMS": {
|
||||||
"TEAM_SELECT_LABEL": "Изберете екип",
|
"TEAM_SELECT_LABEL": "Select Team",
|
||||||
"NONE": "Нито един",
|
"NONE": "Нито един",
|
||||||
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
||||||
"ASSIGN_SELECTED_TEAMS": "Assign selected team.",
|
"ASSIGN_SELECTED_TEAMS": "Assign selected team",
|
||||||
"ASSIGN_SUCCESFUL": "Teams assiged successfully.",
|
"ASSIGN_SUCCESFUL": "Teams assiged successfully",
|
||||||
"ASSIGN_FAILED": "Failed to assign team. Please try again."
|
"ASSIGN_FAILED": "Failed to assign team, please try again"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,75 @@
|
|||||||
{
|
{
|
||||||
"CANNED_MGMT": {
|
"CANNED_MGMT": {
|
||||||
"HEADER": "Готови отговори",
|
"HEADER": "Готови отговори",
|
||||||
"HEADER_BTN_TXT": "Add canned response",
|
"HEADER_BTN_TXT": "Добавяне на готов отговор",
|
||||||
"LOADING": "Fetching canned responses...",
|
"LOADING": "Извличане на готови отговори",
|
||||||
"SEARCH_404": "Няма резултати отговарящи на тази заявка.",
|
"SEARCH_404": "Няма резултати отговарящи на тази заявка",
|
||||||
"SIDEBAR_TXT": "<p><b>Canned Responses</b> </p><p> Canned Responses are pre-written reply templates that help you quickly respond to a conversation. To insert a canned response during a chat, agents can type a short code preceded by a '/' character. </p><p> You can manage your canned responses from this page or create new ones using the \"Add canned response\" button.</p><p>Open the <a target=\"_blank\" href=\"https://www.chatwoot.com/hc/chatwoot-user-guide-cloud-version/articles/1677501325-how-to-create-saved-reply-templates-with-canned-responses\">Canned Responses handbook</a> in another tab for a helping hand.</p><p>Also, check out the all-new <a href=\"https://www.chatwoot.com/tools/canned-responses-library\" target=\"_blank\">Canned Responses Library</a>.</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": "Управлявайте готовите отговори",
|
||||||
"DESC": "Canned Responses are predefined reply templates which can be used to quickly send out replies to conversations.",
|
"DESC": "Готовите отговори са предварително дефинирани шаблони за отговор, които могат да се изпращат бързо в чата.",
|
||||||
"TABLE_HEADER": [
|
"TABLE_HEADER": [
|
||||||
"Short code",
|
"Кратък код",
|
||||||
"Съдържание",
|
"Съдържание",
|
||||||
"Действия"
|
"Действия"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ADD": {
|
"ADD": {
|
||||||
"TITLE": "Add canned response",
|
"TITLE": "Добавяне на готов отговор",
|
||||||
"DESC": "Canned Responses are predefined reply templates which can be used to quickly send out replies to conversations.",
|
"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": "Short code",
|
"LABEL": "Кратък код",
|
||||||
"PLACEHOLDER": "Please enter a short code.",
|
"PLACEHOLDER": "Please enter a short code",
|
||||||
"ERROR": "Short Code is required."
|
"ERROR": "Краткия код е задължителен"
|
||||||
},
|
},
|
||||||
"CONTENT": {
|
"CONTENT": {
|
||||||
"LABEL": "Съобщение",
|
"LABEL": "Съдържание",
|
||||||
"PLACEHOLDER": "Please write the message you want to save as a template to use later.",
|
"PLACEHOLDER": "Моля, въведете съдържание",
|
||||||
"ERROR": "Message is required."
|
"ERROR": "Съдържанието е задължително"
|
||||||
},
|
},
|
||||||
"SUBMIT": "Изпращане"
|
"SUBMIT": "Изпращане"
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response added successfully.",
|
"SUCCESS_MESSAGE": "Готовия отговор е добавен успешно",
|
||||||
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
"TITLE": "Edit canned response",
|
"TITLE": "Промени готовия отговор",
|
||||||
"CANCEL_BUTTON_TEXT": "Отмени",
|
"CANCEL_BUTTON_TEXT": "Отмени",
|
||||||
"FORM": {
|
"FORM": {
|
||||||
"SHORT_CODE": {
|
"SHORT_CODE": {
|
||||||
"LABEL": "Short code",
|
"LABEL": "Кратък код",
|
||||||
"PLACEHOLDER": "Please enter a shortcode.",
|
"PLACEHOLDER": "Моля, въведете кратък код",
|
||||||
"ERROR": "Short code is required."
|
"ERROR": "Краткия код е задължителен"
|
||||||
},
|
},
|
||||||
"CONTENT": {
|
"CONTENT": {
|
||||||
"LABEL": "Съобщение",
|
"LABEL": "Съдържание",
|
||||||
"PLACEHOLDER": "Please write the message you want to save as a template to use later.",
|
"PLACEHOLDER": "Моля, въведете съдържание",
|
||||||
"ERROR": "Message is required."
|
"ERROR": "Съдържанието е задължително"
|
||||||
},
|
},
|
||||||
"SUBMIT": "Изпращане"
|
"SUBMIT": "Изпращане"
|
||||||
},
|
},
|
||||||
"BUTTON_TEXT": "Редактирай",
|
"BUTTON_TEXT": "Редактирай",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response is updated successfully.",
|
"SUCCESS_MESSAGE": "Готовия отговор е обновен успешно",
|
||||||
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DELETE": {
|
"DELETE": {
|
||||||
"BUTTON_TEXT": "Изтрий",
|
"BUTTON_TEXT": "Изтрий",
|
||||||
"API": {
|
"API": {
|
||||||
"SUCCESS_MESSAGE": "Canned response deleted successfully.",
|
"SUCCESS_MESSAGE": "Готовия отговор е изтрит успешно",
|
||||||
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
||||||
},
|
},
|
||||||
"CONFIRM": {
|
"CONFIRM": {
|
||||||
"TITLE": "Потвърди изтриването",
|
"TITLE": "Потвърди изтриването",
|
||||||
"MESSAGE": "Сигурни ли сте за изтриването ",
|
"MESSAGE": "Сигурни ли сте за изтриването ",
|
||||||
"YES": "Yes, delete ",
|
"YES": "Да, изтрий ",
|
||||||
"NO": "No, keep "
|
"NO": "Не, запази "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,30 +51,18 @@
|
|||||||
"ACTIVE": "Last activity"
|
"ACTIVE": "Last activity"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SORT_ORDER_ITEMS": {
|
"CHAT_SORT_FILTER_ITEMS": {
|
||||||
"last_activity_at_asc": {
|
"latest": {
|
||||||
"TEXT": "Last activity: Oldest first"
|
"TEXT": "Last activity"
|
||||||
},
|
},
|
||||||
"last_activity_at_desc": {
|
"sort_on_created_at": {
|
||||||
"TEXT": "Last activity: Newest first"
|
"TEXT": "Създаден в"
|
||||||
},
|
},
|
||||||
"created_at_desc": {
|
"sort_on_priority": {
|
||||||
"TEXT": "Created at: Newest first"
|
"TEXT": "Priority"
|
||||||
},
|
},
|
||||||
"created_at_asc": {
|
"sort_on_waiting_since": {
|
||||||
"TEXT": "Created at: Oldest first"
|
"TEXT": "Pending Response"
|
||||||
},
|
|
||||||
"priority_desc": {
|
|
||||||
"TEXT": "Priority: Highest first"
|
|
||||||
},
|
|
||||||
"priority_asc": {
|
|
||||||
"TEXT": "Priority: Lowest first"
|
|
||||||
},
|
|
||||||
"waiting_since_asc": {
|
|
||||||
"TEXT": "Pending Response: Longest first"
|
|
||||||
},
|
|
||||||
"waiting_since_desc": {
|
|
||||||
"TEXT": "Pending Response: Shortest first"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ATTACHMENTS": {
|
"ATTACHMENTS": {
|
||||||
|
|||||||
@@ -41,9 +41,6 @@
|
|||||||
"SAVE_CONTACT": "Save",
|
"SAVE_CONTACT": "Save",
|
||||||
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
"UPLOADING_ATTACHMENTS": "Uploading attachments...",
|
||||||
"REPLIED_TO_STORY": "Replied to your story",
|
"REPLIED_TO_STORY": "Replied to your story",
|
||||||
"UNSUPPORTED_MESSAGE": "This message is unsupported.",
|
|
||||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "This message is unsupported. You can view this message on the Facebook Messenger app.",
|
|
||||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "This message is unsupported. You can view this message on the Instagram app.",
|
|
||||||
"SUCCESS_DELETE_MESSAGE": "Message deleted successfully",
|
"SUCCESS_DELETE_MESSAGE": "Message deleted successfully",
|
||||||
"FAIL_DELETE_MESSSAGE": "Couldn't delete message! Try again",
|
"FAIL_DELETE_MESSSAGE": "Couldn't delete message! Try again",
|
||||||
"NO_RESPONSE": "No response",
|
"NO_RESPONSE": "No response",
|
||||||
@@ -142,7 +139,6 @@
|
|||||||
"PRIVATE_NOTE": "Private Note",
|
"PRIVATE_NOTE": "Private Note",
|
||||||
"SEND": "Send",
|
"SEND": "Send",
|
||||||
"CREATE": "Add Note",
|
"CREATE": "Add Note",
|
||||||
"INSERT_READ_MORE": "Read more",
|
|
||||||
"DISMISS_REPLY": "Dismiss reply",
|
"DISMISS_REPLY": "Dismiss reply",
|
||||||
"REPLYING_TO": "Replying to:",
|
"REPLYING_TO": "Replying to:",
|
||||||
"TIP_FORMAT_ICON": "Show rich text editor",
|
"TIP_FORMAT_ICON": "Show rich text editor",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user