Compare commits

..
Author SHA1 Message Date
Muhsin Keloth d61eed7169 Add more changes 2023-07-05 15:19:20 +05:30
Muhsin Keloth 464e0a5d35 Add more changes 2023-07-05 13:49:36 +05:30
Muhsin Keloth 146bdd2f83 Update command bar to accomdate more AI options 2023-07-04 15:28:42 +05:30
218 changed files with 4591 additions and 6355 deletions
+18 -18
View File
@@ -73,7 +73,7 @@ jobs:
- run:
name: yarn
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
command: yarn install --cache-folder ~/.cache/yarn
# Store yarn / webpacker cache
- save_cache:
@@ -104,8 +104,9 @@ jobs:
fi
curl -L https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/6.3.0/openapi-generator-cli-6.3.0.jar > ~/tmp/openapi-generator-cli-6.3.0.jar
java -jar ~/tmp/openapi-generator-cli-6.3.0.jar validate -i swagger/swagger.json
# Database setup
- run: yarn install --check-files
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
@@ -116,7 +117,7 @@ jobs:
- run:
name: Rubocop
command: bundle exec rubocop
# - run:
# name: Brakeman
# command: bundle exec brakeman
@@ -125,21 +126,6 @@ jobs:
name: eslint
command: yarn run eslint
# Run frontend tests
- run:
name: Run frontend tests
command: |
mkdir -p ~/tmp/test-results/frontend_specs
~/tmp/cc-test-reporter before-build
TESTFILES=$(circleci tests glob **/specs/*.spec.js | circleci tests split --split-by=timings)
yarn test:coverage --profile 10 \
--out ~/tmp/test-results/yarn.xml \
-- ${TESTFILES}
- run:
name: Code Climate Test Coverage
command: |
~/tmp/cc-test-reporter format-coverage -t lcov -o "coverage/codeclimate.frontend_$CIRCLE_NODE_INDEX.json"
# Run rails tests
- run:
name: Run backend tests
@@ -159,6 +145,20 @@ jobs:
command: |
~/tmp/cc-test-reporter format-coverage -t simplecov -o "coverage/codeclimate.$CIRCLE_NODE_INDEX.json"
- run:
name: Run frontend tests
command: |
mkdir -p ~/tmp/test-results/frontend_specs
~/tmp/cc-test-reporter before-build
TESTFILES=$(circleci tests glob **/specs/*.spec.js | circleci tests split --split-by=timings)
yarn test:coverage --profile 10 \
--out ~/tmp/test-results/yarn.xml \
-- ${TESTFILES}
- run:
name: Code Climate Test Coverage
command: |
~/tmp/cc-test-reporter format-coverage -t lcov -o "coverage/codeclimate.frontend_$CIRCLE_NODE_INDEX.json"
- persist_to_workspace:
root: coverage
paths:
-3
View File
@@ -230,6 +230,3 @@ AZURE_APP_SECRET=
## Change these values to fine tune performance
# control the concurrency setting of sidekiq
# SIDEKIQ_CONCURRENCY=10
# Sentiment analysis model file path
SENTIMENT_FILE_PATH=
-3
View File
@@ -165,9 +165,6 @@ gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'omniauth-rails_csrf_protection', '~> 1.0'
# Sentiment analysis
gem 'informers'
### Gems required only in specific deployment environments ###
##############################################################
+4 -19
View File
@@ -145,7 +145,6 @@ GEM
statsd-ruby (~> 1.1)
bcrypt (3.1.18)
bindex (0.8.1)
blingfire (0.1.8)
bootsnap (1.16.0)
msgpack (~> 1.2)
brakeman (5.4.1)
@@ -362,10 +361,6 @@ GEM
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
informers (0.2.0)
blingfire (>= 0.1.7)
numo-narray
onnxruntime (>= 0.5.1)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
@@ -446,7 +441,7 @@ GEM
mini_magick (4.12.0)
mini_mime (1.1.2)
mini_portile2 (2.8.2)
minitest (5.18.1)
minitest (5.18.0)
mock_redis (0.36.0)
ruby2_keywords
msgpack (1.7.0)
@@ -455,7 +450,7 @@ GEM
multipart-post (2.3.0)
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
net-imap (0.3.6)
net-imap (0.3.4)
date
net-protocol
net-pop (0.1.2)
@@ -479,7 +474,6 @@ GEM
racc (~> 1.4)
nokogiri (1.15.2-x86_64-linux)
racc (~> 1.4)
numo-narray (0.9.2.1)
oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1)
snaky_hash (~> 2.0)
@@ -508,14 +502,6 @@ GEM
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
onnxruntime (0.7.6)
ffi
onnxruntime (0.7.6-arm64-darwin)
ffi
onnxruntime (0.7.6-x86_64-darwin)
ffi
onnxruntime (0.7.6-x86_64-linux)
ffi
openssl (3.1.0)
orm_adapter (0.5.0)
os (1.1.4)
@@ -539,7 +525,7 @@ GEM
pundit (2.3.0)
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.7.1)
racc (1.7.0)
rack (2.2.7)
rack-attack (6.6.1)
rack (>= 1.0, < 3)
@@ -745,7 +731,7 @@ GEM
time_diff (0.3.0)
activesupport
i18n
timeout (0.4.0)
timeout (0.3.2)
trailblazer-option (0.1.2)
twilio-ruby (5.77.0)
faraday (>= 0.9, < 3.0)
@@ -860,7 +846,6 @@ DEPENDENCIES
hashie
html2text!
image_processing
informers
jbuilder
json_refs
json_schemer
@@ -18,11 +18,7 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
end
def authenticate_twilio
client = if permitted_params[:api_key_sid].present?
Twilio::REST::Client.new(permitted_params[:api_key_sid], permitted_params[:auth_token], permitted_params[:account_sid])
else
Twilio::REST::Client.new(permitted_params[:account_sid], permitted_params[:auth_token])
end
client = Twilio::REST::Client.new(permitted_params[:account_sid], permitted_params[:auth_token])
client.messages.list(limit: 1)
end
@@ -44,7 +40,6 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
@twilio_channel = Current.account.twilio_sms.create!(
account_sid: permitted_params[:account_sid],
auth_token: permitted_params[:auth_token],
api_key_sid: permitted_params[:api_key_sid],
messaging_service_sid: permitted_params[:messaging_service_sid].presence,
phone_number: phone_number,
medium: medium
@@ -57,7 +52,7 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
def permitted_params
params.require(:twilio_channel).permit(
:account_id, :messaging_service_sid, :phone_number, :account_sid, :auth_token, :name, :medium, :api_key_sid
:account_id, :messaging_service_sid, :phone_number, :account_sid, :auth_token, :name, :medium
)
end
end
@@ -124,7 +124,7 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
def inbox_attributes
[:name, :avatar, :greeting_enabled, :greeting_message, :enable_email_collect, :csat_survey_enabled,
:enable_auto_assignment, :working_hours_enabled, :out_of_office_message, :timezone, :allow_messages_after_resolved,
:lock_to_single_conversation, :portal_id, :sender_name_type, :business_name]
:lock_to_single_conversation, :portal_id]
end
def permitted_params(channel_attributes = [])
+1 -30
View File
@@ -3,8 +3,7 @@
v-if="!authUIFlags.isFetching"
id="app"
class="app-wrapper app-root"
:class="{ 'app-rtl--wrapper': isRTLView, dark: theme === 'dark' }"
:dir="isRTLView ? 'rtl' : 'ltr'"
:class="{ 'app-rtl--wrapper': isRTLView }"
>
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
<template v-if="!accountUIFlags.isFetchingItem && currentAccountId">
@@ -35,12 +34,10 @@ import PaymentPendingBanner from './components/app/PaymentPendingBanner.vue';
import vueActionCable from './helper/actionCable';
import WootSnackbarBox from './components/SnackbarContainer';
import rtlMixin from 'shared/mixins/rtlMixin';
import { LocalStorage } from 'shared/helpers/localStorage';
import {
registerSubscription,
verifyServiceWorkerExistence,
} from './helper/pushHelper';
import { LOCAL_STORAGE_KEYS } from './constants/localStorage';
export default {
name: 'App',
@@ -61,7 +58,6 @@ export default {
return {
showAddAccountModal: false,
latestChatwootVersion: null,
theme: 'light',
};
},
@@ -93,34 +89,9 @@ export default {
},
},
mounted() {
this.initializeColorTheme();
this.listenToThemeChanges();
this.setLocale(window.chatwootConfig.selectedLocale);
},
methods: {
initializeColorTheme() {
this.setColorTheme(
window.matchMedia('(prefers-color-scheme: dark)').matches
);
},
setColorTheme(isOSOnDarkMode) {
const selectedColorScheme =
LocalStorage.get(LOCAL_STORAGE_KEYS.COLOR_SCHEME) || 'light';
if (
(selectedColorScheme === 'auto' && isOSOnDarkMode) ||
selectedColorScheme === 'dark'
) {
this.theme = 'dark';
document.body.classList.add('dark');
} else {
this.theme = 'light ';
document.body.classList.remove('dark');
}
},
listenToThemeChanges() {
const mql = window.matchMedia('(prefers-color-scheme: dark)');
mql.onchange = e => this.setColorTheme(e.matches);
},
setLocale(locale) {
this.$root.$i18n.locale = locale;
},
@@ -18,10 +18,6 @@ class CacheEnabledApiClient extends ApiClient {
return this.getFromCache();
}
return this.getFromNetwork();
}
getFromNetwork() {
return axios.get(this.url);
}
@@ -36,12 +32,7 @@ class CacheEnabledApiClient extends ApiClient {
}
async getFromCache() {
try {
// IDB is not supported in Firefox private mode: https://bugzilla.mozilla.org/show_bug.cgi?id=781982
await this.dataManager.initDb();
} catch {
return this.getFromNetwork();
}
await this.dataManager.initDb();
const { data } = await axios.get(
`/api/v1/accounts/${this.accountIdFromRoute}/cache_keys`
@@ -64,22 +55,16 @@ class CacheEnabledApiClient extends ApiClient {
}
async refetchAndCommit(newKey = null) {
const response = await this.getFromNetwork();
await this.dataManager.initDb();
const response = await axios.get(this.url);
this.dataManager.replace({
modelName: this.cacheModelName,
data: this.extractDataFromResponse(response),
});
try {
await this.dataManager.initDb();
this.dataManager.replace({
modelName: this.cacheModelName,
data: this.extractDataFromResponse(response),
});
await this.dataManager.setCacheKeys({
[this.cacheModelName]: newKey,
});
} catch {
// Ignore error
}
await this.dataManager.setCacheKeys({
[this.cacheModelName]: newKey,
});
return response;
}
@@ -2,62 +2,18 @@
import ApiClient from '../ApiClient';
/**
* Represents the data object for a OpenAI hook.
* @typedef {Object} ConversationMessageData
* @property {string} [tone] - The tone of the message.
* @property {string} [content] - The content of the message.
* @property {string} [conversation_display_id] - The display ID of the conversation (optional).
*/
/**
* A client for the OpenAI API.
* @extends ApiClient
*/
class OpenAIAPI extends ApiClient {
/**
* Creates a new OpenAIAPI instance.
*/
constructor() {
super('integrations', { accountScoped: true });
/**
* The conversation events supported by the API.
* @type {string[]}
*/
this.conversation_events = [
'summarize',
'reply_suggestion',
'label_suggestion',
];
/**
* The message events supported by the API.
* @type {string[]}
*/
this.message_events = ['rephrase'];
}
/**
* Processes an event using the OpenAI API.
* @param {Object} options - The options for the event.
* @param {string} [options.type='rephrase'] - The type of event to process.
* @param {string} [options.content] - The content of the event.
* @param {string} [options.tone] - The tone of the event.
* @param {string} [options.conversationId] - The ID of the conversation to process the event for.
* @param {string} options.hookId - The ID of the hook to use for processing the event.
* @returns {Promise} A promise that resolves with the result of the event processing.
*/
processEvent({ type = 'rephrase', content, tone, conversationId, hookId }) {
/**
* @type {ConversationMessageData}
*/
let data = {
tone,
content,
};
if (this.conversation_events.includes(type)) {
if (type === 'reply_suggestion' || type === 'summarize') {
data = {
conversation_display_id: conversationId,
};
@@ -20,6 +20,15 @@ select {
margin-left: var(--space-normal);
}
.tooltip {
background-color: var(--black-transparent);
border-radius: $space-smaller;
font-size: $font-size-mini;
max-width: var(--space-giga);
padding: $space-smaller $space-small;
z-index: 999;
}
code {
border: 0;
font-family: 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas',
@@ -1,5 +1,5 @@
.bg-light {
@apply bg-slate-25 dark:bg-slate-800;
@include background-light;
}
.flex-center {
@@ -22,43 +22,43 @@ $spinner-before-border-color: rgba(255, 255, 255, 0.7);
}
@mixin border-normal() {
@apply border border-slate-50 dark:border-slate-700;
border: 1px solid $color-border;
}
@mixin border-normal-left() {
@apply border-l border-slate-50 dark:border-slate-700;
border-left: 1px solid $color-border;
}
@mixin border-normal-top() {
@apply border-t border-slate-50 dark:border-slate-700;
border-top: 1px solid $color-border;
}
@mixin border-normal-right() {
@apply border-r border-slate-50 dark:border-slate-700;
border-right: 1px solid $color-border;
}
@mixin border-normal-bottom() {
@apply border-b border-slate-50 dark:border-slate-700;
border-bottom: 1px solid $color-border;
}
@mixin border-light() {
@apply border border-slate-25 dark:border-slate-700;
border: 1px solid $color-border-light;
}
@mixin border-light-left() {
@apply border-l border-slate-25 dark:border-slate-700;
border-left: 1px solid $color-border-light;
}
@mixin border-light-top() {
@apply border-t border-slate-25 dark:border-slate-700;
border-top: 1px solid $color-border-light;
}
@mixin border-light-right() {
@apply border-r border-slate-25 dark:border-slate-700;
border-right: 1px solid $color-border-light;
}
@mixin border-light-bottom() {
@apply border-b border-slate-25 dark:border-slate-700;
border-bottom: 1px solid $color-border-light;
}
// background
@@ -67,11 +67,11 @@ $spinner-before-border-color: rgba(255, 255, 255, 0.7);
}
@mixin background-light() {
@apply bg-slate-50 dark:bg-slate-800;
background: $color-background-light;
}
@mixin background-white() {
@apply bg-white dark:bg-slate-900;
background: $color-white;
}
// input form
@@ -3,6 +3,9 @@
// Primary sidebar
.primary--sidebar {
border-left: 1px solid var(--s-50);
border-right: 0;
.options-menu.dropdown-pane {
right: var(--space-smaller);
@@ -17,6 +20,63 @@
}
}
// Secondary sidebar
.secondary-sidebar {
.secondary-menu {
border-left: 1px solid var(--s-50);
border-right: 0;
.nested.vertical.menu {
.badge--icon {
margin-left: var(--space-smaller);
margin-right: unset;
}
.menu-label {
text-align: right;
}
}
.secondary-menu--icon {
margin-left: var(--space-smaller);
margin-right: unset;
}
.account-context--group .account-context--switch-group {
--overlay-shadow: linear-gradient(
to left,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 1) 50%
);
background-image: var(--overlay-shadow);
}
// Help center sidebar
.sidebar-header--wrap .header-title--wrap {
margin-left: unset;
margin-right: var(--space-small);
}
}
}
// Woot button
.button {
.icon--emoji + .button__content {
padding-left: 0;
padding-right: var(--space-small);
}
.icon--font + .button__content {
padding-left: 0;
padding-right: var(--space-small);
}
.icon + .button__content {
padding-left: 0;
padding-right: var(--space-small);
}
}
// Settings header
.settings-header {
.header--icon {
@@ -43,13 +43,17 @@
}
.border-right {
@apply border-r border-slate-50 dark:border-slate-700;
border-right: 1px solid var(--color-border);
}
.border-left {
border-left: 1px solid var(--color-border);
}
.bg-white {
background-color: var(--white);
}
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
@@ -1,7 +1,3 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@import 'shared/assets/fonts/plus-jakarta';
@import 'shared/assets/stylesheets/animations';
@import 'shared/assets/stylesheets/colors';
@@ -71,8 +67,3 @@
@import 'plugins/dropdown';
@import '~shared/assets/stylesheets/ionicons';
@import 'utility-helpers';
.tooltip {
@apply bg-slate-900 text-white py-1 px-2 z-40 text-xs rounded-md dark:bg-slate-200 dark:text-slate-900;
}
@@ -4,8 +4,10 @@
// Conversation header - Light BG
.settings-header {
@include background-white;
@include flex;
@include flex-align($x: justify, $y: middle);
border-bottom: 1px solid var(--s-50);
height: $header-height;
min-height: $header-height;
padding: $space-small $space-normal;
@@ -25,7 +27,7 @@
.wizard-box {
.item {
// @include background-light;
@include background-light;
cursor: pointer;
padding: $space-normal $space-normal $space-normal $space-medium;
@@ -126,8 +128,8 @@
}
.wizard-body {
@include border-light;
@include background-white;
@include border-light;
@include full-height();
padding: $space-medium;
@@ -1,201 +1,207 @@
$default-button-height: 2.5rem;
.button {
@apply items-center inline-flex h-10 mb-0;
align-items: center;
display: inline-flex;
height: $default-button-height;
margin-bottom: 0;
.button__content {
@apply w-full;
img,
svg {
@apply inline-block;
}
width: 100%;
}
.spinner {
@apply px-2 py-0;
padding: 0 var(--space-small);
}
.icon--emoji + .button__content {
@apply pl-2 rtl:pr-2 rtl:pl-0;
.icon--emoji+.button__content {
padding-left: var(--space-small);
}
.icon--font + .button__content {
@apply pl-2 rtl:pr-2 rtl:pl-0;
.icon--font+.button__content {
padding-left: var(--space-small);
}
// @TODDO - Remove after moving all buttons to woot-button
.icon + .button__content {
@apply pl-2 w-auto rtl:pr-2 rtl:pl-0;
.icon+.button__content {
padding-left: var(--space-small);
width: auto;
}
&.expanded {
@apply flex justify-center text-center;
display: flex;
justify-content: center;
}
&.round {
@apply rounded-full;
border-radius: $space-larger;
}
// @TODO Use with link
&.compact {
@apply pb-0 pt-0;
padding-bottom: 0;
padding-top: 0;
}
&.hollow {
@apply border border-slate-200 text-woot-700 dark:text-woot-100 hover:bg-woot-50 dark:hover:bg-woot-900;
border-color: var(--s-200);
color: var(--w-700);
&.secondary {
@apply text-slate-700 dark:text-slate-100 hover:bg-slate-50 dark:hover:bg-slate-700;
border-color: var(--s-200);
color: var(--s-700);
}
&.success {
@apply text-green-700 dark:text-green-100 hover:bg-green-50 dark:hover:bg-green-800;
border-color: var(--s-200);
color: var(--g-700);
}
&.alert {
@apply text-red-700 dark:text-red-100 hover:bg-red-50 dark:hover:bg-red-800;
border-color: var(--s-200);
color: var(--r-700);
}
&.warning {
@apply text-yellow-700 dark:text-yellow-100 hover:bg-yellow-50 dark:hover:bg-yellow-800;
border-color: var(--s-200);
color: var(--y-700);
}
&:hover {
@apply bg-slate-75 dark:bg-slate-900 border-slate-100 dark:border-slate-700;
background: var(--s-75);
border-color: var(--s-100);
&.secondary {
@apply border-slate-100 dark:border-slate-700;
border-color: var(--s-100);
}
&.success {
@apply border-slate-100 dark:border-slate-700;
border-color: var(--s-100);
}
&.alert {
@apply border-slate-100 dark:border-slate-700;
border-color: var(--s-100);
}
&.warning {
@apply border-slate-100 dark:border-slate-700;
border-color: var(--s-100);
}
}
}
// Smooth style
&.smooth {
@apply bg-woot-50 dark:bg-woot-800 text-woot-700 dark:text-woot-100 hover:text-woot-700 dark:hover:text-woot-700 hover:bg-woot-100 dark:hover:bg-woot-900;
@include button-style(var(--w-50), var(--w-100), var(--w-700));
&.secondary {
@apply bg-slate-50 dark:bg-slate-700 text-slate-700 dark:text-slate-100 hover:bg-slate-100 dark:hover:bg-slate-800;
@include button-style(var(--s-50), var(--s-100), var(--s-700));
}
&.success {
@apply bg-green-50 dark:bg-green-700 text-green-700 dark:text-green-100 hover:bg-green-100 dark:hover:bg-green-800;
@include button-style(var(--g-50), var(--g-100), var(--g-700));
}
&.alert {
@apply bg-red-50 dark:bg-red-700 dark:bg-opacity-50 text-red-700 dark:text-red-100 hover:bg-red-100 dark:hover:bg-red-800 dark:hover:bg-opacity-30;
@include button-style(var(--r-50), var(--r-100), var(--r-700));
}
&.warning {
@apply bg-yellow-100 dark:bg-yellow-100 text-yellow-700 dark:text-yellow-100 hover:bg-yellow-200 dark:hover:bg-yellow-200;
@include button-style(var(--y-100), var(--y-200), var(--y-700));
}
}
&.clear {
@apply text-woot-500 dark:text-woot-500;
color: var(--w-700);
&.secondary {
@apply text-slate-700 dark:text-slate-100;
color: var(--s-700);
}
&.success {
@apply text-green-700 dark:text-green-100;
color: var(--g-700);
}
&.alert {
@apply text-red-700 dark:text-red-100;
color: var(--r-700);
}
&.warning {
@apply text-yellow-700 dark:text-yellow-100;
color: var(--y-700);
}
&:hover {
@apply hover:bg-woot-50 dark:hover:bg-woot-300;
background: var(--w-50);
&.secondary {
@apply hover:bg-slate-50 dark:hover:bg-slate-700;
background: var(--s-50);
}
&.success {
@apply hover:bg-green-50 dark:hover:bg-green-800;
background: var(--g-50);
}
&.alert {
@apply hover:bg-red-50 dark:hover:bg-red-800;
background: var(--r-50);
}
&.warning {
@apply hover:bg-yellow-50 dark:hover:bg-yellow-800;
}
}
&:active {
&.secondary {
@apply active:bg-slate-100 dark:active:bg-slate-900;
}
}
&:focus {
&.secondary {
@apply focus:bg-slate-50 dark:focus:bg-slate-700;
background: var(--y-50);
}
}
}
// Sizes
&.tiny {
@apply h-6;
height: var(--space-medium);
.icon + .button__content {
@apply pl-1 rtl:pr-1 rtl:pl-0;
.icon+.button__content {
padding-left: var(--space-micro);
}
}
&.small {
@apply h-8 pb-1 pt-1;
height: var(--space-large);
padding-bottom: var(--space-smaller);
padding-top: var(--space-smaller);
.icon + .button__content {
@apply pl-1 rtl:pr-1 rtl:pl-0;
.icon+.button__content {
padding-left: var(--space-smaller);
}
}
&.large {
@apply h-12;
height: var(--space-larger);
}
&.button--only-icon {
@apply justify-center pl-0 pr-0 w-10;
justify-content: center;
padding-left: 0;
padding-right: 0;
width: $default-button-height;
&.tiny {
@apply w-6;
width: var(--space-medium);
}
&.small {
@apply w-8;
width: var(--space-large);
}
&.large {
@apply w-12;
width: var(--space-larger);
}
}
&.link {
@apply h-auto m-0 p-0;
height: auto;
margin: 0;
padding: 0;
&:hover {
@apply underline;
text-decoration: underline;
}
}
}
@@ -59,6 +59,11 @@ $resolve-button-width: 8.25rem;
flex-direction: row;
flex-grow: 1;
justify-content: flex-end;
margin-top: var(--space-small);
@include breakpoint(medium up) {
margin-top: 0;
}
&.has-open-sidebar {
justify-content: flex-end;
@@ -12,7 +12,9 @@
.conversation {
@include flex;
@include flex-shrink;
border-bottom: 1px solid transparent;
border-left: var(--space-micro) solid transparent;
border-top: 1px solid transparent;
cursor: pointer;
padding: 0 var(--space-normal);
position: relative;
@@ -20,11 +22,35 @@
&.active {
animation: left-shift-animation 0.25s $swift-ease-out-function;
background: var(--color-background);
border-bottom-color: var(--color-border-light);
border-left-color: var(--color-woot);
border-top-color: var(--color-border-light);
.conversation--details {
border-top-color: transparent;
}
+ .conversation .conversation--details {
border-top-color: transparent;
}
}
&:first-child {
.conversation--details {
border-top-color: transparent;
}
}
&:last-child {
.conversation--details {
border-bottom-color: var(--color-border-light);
}
}
.conversation--details {
@include border-light-bottom;
@include border-light-top;
border-bottom-color: transparent;
padding: var(--space-slab) 0;
}
@@ -56,11 +82,6 @@
text-overflow: ellipsis;
white-space: nowrap;
width: 16.875rem;
img,
svg {
display: inline-block;
}
}
.conversation--meta {
@@ -390,42 +390,3 @@
margin-bottom: 0;
}
}
.conversation-sidebar-wrap {
flex: 0 0;
flex-basis: 100%;
height: auto;
overflow: auto;
z-index: var(--z-index-low);
@include breakpoint(medium up) {
flex-basis: 17.5rem;
}
@include breakpoint(large up) {
flex-basis: 18.75rem;
}
@include breakpoint(xlarge up) {
flex-basis: 19.375rem;
}
@include breakpoint(xxlarge up) {
flex-basis: 20.625rem;
}
@include breakpoint(xxxlarge up) {
flex-basis: 25rem;
}
}
.activity-wrap {
.message-text--metadata {
.time {
@include breakpoint(xlarge up) {
margin-left: var(--space-small);
}
}
}
}
@@ -61,7 +61,3 @@ input {
border-color: var(--r-400);
}
}
label {
@apply dark:text-slate-300;
}
@@ -34,6 +34,7 @@
.modal-container {
@include normal-shadow;
background-color: $color-white;
border-radius: $space-smaller;
max-height: 100%;
overflow: auto;
@@ -1,6 +1,6 @@
.reply-box {
transition: box-shadow 0.35s $swift-ease-out-function,
height 2s $swift-ease-out-function;
height 2s $swift-ease-out-function;
&.is-focused {
box-shadow: var(--shadow);
@@ -1,3 +1,18 @@
.loading-state {
padding: $space-jumbo $space-smaller;
.message {
color: $color-gray;
display: block;
text-align: center;
width: 100%;
}
.spinner {
float: none;
top: -$space-smaller;
}
}
// EMPTY STATES
.empty-state {
@@ -4,13 +4,15 @@ table {
thead {
th {
@apply font-medium text-left uppercase text-slate-900 dark:text-slate-200;
font-weight: var(--font-weight-bold);
text-align: left;
text-transform: uppercase;
}
}
tbody {
tr {
@apply border-b border-slate-50 dark:border-slate-700;
border-bottom: 1px solid var(--color-border-light);
}
td {
@@ -126,28 +126,27 @@
class="conversations-list"
:class="{ 'is-context-menu-open': isContextMenuOpen }"
>
<div>
<conversation-card
v-for="chat in conversationList"
:key="chat.id"
:active-label="label"
:team-id="teamId"
:folders-id="foldersId"
: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>
<conversation-card
v-for="chat in conversationList"
:key="chat.id"
:active-label="label"
:team-id="teamId"
:folders-id="foldersId"
: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 v-if="chatListLoading" class="text-center">
<span class="spinner" />
</div>
@@ -156,7 +155,6 @@
v-if="!hasCurrentPageEndReached && !chatListLoading"
variant="clear"
size="expanded"
class="text-center"
@click="loadMoreConversations"
>
{{ $t('CHAT_LIST.LOAD_MORE_CONVERSATIONS') }}
@@ -958,6 +956,8 @@ export default {
</script>
<style scoped lang="scss">
@import '~dashboard/assets/scss/woot';
.spinner {
margin-top: var(--space-normal);
margin-bottom: var(--space-normal);
@@ -52,8 +52,7 @@ export default {
},
computed: {
modalContainerClassName() {
let className =
'modal-container bg-white dark:bg-slate-800 skip-context-menu';
let className = 'modal-container skip-context-menu';
if (this.fullWidth) {
return `${className} modal-container--full-width`;
}
@@ -1,17 +1,10 @@
<template>
<div class="column page-top-bar">
<img v-if="headerImage" :src="headerImage" alt="No image" />
<h2
ref="modalHeaderTitle"
class="text-slate-800 text-lg dark:text-slate-100"
>
<h2 class="page-sub-title">
{{ headerTitle }}
</h2>
<p
v-if="headerContent"
ref="modalHeaderContent"
class="small-12 column wrap-content"
>
<p v-if="headerContent" class="small-12 column wrap-content">
{{ headerContent }}
<span v-if="headerContentValue" class="content-value">
{{ headerContentValue }}
@@ -1,11 +1,11 @@
<template>
<div class="row settings--section" :class="{ 'border-bottom': showBorder }">
<div class="row settings--section">
<div class="medium-4 small-12 title--section">
<p v-if="title" class="sub-block-title">
<p class="sub-block-title">
{{ title }}
</p>
<p class="sub-head">
<slot v-if="subTitle" name="subTitle">
<slot name="subTitle">
{{ subTitle }}
</slot>
</p>
@@ -25,15 +25,11 @@ export default {
props: {
title: {
type: String,
default: '',
required: true,
},
subTitle: {
type: String,
default: '',
},
showBorder: {
type: Boolean,
default: true,
required: true,
},
note: {
type: String,
@@ -47,13 +43,9 @@ export default {
@import '~dashboard/assets/scss/variables';
.settings--section {
border-bottom: 1px solid $color-border;
display: flex;
padding: 0 $space-normal $space-normal 0;
&.border-bottom {
padding-top: $space-normal;
border-bottom: 1px solid $color-border;
}
padding: $space-normal $space-normal $space-normal 0;
.sub-block-title {
color: $color-woot;
@@ -3,8 +3,8 @@
size="small"
variant="clear"
color-scheme="secondary"
class="-ml-3 text-black-900 dark:text-slate-300"
icon="list"
class="toggle-sidebar"
@click="onMenuItemClick"
/>
</template>
@@ -20,3 +20,8 @@ export default {
},
};
</script>
<style scoped lang="scss">
.toggle-sidebar {
margin-left: var(--space-minus-small);
}
</style>
@@ -4,7 +4,7 @@
<woot-dropdown-item
v-for="status in availabilityStatuses"
:key="status.value"
class="flex items-baseline"
class="status-items"
>
<woot-button
size="small"
@@ -18,25 +18,23 @@
</woot-button>
</woot-dropdown-item>
<woot-dropdown-divider />
<woot-dropdown-item class="m-0 flex items-center justify-between p-2">
<div class="flex items-center">
<woot-dropdown-item class="auto-offline--toggle">
<div class="info-wrap">
<fluent-icon
v-tooltip.right-start="$t('SIDEBAR.SET_AUTO_OFFLINE.INFO_TEXT')"
icon="info"
size="14"
class="mt-px"
class="info-icon"
/>
<span
class="my-0 mx-1 text-xs font-medium text-slate-600 dark:text-slate-100"
>
<span class="auto-offline--text">
{{ $t('SIDEBAR.SET_AUTO_OFFLINE.TEXT') }}
</span>
</div>
<woot-switch
size="small"
class="mt-px mx-1 mb-0"
class="auto-offline--switch"
:value="currentUserAutoOffline"
@input="updateAutoOffline"
/>
@@ -140,3 +138,71 @@ export default {
},
};
</script>
<style lang="scss">
@import '~dashboard/assets/scss/variables';
.status {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: var(--space-micro) var(--space-smaller);
}
.status-view {
display: flex;
align-items: baseline;
& &--title {
color: var(--b-600);
font-size: var(--font-size-small);
font-weight: var(--font-weight-medium);
margin-left: var(--space-small);
&:first-letter {
text-transform: capitalize;
}
}
}
.status-change {
.dropdown-pane {
top: -132px;
right: var(--space-normal);
}
.status-items {
display: flex;
align-items: baseline;
}
}
.auto-offline--toggle {
align-items: center;
display: flex;
justify-content: space-between;
padding: var(--space-smaller);
margin: 0;
.info-wrap {
display: flex;
align-items: center;
}
.info-icon {
margin-top: -1px;
}
.auto-offline--switch {
margin: -1px var(--space-micro) 0;
}
.auto-offline--text {
margin: 0 var(--space-smaller);
font-size: var(--font-size-mini);
font-weight: var(--font-weight-medium);
color: var(--s-700);
}
}
</style>
@@ -1,5 +1,5 @@
<template>
<aside class="h-full flex">
<aside class="woot-sidebar">
<primary-sidebar
:logo-source="globalConfig.logoThumbnail"
:installation-name="globalConfig.installationName"
@@ -11,21 +11,22 @@
@key-shortcut-modal="toggleKeyShortcutModal"
@open-notification-panel="openNotificationPanel"
/>
<secondary-sidebar
v-if="showSecondarySidebar"
:class="sidebarClassName"
:account-id="accountId"
:inboxes="inboxes"
:labels="labels"
:teams="teams"
:custom-views="customViews"
:menu-config="activeSecondaryMenu"
:current-role="currentRole"
:is-on-chatwoot-cloud="isOnChatwootCloud"
@add-label="showAddLabelPopup"
@toggle-accounts="toggleAccountModal"
/>
<div class="secondary-sidebar">
<secondary-sidebar
v-if="showSecondarySidebar"
:class="sidebarClassName"
:account-id="accountId"
:inboxes="inboxes"
:labels="labels"
:teams="teams"
:custom-views="customViews"
:menu-config="activeSecondaryMenu"
:current-role="currentRole"
:is-on-chatwoot-cloud="isOnChatwootCloud"
@add-label="showAddLabelPopup"
@toggle-accounts="toggleAccountModal"
/>
</div>
</aside>
</template>
@@ -213,3 +214,87 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.woot-sidebar {
background: var(--white);
display: flex;
min-height: 0;
height: 100%;
width: fit-content;
}
</style>
<style lang="scss">
@import '~dashboard/assets/scss/variables';
.account-selector--modal {
.modal-container {
width: 25rem;
}
}
.secondary-sidebar {
overflow-y: auto;
height: 100%;
}
.account-selector {
cursor: pointer;
padding: $space-small $space-large;
.selected--account {
margin-top: -$space-smaller;
& + .account--details {
padding-left: $space-normal - $space-micro;
}
}
.account--details {
padding-left: $space-large + $space-smaller;
}
&:last-child {
margin-bottom: $space-large;
}
a {
align-items: center;
cursor: pointer;
display: flex;
.account--name {
cursor: pointer;
font-size: $font-size-medium;
font-weight: $font-weight-medium;
line-height: 1;
}
.account--role {
cursor: pointer;
font-size: $font-size-mini;
text-transform: capitalize;
}
}
}
.app-context-menu {
align-items: center;
cursor: pointer;
display: flex;
flex-direction: row;
height: 3.75rem;
}
.current-user--options {
font-size: $font-size-big;
margin-bottom: auto;
margin-left: auto;
margin-top: auto;
}
.secondary-menu .nested.vertical.menu {
margin-left: var(--space-small);
}
</style>
@@ -1,29 +1,25 @@
<template>
<div
v-if="showShowCurrentAccountContext"
class="rounded-md text-xs py-2 px-2 mt-2 relative border border-slate-50 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-700 cursor-pointer"
class="account-context--group"
@mouseover="setShowSwitch"
@mouseleave="resetShowSwitch"
>
{{ $t('SIDEBAR.CURRENTLY_VIEWING_ACCOUNT') }}
<p class="text-ellipsis font-medium mb-0">
<p class="account-context--name text-ellipsis">
{{ account.name }}
</p>
<transition name="fade">
<div
v-if="showSwitchButton"
class="bg-gradient-to-r rtl:bg-gradient-to-l from-transparent via-white dark:via-slate-700 to-white dark:to-slate-700 flex items-center h-full justify-end absolute top-0 right-0 w-full"
>
<div class="my-0 mx-2">
<woot-button
variant="clear"
size="tiny"
icon="arrow-swap"
@click="$emit('toggle-accounts')"
>
{{ $t('SIDEBAR.SWITCH') }}
</woot-button>
</div>
<div v-if="showSwitchButton" class="account-context--switch-group">
<woot-button
variant="clear"
size="tiny"
icon="arrow-swap"
class="switch-button"
@click="$emit('toggle-accounts')"
>
{{ $t('SIDEBAR.SWITCH') }}
</woot-button>
</div>
</transition>
</div>
@@ -55,6 +51,51 @@ export default {
};
</script>
<style scoped lang="scss">
.account-context--group {
border-radius: var(--border-radius-normal);
border: 1px solid var(--color-border);
font-size: var(--font-size-mini);
padding: var(--space-small);
margin: var(--space-small) var(--space-small) 0 var(--space-small);
position: relative;
&:hover {
background: var(--b-100);
}
.account-context--name {
font-weight: var(--font-weight-medium);
margin-bottom: 0;
}
}
.switch-button {
margin: 0 var(--space-small);
}
.account-context--switch-group {
--overlay-shadow: linear-gradient(
to right,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 1) 50%
);
align-items: center;
background-image: var(--overlay-shadow);
border-top-left-radius: 0;
border-top-right-radius: var(--border-radius-normal);
border-bottom-left-radius: 0;
border-bottom-right-radius: var(--border-radius-normal);
display: flex;
height: 100%;
justify-content: flex-end;
opacity: 1;
position: absolute;
right: 0;
top: 0;
width: 100%;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 300ms ease;
@@ -2,39 +2,31 @@
<woot-modal
:show="showAccountModal"
:on-close="() => $emit('close-account-modal')"
class="account-selector--modal"
>
<woot-modal-header
:header-title="$t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS')"
:header-content="$t('SIDEBAR_ITEMS.SELECTOR_SUBTITLE')"
/>
<div class="px-8 pt-4 pb-8">
<div class="account-selector--wrap">
<div
v-for="account in currentUser.accounts"
:id="`account-${account.id}`"
:key="account.id"
class="pt-0 pb-0"
class="account-selector"
>
<button
class="flex justify-between items-center expanded clear link cursor-pointer px-4 py-3 w-full rounded-lg hover:underline hover:bg-slate-25 dark:hover:bg-slate-900"
class="button expanded clear link"
@click="onChangeAccount(account.id)"
>
<span class="w-full">
<label :for="account.name" class="text-left rtl:text-right">
<div
class="text-slate-700 text-lg dark:text-slate-100 font-medium hover:underline-offset-4 leading-5"
>
{{ account.name }}
</div>
<div
class="text-slate-500 text-xs dark:text-slate-500 font-medium hover:underline-offset-4"
>
{{ account.role }}
</div>
<span class="button__content">
<label :for="account.name" class="account-details--wrap">
<div class="account--name">{{ account.name }}</div>
<div class="account--role">{{ account.role }}</div>
</label>
</span>
<fluent-icon
v-show="account.id === accountId"
class="text-slate-800 dark:text-slate-100"
class="selected--account"
icon="checkmark-circle"
type="solid"
size="24"
@@ -82,3 +74,32 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.account-selector--wrap {
margin-top: var(--space-normal);
}
.account-selector {
padding-top: 0;
padding-bottom: 0;
.button {
display: flex;
justify-content: space-between;
padding: var(--space-one) var(--space-normal);
.account-details--wrap {
text-align: left;
.account--name {
cursor: pointer;
font-size: var(--font-size-medium);
font-weight: var(--font-weight-medium);
line-height: 1;
}
.account--role {
cursor: pointer;
font-size: var(--font-size-mini);
text-transform: capitalize;
}
}
}
}
</style>
@@ -1,5 +1,5 @@
<template>
<div class="w-8 h-8">
<div class="logo">
<router-link :to="dashboardPath" replace>
<img :src="source" :alt="name" />
</router-link>
@@ -30,3 +30,17 @@ export default {
},
};
</script>
<style lang="scss" scoped>
$logo-size: 32px;
.logo {
padding: var(--space-normal);
img {
width: $logo-size;
height: $logo-size;
object-fit: cover;
object-position: left center;
}
}
</style>
@@ -1,25 +1,15 @@
<template>
<div class="mb-4">
<button
class="text-slate-600 dark:text-slate-100 w-10 h-10 my-2 flex items-center justify-center rounded-lg hover:bg-slate-25 dark:hover:bg-slate-800 dark:hover:text-slate-100 hover:text-slate-600 relative"
:class="{
'bg-woot-50 dark:bg-slate-800 text-woot-500 hover:bg-woot-50': isNotificationPanelActive,
}"
<div class="notifications-link">
<woot-button
class-names="notifications-link--button"
variant="clear"
color-scheme="secondary"
:class="{ 'is-active': isNotificationPanelActive }"
@click="openNotificationPanel"
>
<fluent-icon
icon="alert"
:class="{
'text-woot-500': isNotificationPanelActive,
}"
/>
<span
v-if="unreadCount"
class="text-black-900 bg-yellow-300 absolute -top-0.5 -right-1 p-1 text-xxs min-w-[1rem] rounded-full"
>
{{ unreadCount }}
</span>
</button>
<fluent-icon icon="alert" />
<span v-if="unreadCount" class="badge warning">{{ unreadCount }}</span>
</woot-button>
</div>
</template>
<script>
@@ -53,3 +43,37 @@ export default {
},
};
</script>
<style scoped lang="scss">
.notifications-link {
margin-bottom: var(--space-small);
}
.badge {
position: absolute;
right: var(--space-minus-smaller);
top: var(--space-minus-smaller);
}
.notifications-link--button {
display: flex;
position: relative;
border-radius: var(--border-radius-large);
border: 1px solid transparent;
color: var(--s-600);
margin: var(--space-small) 0;
&:hover {
background: var(--w-50);
color: var(--s-600);
}
&:focus {
border-color: var(--w-500);
}
&.is-active {
background: var(--w-50);
color: var(--w-500);
}
}
</style>
@@ -3,10 +3,11 @@
<div
v-if="show"
v-on-clickaway="onClickAway"
class="left-3 rtl:left-auto rtl:right-3 bottom-16 w-64 absolute z-10 rounded-md shadow-xl bg-white dark:bg-slate-800 py-2 px-2 border border-slate-25 dark:border-slate-700"
:class="{ 'block visible': show }"
class="options-menu dropdown-pane"
:class="{ 'dropdown-pane--open': show }"
>
<availability-status />
<li class="divider" />
<woot-dropdown-menu>
<woot-dropdown-item v-if="showChangeAccountOption">
<woot-button
@@ -49,7 +50,7 @@
>
<a
:href="href"
class="button small clear secondary bg-white dark:bg-slate-800 h-8"
class="button small clear secondary custom-sidebar--button"
:class="{ 'is-active': isActive }"
@click="e => handleProfileSettingClick(e, navigate)"
>
@@ -63,7 +64,7 @@
<woot-dropdown-item v-if="currentUser.type === 'SuperAdmin'">
<a
href="/super_admin"
class="button small clear secondary bg-white dark:bg-slate-800 h-8"
class="button small clear secondary custom-sidebar--button"
target="_blank"
rel="noopener nofollow noreferrer"
@click="$emit('close')"
@@ -148,3 +149,17 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.options-menu.dropdown-pane {
left: var(--space-slab);
bottom: var(--space-larger);
min-width: var(--space-giga);
top: unset;
z-index: var(--z-index-low);
}
.custom-sidebar--button {
height: var(--space-large) !important;
padding: var(--space-smaller) 0.675rem !important;
}
</style>
@@ -1,14 +1,11 @@
<template>
<div
class="h-full w-16 bg-white dark:bg-slate-900 border-r border-slate-50 dark:border-slate-700 rtl:border-l rtl:border-r-0 flex justify-between flex-col"
>
<div class="flex flex-col items-center">
<logo
:source="logoSource"
:name="installationName"
:account-id="accountId"
class="m-4 mb-10"
/>
<div class="primary--sidebar">
<logo
:source="logoSource"
:name="installationName"
:account-id="accountId"
/>
<nav class="menu vertical">
<primary-nav-item
v-for="menuItem in menuItems"
:key="menuItem.toState"
@@ -17,8 +14,8 @@
:to="menuItem.toState"
:is-child-menu-active="menuItem.key === activeMenuItem"
/>
</div>
<div class="flex flex-col items-center justify-end pb-6">
</nav>
<div class="menu vertical user-menu">
<primary-nav-item
v-if="!isACustomBrandedInstance"
icon="book-open-globe"
@@ -104,3 +101,27 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.primary--sidebar {
display: flex;
flex-direction: column;
width: var(--space-jumbo);
border-right: 1px solid var(--s-50);
box-sizing: content-box;
height: 100%;
flex-shrink: 0;
}
.menu {
align-items: center;
margin-top: var(--space-medium);
}
.user-menu {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: flex-end;
margin-bottom: var(--space-normal);
}
</style>
@@ -3,28 +3,15 @@
<a
v-tooltip.right="$t(`SIDEBAR.${name}`)"
:href="href"
class="text-slate-600 dark:text-slate-100 w-10 h-10 my-2 flex items-center justify-center rounded-lg hover:bg-slate-25 dark:hover:bg-slate-800 dark:hover:text-slate-100 hover:text-slate-600 relative"
:class="{
'bg-woot-50 dark:bg-slate-800 text-woot-500 hover:bg-woot-50':
isActive || isChildMenuActive,
}"
class="button clear button--only-icon menu-item"
:class="{ 'is-active': isActive || isChildMenuActive }"
:rel="openInNewPage ? 'noopener noreferrer nofollow' : undefined"
:target="openInNewPage ? '_blank' : undefined"
@click="navigate"
>
<fluent-icon
:icon="icon"
:class="{
'text-woot-500': isActive || isChildMenuActive,
}"
/>
<span class="sr-only">{{ name }}</span>
<span
v-if="count"
class="text-black-900 bg-yellow-500 absolute -top-1 -right-1"
>
{{ count }}
</span>
<fluent-icon :icon="icon" />
<span class="show-for-sr">{{ name }}</span>
<span v-if="count" class="badge warning">{{ count }}</span>
</a>
</router-link>
</template>
@@ -58,3 +45,40 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.button {
margin: var(--space-small) 0;
}
.menu-item {
display: inline-flex;
position: relative;
border-radius: var(--border-radius-large);
border: 1px solid transparent;
color: var(--s-600);
&:hover {
background: var(--w-25);
color: var(--s-600);
}
&:focus {
border-color: var(--w-500);
}
&.is-active {
background: var(--w-50);
color: var(--w-500);
}
}
.icon {
font-size: var(--font-size-default);
}
.badge {
position: absolute;
right: var(--space-minus-smaller);
top: var(--space-minus-smaller);
}
</style>
@@ -1,14 +1,7 @@
<template>
<div
v-if="hasSecondaryMenu"
class="h-full overflow-auto w-48 flex flex-col bg-white dark:bg-slate-900 border-r dark:border-slate-700 rtl:border-r-0 rtl:border-l border-slate-50 text-sm px-2 pb-8"
>
<div v-if="hasSecondaryMenu" class="main-nav secondary-menu">
<account-context @toggle-accounts="toggleAccountModal" />
<transition-group
name="menu-list"
tag="ul"
class="pt-2 list-none ml-0 mb-0"
>
<transition-group name="menu-list" tag="ul" class="menu vertical">
<secondary-nav-item
v-for="menuItem in accessibleMenuItems"
:key="menuItem.toState"
@@ -256,3 +249,27 @@ export default {
},
};
</script>
<style lang="scss" scoped>
@import '~dashboard/assets/scss/woot';
.secondary-menu {
display: flex;
flex-direction: column;
background: var(--white);
border-right: 1px solid var(--s-50);
height: 100%;
width: 12.5rem;
flex-shrink: 0;
overflow-y: hidden;
position: unset;
&:hover {
overflow-y: hidden;
}
.menu {
padding: var(--space-small);
overflow-y: auto;
}
}
</style>
@@ -5,69 +5,38 @@
custom
active-class="active"
>
<li
class="font-medium h-7 my-1 hover:bg-slate-25 hover:text-bg-50 flex items-center px-2 rounded-md dark:hover:bg-slate-800"
:class="{
'bg-woot-25 dark:bg-slate-800': isActive,
'text-ellipsis overflow-hidden whitespace-nowrap max-w-full': shouldTruncate,
}"
@click="navigate"
>
<li :class="{ active: isActive }">
<a
:href="href"
class="inline-flex text-left max-w-full w-full items-center"
class="button clear menu-item text-truncate"
:class="{ 'is-active': isActive, 'text-truncate': shouldTruncate }"
@click="navigate"
>
<span
v-if="icon"
class="inline-flex items-center justify-center w-4 rounded-sm bg-slate-100 dark:bg-slate-700 p-0.5 mr-1.5 rtl:mr-0 rtl:ml-1.5"
>
<fluent-icon
class="text-xxs text-slate-600 dark:text-slate-200"
:class="{
'text-woot-500 dark:text-woot-500': isActive,
}"
:icon="icon"
size="12"
/>
<span v-if="icon" class="badge--icon">
<fluent-icon class="inbox-icon" :icon="icon" size="12" />
</span>
<span
v-if="labelColor"
class="inline-flex rounded-sm bg-slate-100 h-3 w-3.5 mr-1.5 rtl:mr-0 rtl:ml-1.5 border border-slate-50 dark:border-slate-900"
class="badge--label"
:style="{ backgroundColor: labelColor }"
/>
<div
class="items-center flex overflow-hidden whitespace-nowrap text-ellipsis w-full justify-between"
<span
:title="menuTitle"
class="menu-label button__content"
:class="{ 'text-truncate': shouldTruncate }"
>
<span
:title="menuTitle"
class="text-sm text-slate-600 dark:text-slate-100"
:class="{
'text-woot-500 dark:text-woot-500': isActive,
'text-ellipsis overflow-hidden whitespace-nowrap max-w-full': shouldTruncate,
}"
>
{{ label }}
</span>
<span
v-if="showChildCount"
class="bg-slate-50 dark:bg-slate-700 rounded text-xxs font-medium mx-1 py-0 px-1"
:class="
isCountZero
? `text-slate-300 dark:text-slate-600`
: `text-slate-600 dark:text-slate-50`
"
>
{{ label }}
<span v-if="showChildCount" class="count-view">
{{ childItemCount }}
</span>
</div>
<span
v-if="warningIcon"
class="inline-flex rounded-sm mr-1 bg-slate-100"
>
</span>
<span v-if="count" class="badge" :class="{ secondary: !isActive }">
{{ count }}
</span>
<span v-if="warningIcon" class="badge--icon">
<fluent-icon
v-tooltip.top-end="$t('SIDEBAR.FACEBOOK_REAUTHORIZE')"
class="text-xxs"
class="inbox-icon"
:icon="warningIcon"
size="12"
/>
@@ -103,6 +72,10 @@ export default {
type: String,
default: '',
},
count: {
type: String,
default: '',
},
showChildCount: {
type: Boolean,
default: false,
@@ -116,12 +89,96 @@ export default {
showIcon() {
return { 'text-truncate': this.shouldTruncate };
},
isCountZero() {
return this.childItemCount === 0;
},
menuTitle() {
return this.shouldTruncate ? this.label : '';
},
},
};
</script>
<style lang="scss" scoped>
$badge-size: var(--space-normal);
$label-badge-size: var(--space-slab);
.button {
margin: var(--space-small) 0;
}
.menu-item {
display: inline-flex;
color: var(--s-600);
font-weight: var(--font-weight-medium);
width: 100%;
height: var(--space-medium);
padding: var(--space-smaller) var(--space-smaller);
margin: var(--space-smaller) 0;
text-align: left;
line-height: 1.2;
&:hover {
background: var(--s-25);
color: var(--s-600);
}
&:focus {
border-color: var(--w-300);
}
&.is-active {
background: var(--w-25);
color: var(--w-500);
border-color: var(--w-25);
}
&.is-active .count-view {
background: var(--w-75);
color: var(--w-500);
}
}
.menu-label {
flex-grow: 1;
}
.inbox-icon {
font-size: var(--font-size-nano);
}
.badge--label,
.badge--icon {
display: inline-flex;
border-radius: var(--border-radius-small);
margin-right: var(--space-smaller);
background: var(--s-100);
}
.badge--icon {
align-items: center;
height: $badge-size;
justify-content: center;
min-width: $badge-size;
}
.badge--label {
height: $label-badge-size;
min-width: $label-badge-size;
margin-left: var(--space-smaller);
border: 1px solid var(--color-border-light);
}
.badge.secondary {
min-width: unset;
background: var(--s-75);
color: var(--s-600);
font-weight: var(--font-weight-bold);
}
.count-view {
background: var(--s-50);
border-radius: var(--border-radius-normal);
color: var(--s-600);
font-size: var(--font-size-micro);
font-weight: var(--font-weight-bold);
margin-left: var(--space-smaller);
padding: var(--space-zero) var(--space-smaller);
line-height: var(--font-size-small);
}
</style>
@@ -1,57 +1,46 @@
<template>
<li v-show="isMenuItemVisible" class="mt-1">
<div v-if="hasSubMenu" class="flex justify-between">
<span
class="text-sm text-slate-700 dark:text-slate-200 font-semibold my-2 px-2 pt-1"
>
<li v-show="isMenuItemVisible" class="sidebar-item">
<div v-if="hasSubMenu" class="secondary-menu--wrap">
<span class="secondary-menu--header fs-small">
{{ $t(`SIDEBAR.${menuItem.label}`) }}
</span>
<div v-if="menuItem.showNewButton" class="flex items-center">
<div v-if="menuItem.showNewButton" class="submenu-icons">
<woot-button
size="tiny"
variant="clear"
color-scheme="secondary"
icon="add"
class="p-0 ml-2"
class="submenu-icon"
@click="onClickOpen"
/>
</div>
</div>
<router-link
v-else
class="rounded-lg leading-4 font-medium flex items-center p-2 m-0 text-sm text-slate-700 dark:text-slate-100 hover:bg-slate-25 dark:hover:bg-slate-800"
class="secondary-menu--title secondary-menu--link fs-small"
:class="computedClass"
:to="menuItem && menuItem.toState"
>
<fluent-icon
:icon="menuItem.icon"
class="min-w-[1rem] mr-1.5 rtl:mr-0 rtl:ml-1.5"
class="secondary-menu--icon"
size="14"
/>
{{ $t(`SIDEBAR.${menuItem.label}`) }}
<span
v-if="showChildCount(menuItem.count)"
class="rounded-md text-xxs font-medium mx-1 py-0 px-1"
:class="{
'text-slate-300 dark:text-slate-600': isCountZero && !isActiveView,
'text-slate-600 dark:text-slate-50': !isCountZero && !isActiveView,
'bg-woot-75 dark:bg-woot-200 text-woot-600 dark:text-woot-600': isActiveView,
'bg-slate-50 dark:bg-slate-700': !isActiveView,
}"
>
<span v-if="showChildCount(menuItem.count)" class="count-view">
{{ `${menuItem.count}` }}
</span>
<span
v-if="menuItem.beta"
data-view-component="true"
label="Beta"
class="px-1 mx-1 inline-block font-medium leading-4 border border-green-400 text-green-500 rounded-lg text-xxs"
class="beta"
>
{{ $t('SIDEBAR.BETA') }}
</span>
</router-link>
<ul v-if="hasSubMenu" class="list-none ml-0 mb-0">
<ul v-if="hasSubMenu" class="nested vertical menu">
<secondary-child-nav-item
v-for="child in menuItem.children"
:key="child.id"
@@ -66,21 +55,21 @@
/>
<router-link
v-if="showItem(menuItem)"
v-slot="{ href, navigate }"
v-slot="{ href, isActive, navigate }"
:to="menuItem.toState"
custom
>
<li class="pl-1">
<a :href="href">
<woot-button
size="tiny"
variant="clear"
color-scheme="secondary"
icon="add"
@click="e => newLinkClick(e, navigate)"
>
<li class="menu-item--new">
<a
:href="href"
class="button small link clear secondary"
:class="{ 'is-active': isActive }"
@click="e => newLinkClick(e, navigate)"
>
<fluent-icon icon="add" size="16" />
<span class="button__content">
{{ $t(`SIDEBAR.${menuItem.newLinkTag}`) }}
</woot-button>
</span>
</a>
</li>
</router-link>
@@ -119,12 +108,6 @@ export default {
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
globalConfig: 'globalConfig/get',
}),
isCountZero() {
return this.menuItem.count === 0;
},
isActiveView() {
return this.computedClass.includes('active-view');
},
hasSubMenu() {
return !!this.menuItem.children;
},
@@ -195,7 +178,7 @@ export default {
this.isUnattended ||
this.isCurrentRoute
) {
return 'bg-woot-25 dark:bg-slate-800 text-woot-500 dark:text-woot-500 hover:text-woot-500 dark:hover:text-woot-500 active-view';
return 'is-active';
}
if (this.hasSubMenu) {
if (
@@ -204,12 +187,12 @@ export default {
this.isIntegrationsSettings ||
this.isApplicationsSettings
) {
return 'bg-woot-25 dark:bg-slate-800 text-woot-500 dark:text-woot-500 hover:text-woot-500 dark:hover:text-woot-500 active-view';
return 'is-active';
}
return 'hover:text-slate-700 dark:hover:text-slate-100';
return ' ';
}
return 'hover:text-slate-700 dark:hover:text-slate-100';
return '';
},
},
methods: {
@@ -250,3 +233,132 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.sidebar-item {
margin: var(--space-smaller) 0 0;
}
.secondary-menu--wrap {
display: flex;
justify-content: space-between;
margin-top: var(--space-small);
}
.secondary-menu--header {
color: var(--s-700);
display: flex;
font-weight: var(--font-weight-bold);
line-height: var(--space-normal);
margin: var(--space-small) 0;
padding: 0 var(--space-small);
}
.secondary-menu--title {
color: var(--s-600);
display: flex;
font-weight: var(--font-weight-medium);
line-height: var(--space-normal);
margin: var(--space-small) 0;
padding: 0 var(--space-small);
}
.secondary-menu--link {
display: flex;
align-items: center;
margin: 0;
padding: var(--space-small);
font-weight: var(--font-weight-medium);
border-radius: var(--border-radius-normal);
color: var(--s-700);
&:hover {
background: var(--s-25);
color: var(--s-600);
}
&:focus {
border-color: var(--w-300);
}
&.router-link-exact-active,
&.is-active {
background: var(--w-25);
color: var(--w-500);
border-color: var(--w-25);
}
&.is-active .count-view {
background: var(--w-75);
color: var(--w-600);
}
}
.secondary-menu--icon {
margin-right: var(--space-smaller);
min-width: var(--space-normal);
}
.sub-menu-link {
color: var(--s-600);
}
.wrap {
display: flex;
align-items: center;
}
.label-color--display {
border-radius: var(--space-smaller);
height: var(--space-normal);
margin-right: var(--space-small);
min-width: var(--space-normal);
width: var(--space-normal);
}
.inbox-icon {
position: relative;
top: -1px;
}
.sidebar-item .menu-item--new {
padding: var(--space-small) 0;
.button {
display: inline-flex;
color: var(--s-500);
}
}
.beta {
padding-right: var(--space-smaller) !important;
padding-left: var(--space-smaller) !important;
margin: 0 var(--space-smaller) !important;
display: inline-block;
font-size: var(--font-size-micro);
font-weight: var(--font-weight-medium);
line-height: 14px;
border: 1px solid transparent;
border-radius: 2em;
color: var(--g-800);
border-color: var(--g-700);
}
.count-view {
background: var(--s-50);
border-radius: var(--border-radius-normal);
color: var(--s-600);
font-size: var(--font-size-micro);
font-weight: var(--font-weight-bold);
margin: 0 var(--space-smaller);
padding: var(--space-zero) var(--space-smaller);
}
.submenu-icons {
display: flex;
align-items: center;
.submenu-icon {
padding: 0;
margin-left: var(--space-small);
}
}
</style>
@@ -4,6 +4,7 @@ import Vuex from 'vuex';
import VueI18n from 'vue-i18n';
import i18n from 'dashboard/i18n';
import WootModal from 'dashboard/components/Modal';
import WootModalHeader from 'dashboard/components/ModalHeader';
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon';
@@ -37,7 +38,9 @@ describe('accountSelctor', () => {
},
],
};
const accountId = 1;
const globalConfig = { createNewAccountFromDashboard: false };
let store = null;
let actions = null;
let modules = null;
@@ -46,46 +49,44 @@ describe('accountSelctor', () => {
modules = {
auth: {
getters: {
getCurrentAccountId: () => 1,
getCurrentAccountId: () => accountId,
getCurrentUser: () => currentUser,
},
},
globalConfig: {
getters: {
'globalConfig/get': () => ({ createNewAccountFromDashboard: false }),
'globalConfig/get': () => globalConfig,
},
},
};
let store = new Vuex.Store({ actions, modules });
store = new Vuex.Store({
actions,
modules,
});
accountSelector = mount(AccountSelector, {
store,
localVue,
i18n: i18nConfig,
propsData: { showAccountModal: true },
stubs: { WootButton: { template: '<button />' } },
propsData: {
showAccountModal: true,
},
});
});
it('title and sub title exist', () => {
const headerComponent = accountSelector.findComponent(WootModalHeader);
const title = headerComponent.findComponent({ ref: 'modalHeaderTitle' });
expect(title.text()).toBe('Switch Account');
const content = headerComponent.findComponent({
ref: 'modalHeaderContent',
});
expect(content.text()).toBe('Select an account from the following list');
const topBar = headerComponent.find('.page-top-bar');
const titleComponent = topBar.find('.page-sub-title');
expect(titleComponent.text()).toBe('Switch Account');
const subTitleComponent = topBar.find('p');
expect(subTitleComponent.text()).toBe(
'Select an account from the following list'
);
});
it('first account item is checked', () => {
const selectedAccountCheckmark = accountSelector.find(
'#account-1 > button > svg'
);
expect(selectedAccountCheckmark.exists()).toBe(true);
const otherAccountCheckmark = accountSelector.find(
'#account-2 > button > svg'
);
expect(otherAccountCheckmark.exists()).toBe(true);
const accountFirstItem = accountSelector.find('.account-selector svg');
expect(accountFirstItem.exists()).toBe(true);
});
});
@@ -19,7 +19,10 @@ localVue.component('woot-dropdown-menu', WootDropdownMenu);
localVue.component('woot-dropdown-divider', WootDropdownDivider);
localVue.component('woot-dropdown-item', WootDropdownItem);
const i18nConfig = new VueI18n({ locale: 'en', messages: i18n });
const i18nConfig = new VueI18n({
locale: 'en',
messages: i18n,
});
describe('AvailabilityStatus', () => {
const currentAvailability = 'online';
@@ -45,13 +48,15 @@ describe('AvailabilityStatus', () => {
},
};
store = new Vuex.Store({ actions, modules });
store = new Vuex.Store({
actions,
modules,
});
availabilityStatus = mount(AvailabilityStatus, {
store,
localVue,
i18n: i18nConfig,
stubs: { WootSwitch: { template: '<button />' } },
});
});
@@ -3,9 +3,7 @@ import SidemenuIcon from '../SidemenuIcon';
describe('SidemenuIcon', () => {
test('matches snapshot', () => {
const wrapper = shallowMount(SidemenuIcon, {
stubs: { WootButton: { template: '<button><slot /></button>' } },
});
const wrapper = shallowMount(SidemenuIcon);
expect(wrapper.vm).toBeTruthy();
expect(wrapper.element).toMatchSnapshot();
});
@@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SidemenuIcon matches snapshot 1`] = `
<button
class="-ml-3 text-black-900 dark:text-slate-300"
<woot-button
class="toggle-sidebar"
color-scheme="secondary"
icon="list"
size="small"
@@ -4,7 +4,7 @@
<fluent-icon :icon="icon" size="12" class="label--icon" />
</span>
<span
v-if="['smooth', 'dashed'].includes(variant) && title && !icon"
v-if="variant === 'smooth' && title && !icon"
:style="{ background: color }"
class="label-color-dot"
/>
@@ -69,7 +69,6 @@ export default {
computed: {
textColor() {
if (this.variant === 'smooth') return '';
if (this.variant === 'dashed') return '';
return this.color || getContrastingTextColor(this.bgColor);
},
labelClass() {
@@ -200,14 +199,8 @@ export default {
&.smooth {
background: transparent;
color: var(--s-700);
border: 1px solid var(--s-100);
}
&.dashed {
background: transparent;
color: var(--s-700);
border: 1px dashed var(--s-100);
}
}
@@ -13,10 +13,9 @@
<div class="content-wrap">
{{ content }}
</div>
<div v-if="src" class="image-wrap">
<div class="image-wrap">
<img :src="src" class="image" :class="{ activeimage: active }" />
</div>
<slot v-else />
</div>
</template>
@@ -5,7 +5,7 @@
delay: { show: 1500, hide: 0 },
hideOnClick: true,
}"
class="text-xxs text-black-600 leading-4 ml-auto hover:text-black-900"
class="time-ago"
>
<span>{{ `${createdAtTime} • ${lastActivityTime}` }}</span>
</div>
@@ -109,3 +109,16 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.time-ago {
color: var(--b-600);
font-size: var(--font-size-micro);
font-weight: var(--font-weight-normal);
line-height: var(--space-normal);
margin-left: auto;
&:hover {
color: var(--b-900);
}
}
</style>
@@ -18,11 +18,7 @@
:icon="icon"
:icon-size="iconSize"
/>
<span
v-if="$slots.default"
class="button__content"
:class="{ 'text-left rtl:text-right': size !== 'expanded' }"
>
<span v-if="$slots.default" class="button__content">
<slot />
</span>
</button>
@@ -8,73 +8,268 @@
size="small"
@click="openAIAssist"
/>
<!-- <woot-button
v-tooltip.top-end="$t('INTEGRATION_SETTINGS.OPEN_AI.TITLE')"
icon="text-grammar-wand"
color-scheme="secondary"
variant="smooth"
size="small"
@click="toggleDropdown"
/> -->
<div v-if="!message">
<!-- <woot-button
v-if="isPrivateNote"
v-tooltip.top-end="$t('INTEGRATION_SETTINGS.OPEN_AI.SUMMARY_TITLE')"
icon="book-pulse"
color-scheme="secondary"
variant="smooth"
size="small"
:is-loading="uiFlags.summarize"
@click="processEvent('summarize')"
/>
<woot-button
v-else
v-tooltip.top-end="$t('INTEGRATION_SETTINGS.OPEN_AI.REPLY_TITLE')"
icon="wand"
color-scheme="secondary"
variant="smooth"
size="small"
:is-loading="uiFlags.reply_suggestion"
@click="processEvent('reply_suggestion')"
/> -->
</div>
<div
v-if="showDropdown"
v-on-clickaway="closeDropdown"
class="dropdown-pane dropdown-pane--open ai-modal"
>
<h4 class="sub-block-title margin-top-1">
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.TITLE') }}
</h4>
<p>
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.SUBTITLE') }}
</p>
<label>
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.TONE.TITLE') }}
</label>
<div class="tone__item">
<select v-model="activeTone" class="status--filter small">
<option v-for="tone in tones" :key="tone.key" :value="tone.key">
{{ tone.value }}
</option>
</select>
</div>
<div class="modal-footer flex-container align-right">
<woot-button variant="clear" size="small" @click="closeDropdown">
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.BUTTONS.CANCEL') }}
</woot-button>
<woot-button
:is-loading="uiFlags.rephrase"
size="small"
@click="processEvent('rephrase')"
>
{{ buttonText }}
</woot-button>
</div>
</div>
<woot-modal
:show.sync="showAIAssistanceModal"
:on-close="hideAIAssistanceModal"
>
<AIAssistanceModal
:ai-option="aiOption"
@apply-text="insertText"
@close="hideAIAssistanceModal"
/>
</woot-modal>
<woot-modal
:show.sync="showAIAssistanceReply"
:on-close="hideAIAssistanceReplyModal"
>
<AIAssistanceReply
@apply-text="insertText"
@close="hideAIAssistanceReplyModal"
/>
</woot-modal>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { mixin as clickaway } from 'vue-clickaway';
import OpenAPI from 'dashboard/api/integrations/openapi';
import alertMixin from 'shared/mixins/alertMixin';
import { OPEN_AI_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
import { CMD_AI_ASSIST } from '../../routes/dashboard/commands/commandBarBusEvents';
import AIAssistanceModal from './AIAssistanceModal.vue';
import aiMixin from 'dashboard/mixins/aiMixin';
import { CMD_AI_ASSIST } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
import { buildHotKeys } from 'shared/helpers/KeyboardHelpers';
import AIAssistanceReply from './AIAssistanceReply.vue';
export default {
components: {
AIAssistanceModal,
AIAssistanceReply,
},
mixins: [alertMixin, clickaway],
props: {
conversationId: {
type: Number,
default: 0,
},
message: {
type: String,
default: '',
},
isPrivateNote: {
type: Boolean,
default: false,
},
},
data() {
return {
showAIAssistanceModal: false,
showAIAssistanceReply: false,
uiFlags: {
rephrase: false,
reply_suggestion: false,
summarize: false,
},
showDropdown: false,
activeTone: 'professional',
tones: [
{
key: 'professional',
value: this.$t(
'INTEGRATION_SETTINGS.OPEN_AI.TONE.OPTIONS.PROFESSIONAL'
),
},
{
key: 'friendly',
value: this.$t('INTEGRATION_SETTINGS.OPEN_AI.TONE.OPTIONS.FRIENDLY'),
},
],
};
},
mixins: [aiMixin, eventListenerMixins],
data: () => ({
showAIAssistanceModal: false,
aiOption: '',
initialMessage: '',
}),
computed: {
...mapGetters({
currentChat: 'getSelectedChat',
}),
...mapGetters({ appIntegrations: 'integrations/getAppIntegrations' }),
isAIIntegrationEnabled() {
return this.appIntegrations.find(
integration => integration.id === 'openai' && !!integration.hooks.length
);
},
hookId() {
return this.appIntegrations.find(
integration => integration.id === 'openai' && !!integration.hooks.length
).hooks[0].id;
},
buttonText() {
return this.uiFlags.isRephrasing
? this.$t('INTEGRATION_SETTINGS.OPEN_AI.BUTTONS.GENERATING')
: this.$t('INTEGRATION_SETTINGS.OPEN_AI.BUTTONS.GENERATE');
},
},
mounted() {
bus.$on(CMD_AI_ASSIST, this.onAIAssist);
this.initialMessage = this.draftMessage;
if (!this.appIntegrations.length) {
this.$store.dispatch('integrations/get');
}
},
destroyed() {
bus.$off(CMD_AI_ASSIST, this.onAIAssist);
},
methods: {
onKeyDownHandler(event) {
const keyPattern = buildHotKeys(event);
const shouldRevertTheContent =
['meta+z', 'ctrl+z'].includes(keyPattern) && !!this.initialMessage;
if (shouldRevertTheContent) {
this.$emit('replace-text', this.initialMessage);
this.initialMessage = '';
}
hideAIAssistanceReplyModal() {
this.showAIAssistanceReply = false;
},
hideAIAssistanceModal() {
this.showAIAssistanceModal = false;
},
openAIAssist() {
this.initialMessage = this.draftMessage;
const ninja = document.querySelector('ninja-keys');
ninja.open({ parent: 'ai_assist' });
},
onAIAssist(option) {
this.aiOption = option;
this.showAIAssistanceModal = true;
onAIAssist() {
// this.showAIAssistanceModal = true;
this.showAIAssistanceReply = true;
},
toggleDropdown() {
this.showDropdown = !this.showDropdown;
},
closeDropdown() {
this.showDropdown = false;
},
async recordAnalytics({ type, tone }) {
const event = OPEN_AI_EVENTS[type.toUpperCase()];
if (event) {
this.$track(event, {
type,
tone,
});
}
},
insertText(message) {
console.log('message', message);
this.$emit('replace-text', message);
},
async processEvent(type = 'rephrase') {
this.uiFlags[type] = true;
try {
const result = await OpenAPI.processEvent({
hookId: this.hookId,
type,
content: this.message,
tone: this.activeTone,
conversationId: this.conversationId,
});
const {
data: { message: generatedMessage },
} = result;
this.$emit('replace-text', generatedMessage || this.message);
this.closeDropdown();
this.recordAnalytics({ type, tone: this.activeTone });
} catch (error) {
this.showAlert(this.$t('INTEGRATION_SETTINGS.OPEN_AI.GENERATE_ERROR'));
} finally {
this.uiFlags[type] = false;
}
},
},
};
</script>
<style lang="scss" scoped>
.ai-modal {
width: 400px;
right: 0;
left: 0;
padding: var(--space-normal);
bottom: 34px;
position: absolute;
span {
font-size: var(--font-size-small);
font-weight: var(--font-weight-medium);
}
p {
color: var(--s-600);
}
label {
margin-bottom: var(--space-smaller);
}
.status--filter {
background-color: var(--color-background-light);
border: 1px solid var(--color-border);
font-size: var(--font-size-small);
height: var(--space-large);
padding: 0 var(--space-medium) 0 var(--space-small);
}
.modal-footer {
gap: var(--space-smaller);
}
}
</style>
@@ -1,33 +1,52 @@
<template>
<div class="column">
<woot-modal-header :header-title="headerTitle" />
<form class="row modal-content" @submit.prevent="applyText">
<div v-if="draftMessage" class="w-full">
<h4 class="sub-block-title margin-top-1 ">
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.DRAFT_TITLE') }}
</h4>
<p v-dompurify-html="formatMessage(draftMessage, false)" />
<woot-modal-header header-title="Rephrase the content" />
<form class="row modal-content" @submit.prevent="chooseTime">
<div>
<h4 class="sub-block-title margin-top-1">
{{
$t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.GENERATED_TITLE')
}}
Draft content
</h4>
</div>
<div>
<AILoader v-if="isGenerating" />
<p v-else v-dompurify-html="formatMessage(generatedContent, false)" />
<p>
{{ draftContent }}
</p>
<div class="container">
<h4 class="sub-block-title margin-top-1">
AI generated content
</h4>
</div>
<div class="animation-container margin-top-1">
<div class="ai-typing--wrap ">
<fluent-icon icon="wand" size="14" class="ai-typing--icon" />
<label>AI is writing</label>
</div>
<span class="loader" />
<span class="loader" />
<span class="loader" />
</div>
<div class="modal-footer justify-content-end w-full">
<!-- <woot-button
icon="delete-outline"
variant="clear"
@click.prevent="onClose"
>
Discard
</woot-button> -->
<woot-button variant="clear" @click.prevent="onClose">
{{
$t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.BUTTONS.CANCEL')
}}
Discard
</woot-button>
<woot-button :disabled="!generatedContent">
{{
$t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.BUTTONS.APPLY')
}}
<!-- <woot-button
v-tooltip.top-end="$t('CONVERSATION.TRY_AGAIN')"
color-scheme="alert"
variant="clear"
icon="arrow-clockwise"
:disabled="!enableButtons"
>Retry
</woot-button> -->
<woot-button :disabled="!enableButtons">
Apply
</woot-button>
</div>
</form>
@@ -35,63 +54,36 @@
</template>
<script>
import { mapGetters } from 'vuex';
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
import AILoader from './AILoader.vue';
import aiMixin from 'dashboard/mixins/aiMixin';
export default {
components: {
AILoader,
},
mixins: [aiMixin, messageFormatterMixin],
props: {
aiOption: {
type: String,
required: true,
},
},
data() {
return {
draftContent:
'I am closing this thread as there is no response. Please feel to create a new one if the issue/query is not resolved.',
generatedContent: '',
isGenerating: true,
};
},
computed: {
...mapGetters({
appIntegrations: 'integrations/getAppIntegrations',
}),
headerTitle() {
const translationKey = this.aiOption?.toUpperCase();
return translationKey
? this.$t(`INTEGRATION_SETTINGS.OPEN_AI.WITH_AI`, {
option: this.$t(
`INTEGRATION_SETTINGS.OPEN_AI.OPTIONS.${translationKey}`
),
})
: '';
aiContent() {
return this.generatedContent.length > 0
? this.generatedContent
: 'Generating content...';
},
enableButtons() {
return this.generatedContent.length > 0;
},
},
mounted() {
this.generateAIContent(this.aiOption);
setTimeout(() => {
this.generatedContent =
'I am closing this thread as there has been no response. Please feel free to create a new thread if the issue or query remains unresolved.';
}, 6000);
},
methods: {
onClose() {
this.$emit('close');
},
async generateAIContent(type = 'rephrase') {
this.isGenerating = true;
this.generatedContent = await this.processEvent(type);
this.isGenerating = false;
},
applyText() {
this.recordAnalytics(this.aiOption);
this.$emit('apply-text', this.generatedContent);
this.onClose();
},
chooseTime() {},
},
};
</script>
@@ -104,4 +96,57 @@ export default {
.container {
width: 100%;
}
.ai-typing--wrap {
display: flex;
align-items: center;
gap: 4px;
.ai-typing--icon {
color: var(--v-500);
}
}
.animation-container {
position: relative;
display: flex;
}
.animation-container label {
display: inline-block;
margin-right: 8px;
color: var(--v-400);
}
.loader {
display: inline-block;
width: 6px;
height: 6px;
margin-right: 4px;
margin-top: 12px;
background-color: var(--v-300);
border-radius: 50%;
animation: bubble-scale 1.2s infinite;
}
.loader:nth-child(2) {
animation-delay: 0.4s;
}
.loader:nth-child(3) {
animation-delay: 0.8s;
}
@keyframes bubble-scale {
0%,
100% {
transform: scale(1);
}
25% {
transform: scale(1.3);
}
50% {
transform: scale(1);
}
}
</style>
@@ -0,0 +1,175 @@
<template>
<div class="column">
<woot-modal-header header-title="Reply suggestion with AI" />
<form class="row modal-content" @submit.prevent="chooseTime">
<div v-if="!generatedContent" class="animation-container margin-top-1">
<div class="ai-typing--wrap ">
<fluent-icon icon="wand" size="14" class="ai-typing--icon" />
<label>AI is writing</label>
</div>
<span class="loader" />
<span class="loader" />
<span class="loader" />
</div>
<div v-else>
<p>
{{ aiContent }}
</p>
</div>
<div class="modal-footer justify-content-end w-full">
<!-- <woot-button
icon="delete-outline"
variant="clear"
@click.prevent="onClose"
>
Discard
</woot-button> -->
<woot-button variant="clear" @click.prevent="onClose">
Discard
</woot-button>
<!-- <woot-button
v-tooltip.top-end="$t('CONVERSATION.TRY_AGAIN')"
color-scheme="alert"
variant="clear"
icon="arrow-clockwise"
:disabled="!enableButtons"
>Retry
</woot-button> -->
<woot-button :disabled="!enableButtons" @click="applyContent">
Apply
</woot-button>
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
draftContent:
'I am closing this thread as there is no response. Please feel to create a new one if the issue/query is not resolved.',
generatedContent: '',
};
},
computed: {
aiContent() {
return this.generatedContent.length > 0
? this.generatedContent
: 'Generating content...';
},
enableButtons() {
return this.generatedContent.length > 0;
},
},
mounted() {
setTimeout(() => {
this.generatedContent =
'Hi there! Its great to see you here. How can I be of assistance to you today? Whether you need help with a specific task or have a general question, I am here to lend a hand. Let me know what you need, and I will do my best to provide you with the information or guidance you are looking for.';
}, 5000);
},
methods: {
onClose() {
this.$emit('close');
},
chooseTime() {},
applyContent() {
this.$emit('apply-text', this.generatedContent);
this.$emit('close');
},
},
};
</script>
<style lang="scss" scoped>
.modal-content {
padding-top: var(--space-small);
}
.container {
width: 100%;
}
.ai-typing--wrap {
display: flex;
align-items: center;
gap: 4px;
.ai-typing--icon {
color: var(--v-500);
}
}
.animation-container {
position: relative;
display: flex;
}
.animation-container label {
display: inline-block;
margin-right: 8px;
color: var(--v-400);
}
.loader {
display: inline-block;
width: 6px;
height: 6px;
margin-right: 4px;
margin-top: 12px;
background-color: var(--v-300);
border-radius: 50%;
animation: bubble-scale 1.2s infinite;
}
.loader:nth-child(2) {
animation-delay: 0.4s;
}
.loader:nth-child(3) {
animation-delay: 0.8s;
}
@keyframes bubble-scale {
0%,
100% {
transform: scale(1);
}
25% {
transform: scale(1.3);
}
50% {
transform: scale(1);
}
}
// .typewriter p {
// overflow: hidden; /* Ensures the content is not revealed until the animation */
// border-right: 0.15em solid orange; /* The typwriter cursor */
// margin: 0 auto; /* Gives that scrolling effect as the typing happens */
// animation: typing 3.5s steps(30, end), blink-caret 0.5s step-end infinite;
// }
// /* The typing effect */
// @keyframes typing {
// from {
// width: 0;
// }
// to {
// width: 100%;
// }
// }
// @keyframes blink-caret {
// from,
// to {
// border-color: transparent;
// }
// 50% {
// border-color: orange;
// }
// }
</style>
@@ -1,67 +0,0 @@
<template>
<div class="animation-container margin-top-1">
<div class="ai-typing--wrap ">
<fluent-icon icon="wand" size="14" class="ai-typing--icon" />
<label>
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.AI_WRITING') }}
</label>
</div>
<span class="loader" />
<span class="loader" />
<span class="loader" />
</div>
</template>
<script></script>
<style lang="scss" scoped>
.animation-container {
position: relative;
display: flex;
.ai-typing--wrap {
display: flex;
align-items: center;
gap: 4px;
.ai-typing--icon {
color: var(--v-500);
}
}
label {
display: inline-block;
margin-right: var(--space-smaller);
color: var(--v-400);
}
.loader {
display: inline-block;
width: 6px;
height: 6px;
margin-right: var(--space-smaller);
margin-top: var(--space-slab);
background-color: var(--v-300);
border-radius: 50%;
animation: bubble-scale 1.2s infinite;
}
.loader:nth-child(2) {
animation-delay: 0.4s;
}
.loader:nth-child(3) {
animation-delay: 0.8s;
}
@keyframes bubble-scale {
0%,
100% {
transform: scale(1);
}
25% {
transform: scale(1.3);
}
50% {
transform: scale(1);
}
}
}
</style>
@@ -1,6 +1,6 @@
<template>
<div class="avatar-container" :style="style" aria-hidden="true">
<slot>{{ userInitial }}</slot>
{{ userInitial }}
</div>
</template>
@@ -21,9 +21,6 @@ export default {
},
},
computed: {
hasFbConfigured() {
return window.chatwootConfig?.fbAppId;
},
isActive() {
const { key } = this.channel;
if (Object.keys(this.enabledFeatures).length === 0) {
@@ -33,7 +30,10 @@ export default {
return this.enabledFeatures.channel_website;
}
if (key === 'facebook') {
return this.enabledFeatures.channel_facebook && this.hasFbConfigured;
return this.enabledFeatures.channel_facebook;
}
if (key === 'twitter') {
return this.enabledFeatures.channel_twitter;
}
if (key === 'email') {
return this.enabledFeatures.channel_email;
@@ -1,11 +1,6 @@
<template>
<div class="flex items-center justify-center p-8">
<h6
class="block text-base text-center w-100 text-slate-800 dark:text-slate-300"
>
<span class="mr-3">{{ message }}</span>
<span class="spinner" />
</h6>
<div class="row loading-state">
<h6 class="message">{{ message }}<span class="spinner" /></h6>
</div>
</template>
<script>
@@ -5,21 +5,19 @@
:title="title"
>
<!-- Using v-show instead of v-if to avoid flickering as v-if removes dom elements. -->
<slot>
<img
v-show="shouldShowImage"
:src="src"
:class="thumbnailClass"
@load="onImgLoad"
@error="onImgError"
/>
<Avatar
v-show="!shouldShowImage"
:username="userNameWithoutEmoji"
:class="thumbnailClass"
:size="avatarSize"
/>
</slot>
<img
v-show="shouldShowImage"
:src="src"
:class="thumbnailClass"
@load="onImgLoad"
@error="onImgError"
/>
<Avatar
v-show="!shouldShowImage"
:username="userNameWithoutEmoji"
:class="thumbnailClass"
:size="avatarSize"
/>
<img
v-if="badgeSrc"
class="source-badge"
@@ -0,0 +1,77 @@
<template>
<div class="typing-indicator">
<label>AI is typing</label>
<div class="typing">
<span class="circle scaling" />
<span class="circle scaling" />
<span class="circle scaling" />
</div>
</div>
</template>
<style lang="scss" scoped>
.typing-indicator {
color: #282d3e;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
width: fit-content;
.typing {
display: block;
width: 36px;
height: 16px;
border-radius: 20px;
margin: 0 4px 0 8px;
display: flex;
justify-content: center;
align-items: center;
}
.circle {
display: block;
height: 6px;
width: 6px;
border-radius: 50%;
background-color: #000;
margin: 3px;
&.scaling {
animation: typing 1000ms ease-in-out infinite;
animation-delay: 3600ms;
}
&.bouncing {
animation: bounce 1000ms ease-in-out infinite;
animation-delay: 3600ms;
}
.circle:nth-child(1) {
animation-delay: 0ms;
}
.circle:nth-child(2) {
animation-delay: 333ms;
}
.circle:nth-child(3) {
animation-delay: 666ms;
}
}
}
@keyframes typing {
0% {
transform: scale(1);
}
33% {
transform: scale(1);
}
50% {
transform: scale(1.4);
}
100% {
transform: scale(1);
}
}
</style>
@@ -310,9 +310,11 @@ export default {
userFullName: mentionItem.name,
});
const tr = this.editorView.state.tr
.replaceWith(this.range.from, this.range.to, node)
.insertText(` `);
const tr = this.editorView.state.tr.replaceWith(
this.range.from,
this.range.to,
node
);
this.state = this.editorView.state.apply(tr);
this.emitOnChange();
this.$track(CONVERSATION_EVENTS.USED_MENTIONS);
@@ -121,7 +121,8 @@ export default {
.top-box {
display: flex;
justify-content: space-between;
@apply bg-black-50 dark:bg-slate-800;
background: var(--b-50);
}
.button-group {
@@ -144,11 +145,11 @@ export default {
.button--reply {
border-radius: 0;
@apply border-r border-b-0 border-l-0 border-slate-50 dark:border-slate-700;
border-right: 1px solid var(--color-border);
&:hover,
&:focus {
@apply border-r border-slate-50 dark:border-slate-700;
border-right: 1px solid var(--color-border);
}
}
@@ -156,7 +157,7 @@ export default {
border-radius: 0;
&.is-active {
@apply border-r border-b-0 border-slate-50 dark:border-slate-700;
border-right: 1px solid var(--color-border);
background: var(--y-50);
}
@@ -1,9 +1,5 @@
<template>
<div
:class="
`status-badge status-badge__${status} rounded-full w-2.5 h-2.5 mr-0.5 rtl:mr-0 rtl:ml-0.5 inline-flex`
"
/>
<div :class="`status-badge status-badge__${status}`" />
</template>
<script>
export default {
@@ -13,15 +9,21 @@ export default {
};
</script>
<style lang="scss" scoped>
@import '~dashboard/assets/scss/variables';
.status-badge {
width: var(--space-one);
height: var(--space-one);
margin-right: var(--space-micro);
display: inline-block;
border-radius: 50%;
&__online {
@apply bg-green-400;
background: var(--g-400);
}
&__offline {
@apply bg-slate-500;
background: var(--s-500);
}
&__busy {
@apply bg-yellow-500;
background: var(--y-500);
}
}
</style>
@@ -1,6 +1,6 @@
<template>
<div
class="conversation-details-wrap bg-slate-25 dark:bg-slate-800"
class="conversation-details-wrap"
:class="{ 'with-border-right': !isOnExpandedLayout }"
>
<conversation-header
@@ -23,10 +23,7 @@
:show-badge="false"
/>
</woot-tabs>
<div
v-show="!activeIndex"
class="flex bg-slate-25 dark:bg-slate-800 m-0 h-full min-h-0"
>
<div v-show="!activeIndex" class="messages-and-sidebar">
<messages-view
v-if="currentChat.id"
:inbox-id="inboxId"
@@ -142,14 +139,16 @@ export default {
</script>
<style lang="scss" scoped>
@import '~dashboard/assets/scss/woot';
.conversation-details-wrap {
display: flex;
flex-direction: column;
min-width: 0;
width: 100%;
background: var(--color-background-light);
&.with-border-right {
@apply border-r border-slate-50 dark:border-slate-700;
border-right: 1px solid var(--color-border);
}
}
@@ -159,8 +158,20 @@ export default {
min-height: var(--dashboard-app-tabs-height);
}
.messages-and-sidebar {
display: flex;
background: var(--color-background-light);
margin: 0;
height: 100%;
min-height: 0;
}
.conversation-sidebar-wrap {
@apply border-r border-slate-50 dark:border-slate-700;
border-right: 1px solid var(--color-border);
height: auto;
flex: 0 0;
z-index: var(--z-index-low);
overflow: auto;
background: white;
flex-basis: 100%;
@@ -1,6 +1,6 @@
<template>
<div
class="conversation group border-t border-transparent"
class="conversation"
:class="{
active: isActiveChat,
'unread-chat': hasUnread,
@@ -30,9 +30,7 @@
:status="currentContact.availability_status"
size="40px"
/>
<div
class="conversation--details border-b group-last:border-transparent group-hover:border-transparent border-slate-50 dark:border-slate-800 columns"
>
<div class="conversation--details columns">
<div class="conversation--metadata">
<inbox-name v-if="showInboxName" :inbox="inbox" />
<div class="conversation-metadata-attributes">
@@ -1,8 +1,6 @@
<template>
<div class="conv-header flex-col md:flex-row">
<div
class="flex-1 w-100 flex flex-col md:flex-row items-center justify-center"
>
<div class="conv-header">
<div class="conversation-header--details">
<div class="user">
<back-button v-if="showBackButton" :back-url="backButtonUrl" />
<Thumbnail
@@ -46,7 +44,7 @@
</div>
</div>
<div
class="header-actions-wrap mt-3 lg:mt-0"
class="header-actions-wrap"
:class="{ 'has-open-sidebar': isContactPanelOpen }"
>
<more-actions :conversation-id="currentChat.id" />
@@ -161,9 +159,27 @@ export default {
</script>
<style lang="scss" scoped>
@import '~dashboard/assets/scss/woot';
.conv-header {
flex: 0 0 var(--space-jumbo);
flex-direction: row;
@include breakpoint(medium up) {
flex-direction: column;
}
}
.conversation-header--details {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
width: 100%;
@include breakpoint(medium up) {
flex-direction: row;
}
}
.option__desc {
@@ -64,12 +64,6 @@
:has-instagram-story="hasInstagramStory"
:is-web-widget-inbox="isAWebWidgetInbox"
/>
<conversation-label-suggestion
v-if="shouldShowLabelSuggestions"
:suggested-labels="labelSuggestions"
:chat-labels="currentChat.labels"
:conversation-id="currentChat.id"
/>
</ul>
<div
class="conversation-footer"
@@ -97,48 +91,29 @@
</template>
<script>
// components
import ReplyBox from './ReplyBox';
import Message from './Message';
import ConversationLabelSuggestion from './conversation/LabelSuggestion';
import Banner from 'dashboard/components/ui/Banner.vue';
// stores and apis
import { mapGetters } from 'vuex';
// mixins
import ReplyBox from './ReplyBox';
import Message from './Message';
import conversationMixin, {
filterDuplicateSourceMessages,
} from '../../../mixins/conversations';
import inboxMixin from 'shared/mixins/inboxMixin';
import configMixin from 'shared/mixins/configMixin';
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
import aiMixin from 'dashboard/mixins/aiMixin';
// utils
import Banner from 'dashboard/components/ui/Banner.vue';
import { getTypingUsersText } from '../../../helper/commons';
import { calculateScrollTop } from './helpers/scrollTopCalculationHelper';
import { isEscape } from 'shared/helpers/KeyboardHelpers';
// constants
import { BUS_EVENTS } from 'shared/constants/busEvents';
import { REPLY_POLICY } from 'shared/constants/links';
import wootConstants from 'dashboard/constants/globals';
import inboxMixin from 'shared/mixins/inboxMixin';
import { calculateScrollTop } from './helpers/scrollTopCalculationHelper';
import { isEscape } from 'shared/helpers/KeyboardHelpers';
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
export default {
components: {
Message,
ReplyBox,
Banner,
ConversationLabelSuggestion,
},
mixins: [
conversationMixin,
inboxMixin,
eventListenerMixins,
configMixin,
aiMixin,
],
mixins: [conversationMixin, inboxMixin, eventListenerMixins],
props: {
isContactPanelOpen: {
type: Boolean,
@@ -152,11 +127,7 @@ export default {
heightBeforeLoad: null,
conversationPanel: null,
selectedTweetId: null,
hasUserScrolled: false,
isProgrammaticScroll: false,
isPopoutReplyBox: false,
messageSentSinceOpened: false,
labelSuggestions: [],
};
},
@@ -167,20 +138,7 @@ export default {
inboxesList: 'inboxes/getInboxes',
listLoadingStatus: 'getAllMessagesLoaded',
loadingChatList: 'getChatListLoadingStatus',
appIntegrations: 'integrations/getAppIntegrations',
currentAccountId: 'getCurrentAccountId',
}),
isOpen() {
return this.currentChat?.status === wootConstants.STATUS_TYPE.OPEN;
},
shouldShowLabelSuggestions() {
return (
this.isOpen &&
this.isEnterprise &&
this.isAIIntegrationEnabled &&
!this.messageSentSinceOpened
);
},
inboxId() {
return this.currentChat.inbox_id;
},
@@ -322,28 +280,18 @@ export default {
return;
}
this.fetchAllAttachmentsFromCurrentChat();
this.fetchSuggestions();
this.messageSentSinceOpened = false;
this.selectedTweetId = null;
},
},
created() {
bus.$on(BUS_EVENTS.SCROLL_TO_MESSAGE, this.onScrollToMessage);
// when a new message comes in, we refetch the label suggestions
bus.$on(BUS_EVENTS.FETCH_LABEL_SUGGESTIONS, this.fetchSuggestions);
bus.$on(BUS_EVENTS.SET_TWEET_REPLY, this.setSelectedTweet);
// when a message is sent we set the flag to true this hides the label suggestions,
// until the chat is changed and the flag is reset in the watch for currentChat
bus.$on(BUS_EVENTS.MESSAGE_SENT, () => {
this.messageSentSinceOpened = true;
});
},
mounted() {
this.addScrollListener();
this.fetchAllAttachmentsFromCurrentChat();
this.fetchSuggestions();
},
beforeDestroy() {
@@ -352,49 +300,6 @@ export default {
},
methods: {
async fetchSuggestions() {
// start empty, this ensures that the label suggestions are not shown
this.labelSuggestions = [];
if (this.isLabelSuggestionDismissed()) {
return;
}
if (!this.isEnterprise) {
return;
}
// method available in mixin, need to ensure that integrations are present
await this.fetchIntegrationsIfRequired();
if (!this.isAIIntegrationEnabled) {
return;
}
this.labelSuggestions = await this.fetchLabelSuggestions({
conversationId: this.currentChat.id,
});
// once the labels are fetched, we need to scroll to bottom
// but we need to wait for the DOM to be updated
// so we use the nextTick method
this.$nextTick(() => {
// this param is added to route, telling the UI to navigate to the message
// it is triggered by the SCROLL_TO_MESSAGE method
// see setActiveChat on ConversationView.vue for more info
const { messageId } = this.$route.query;
// only trigger the scroll to bottom if the user has not scrolled
// and there's no active messageId that is selected in view
if (!messageId && !this.hasUserScrolled) {
this.scrollToBottom();
}
});
},
isLabelSuggestionDismissed() {
const dismissed = this.getDismissedConversations(this.currentAccountId);
return dismissed[this.currentAccountId].includes(this.conversationId);
},
fetchAllAttachmentsFromCurrentChat() {
this.$store.dispatch('fetchAllAttachments', this.currentChat.id);
},
@@ -409,7 +314,6 @@ export default {
this.$nextTick(() => {
const messageElement = document.getElementById('message' + messageId);
if (messageElement) {
this.isProgrammaticScroll = true;
messageElement.scrollIntoView({ behavior: 'smooth' });
this.fetchPreviousMessages();
} else {
@@ -440,34 +344,18 @@ export default {
this.conversationPanel.removeEventListener('scroll', this.handleScroll);
},
scrollToBottom() {
this.isProgrammaticScroll = true;
let relevantMessages = [];
// label suggestions are not part of the messages list
// so we need to handle them separately
let labelSuggestions = this.conversationPanel.querySelector(
'.label-suggestion'
);
// if there are unread messages, scroll to the first unread message
if (this.unreadMessageCount > 0) {
// capturing only the unread messages
relevantMessages = this.conversationPanel.querySelectorAll(
'.message--unread'
);
} else if (labelSuggestions) {
// when scrolling to the bottom, the label suggestions is below the last message
// so we scroll there if there are no unread messages
// Unread messages always take the highest priority
relevantMessages = [labelSuggestions];
} else {
// if there are no unread messages or label suggestion, scroll to the last message
// capturing last message from the messages list
relevantMessages = Array.from(
this.conversationPanel.querySelectorAll('.message--read')
).slice(-1);
}
this.conversationPanel.scrollTop = calculateScrollTop(
this.conversationPanel.scrollHeight,
this.$el.scrollHeight,
@@ -514,13 +402,6 @@ export default {
},
handleScroll(e) {
if (this.isProgrammaticScroll) {
// Reset the flag
this.isProgrammaticScroll = false;
this.hasUserScrolled = false;
} else {
this.hasUserScrolled = true;
}
bus.$emit(BUS_EVENTS.ON_MESSAGE_LIST_SCROLL);
this.fetchPreviousMessages(e.target.scrollTop);
},
@@ -30,7 +30,7 @@
</p>
</div>
<div class="features-item">
<h2 class="block-title text-black-900 dark:text-slate-200">
<h2 class="block-title">
<span class="emoji">💬</span>
<span class="conversation--title">{{
$t('ONBOARDING.ALL_CONVERSATION.TITLE')
@@ -41,7 +41,7 @@
</p>
</div>
<div class="features-item">
<h2 class="block-title text-black-900 dark:text-slate-200">
<h2 class="block-title">
<span class="emoji">👥</span>
{{ $t('ONBOARDING.TEAM_MEMBERS.TITLE') }}
</h2>
@@ -53,7 +53,7 @@
</router-link>
</div>
<div class="features-item">
<h2 class="block-title text-black-900 dark:text-slate-200">
<h2 class="block-title">
<span class="emoji">📥</span>{{ $t('ONBOARDING.INBOXES.TITLE') }}
</h2>
<p class="intro-body">
@@ -64,7 +64,7 @@
</router-link>
</div>
<div class="features-item">
<h2 class="block-title text-black-900 dark:text-slate-200">
<h2 class="block-title">
<span class="emoji">🔖</span>{{ $t('ONBOARDING.LABELS.TITLE') }}
</h2>
<p class="intro-body">
@@ -6,11 +6,8 @@
delay: { show: 1500, hide: 0 },
hideOnClick: true,
}"
class="shrink-0 rounded-xs inline-flex w-3 h-3 "
:class="{
'bg-red-50 text-red-500': isUrgent,
'bg-slate-50 text-slate-600': !isUrgent,
}"
class="conversation-priority-mark"
:class="{ urgent: priority === CONVERSATION_PRIORITY.URGENT }"
>
<fluent-icon
:icon="`priority-${priority.toLowerCase()}`"
@@ -44,9 +41,24 @@ export default {
`CONVERSATION.PRIORITY.OPTIONS.${this.priority.toUpperCase()}`
);
},
isUrgent() {
return this.priority === CONVERSATION_PRIORITY.URGENT;
},
},
};
</script>
<style scoped lang="scss">
.conversation-priority-mark {
align-items: center;
flex-shrink: 0;
background: var(--s-50);
border-radius: var(--border-radius-small);
color: var(--s-600);
display: inline-flex;
width: var(--space-snug);
height: var(--space-snug);
&.urgent {
background: var(--r-50);
color: var(--r-500);
}
}
</style>
@@ -178,6 +178,8 @@ import inboxMixin from 'shared/mixins/inboxMixin';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { DirectUpload } from 'activestorage';
import { frontendURL } from '../../../helper/URLHelper';
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
import { LocalStorage } from 'shared/helpers/localStorage';
import { trimContent, debounce } from '@chatwoot/utils';
import wootConstants from 'dashboard/constants/globals';
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
@@ -581,15 +583,30 @@ export default {
display_rich_content_editor: !this.showRichContentEditor,
});
},
getSavedDraftMessages() {
return LocalStorage.get(LOCAL_STORAGE_KEYS.DRAFT_MESSAGES) || {};
},
saveDraft(conversationId, replyType) {
if (this.message || this.message === '') {
const savedDraftMessages = this.getSavedDraftMessages();
const key = `draft-${conversationId}-${replyType}`;
const draftToSave = trimContent(this.message || '');
const {
[key]: currentDraft,
...restOfDraftMessages
} = savedDraftMessages;
this.$store.dispatch('draftMessages/set', {
key,
message: draftToSave,
});
const updatedDraftMessages = draftToSave
? {
...restOfDraftMessages,
[key]: draftToSave,
}
: restOfDraftMessages;
LocalStorage.set(
LOCAL_STORAGE_KEYS.DRAFT_MESSAGES,
updatedDraftMessages
);
}
},
setToDraft(conversationId, replyType) {
@@ -598,14 +615,24 @@ export default {
},
getFromDraft() {
if (this.conversationIdByRoute) {
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
this.message = this.$store.getters['draftMessages/get'](key) || '';
try {
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
const savedDraftMessages = this.getSavedDraftMessages();
this.message = `${savedDraftMessages[key] || ''}`;
} catch (error) {
this.message = '';
}
}
},
removeFromDraft() {
if (this.conversationIdByRoute) {
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
this.$store.dispatch('draftMessages/delete', { key });
const draftMessages = this.getSavedDraftMessages();
const { [key]: toBeRemoved, ...updatedDraftMessages } = draftMessages;
LocalStorage.set(
LOCAL_STORAGE_KEYS.DRAFT_MESSAGES,
updatedDraftMessages
);
}
},
handleKeyEvents(e) {
@@ -755,7 +782,6 @@ export default {
messagePayload
);
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
bus.$emit(BUS_EVENTS.MESSAGE_SENT);
this.removeFromDraft();
} catch (error) {
const errorMessage =
@@ -782,9 +808,6 @@ export default {
},
setReplyMode(mode = REPLY_EDITOR_MODES.REPLY) {
const { can_reply: canReply } = this.currentChat;
this.$store.dispatch('draftMessages/setReplyEditorMode', {
mode,
});
if (canReply || this.isAWhatsAppChannel) this.replyType = mode;
if (this.showRichContentEditor) {
if (this.isRecordingAudio) {
@@ -1053,7 +1076,7 @@ export default {
// Retrieve the email of the current conversation's sender
const conversationContact = this.currentChat?.meta?.sender?.email || '';
let cc = emailAttributes.cc ? [...emailAttributes.cc] : [];
let cc = [...emailAttributes.cc] || [];
let to = [];
// there might be a situation where the current conversation will include a message from a third person,
@@ -1121,7 +1144,8 @@ export default {
}
.reply-box {
@apply border-r border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-900;
border-top: 1px solid var(--color-border);
background: white;
&.is-private {
background: var(--y-50);
@@ -254,6 +254,8 @@ export default {
</script>
<style lang="scss" scoped>
@import '~dashboard/assets/scss/woot';
.right {
.message-text--metadata {
align-items: center;
@@ -317,6 +319,10 @@ export default {
text-align: center;
font-size: var(--font-size-micro);
margin-left: 0;
@include breakpoint(xlarge up) {
margin-left: var(--space-small);
}
}
}
}
@@ -10,15 +10,15 @@
<letter v-else class="text-content" :html="message" />
<button
v-if="showQuoteToggle"
class="text-slate-300 dark:text-slate-300 cursor-pointer text-xs py-1"
class="quoted-text--button"
@click="toggleQuotedContent"
>
<span v-if="showQuotedContent" class="flex items-center gap-0.5">
<fluent-icon icon="chevron-up" size="16" />
<span v-if="showQuotedContent">
<fluent-icon icon="chevron-up" class="fluent-icon" size="16" />
{{ $t('CHAT_LIST.HIDE_QUOTED_TEXT') }}
</span>
<span v-else class="flex items-center gap-0.5">
<fluent-icon icon="chevron-down" size="16" />
<span v-else>
<fluent-icon icon="chevron-down" class="fluent-icon" size="16" />
{{ $t('CHAT_LIST.SHOW_QUOTED_TEXT') }}
</span>
</button>
@@ -104,13 +104,25 @@ export default {
.show--quoted {
blockquote {
@apply block;
display: block;
}
}
.hide--quoted {
blockquote {
@apply hidden;
display: none;
}
}
.quoted-text--button {
color: var(--s-400);
cursor: pointer;
font-size: var(--font-size-mini);
padding-bottom: var(--space-small);
padding-top: var(--space-small);
.fluent-icon {
margin-bottom: var(--space-minus-smaller);
}
}
</style>
@@ -1,275 +0,0 @@
<template>
<li
v-if="shouldShowSuggestions"
class="label-suggestion right"
@mouseover="isHovered = true"
@mouseleave="isHovered = false"
>
<div class="wrap">
<div class="label-suggestion--container">
<h6 class="label-suggestion--title">Suggested labels</h6>
<div v-if="!fetchingSuggestions" class="label-suggestion--options">
<button
v-for="label in preparedLabels"
:key="label.title"
v-tooltip.top="{
content: selectedLabels.includes(label.title)
? $t('LABEL_MGMT.SUGGESTIONS.TOOLTIP.DESELECT')
: labelTooltip,
delay: { show: 600, hide: 0 },
hideOnClick: true,
}"
class="label-suggestion--option"
@click="pushOrAddLabel(label.title)"
>
<woot-label
variant="dashed"
v-bind="label"
:bg-color="
selectedLabels.includes(label.title) ? 'var(--w-100)' : ''
"
/>
</button>
<woot-button
v-if="preparedLabels.length === 1"
v-tooltip.top="{
content: $t('LABEL_MGMT.SUGGESTIONS.TOOLTIP.DISMISS'),
delay: { show: 600, hide: 0 },
hideOnClick: true,
}"
variant="smooth"
:color-scheme="isHovered ? 'alert' : 'primary'"
class="label--add"
icon="dismiss"
size="tiny"
@click="dismissSuggestions"
/>
</div>
<div v-if="preparedLabels.length > 1">
<woot-button
:variant="selectedLabels.length === 0 ? 'smooth' : ''"
class="label--add"
icon="add"
size="tiny"
@click="addAllLabels"
>
{{ addButtonText }}
</woot-button>
<woot-button
v-tooltip.top="{
content: $t('LABEL_MGMT.SUGGESTIONS.TOOLTIP.DISMISS'),
delay: { show: 600, hide: 0 },
hideOnClick: true,
}"
:color-scheme="isHovered ? 'alert' : 'primary'"
variant="smooth"
class="label--add"
icon="dismiss"
size="tiny"
@click="dismissSuggestions"
/>
</div>
</div>
<div class="sender--info has-tooltip" data-original-title="null">
<woot-thumbnail
v-tooltip.top="{
content: $t('LABEL_MGMT.SUGGESTIONS.POWERED_BY'),
delay: { show: 600, hide: 0 },
hideOnClick: true,
}"
size="16px"
>
<avatar class="user-thumbnail thumbnail-rounded">
<fluent-icon class="chatwoot-ai-icon" icon="chatwoot-ai" />
</avatar>
</woot-thumbnail>
</div>
</div>
</li>
</template>
<script>
// components
import WootButton from '../../../ui/WootButton.vue';
import Avatar from '../../Avatar.vue';
import aiMixin from 'dashboard/mixins/aiMixin';
// store & api
import { mapGetters } from 'vuex';
// utils & constants
import { LocalStorage } from 'shared/helpers/localStorage';
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
import { OPEN_AI_EVENTS } from '../../../../helper/AnalyticsHelper/events';
export default {
name: 'LabelSuggestion',
components: {
Avatar,
WootButton,
},
mixins: [aiMixin],
props: {
suggestedLabels: {
type: Array,
required: true,
},
chatLabels: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
isDismissed: false,
fetchingSuggestions: false,
isHovered: false,
selectedLabels: [],
};
},
computed: {
...mapGetters({
allLabels: 'labels/getLabels',
currentAccountId: 'getCurrentAccountId',
}),
labelTooltip() {
if (this.preparedLabels.length > 1) {
return this.$t('LABEL_MGMT.SUGGESTIONS.TOOLTIP.MULTIPLE_SUGGESTION');
}
return this.$t('LABEL_MGMT.SUGGESTIONS.TOOLTIP.SINGLE_SUGGESTION');
},
addButtonText() {
if (this.selectedLabels.length === 1) {
return this.$t('LABEL_MGMT.SUGGESTIONS.ADD_SELECTED_LABEL');
}
if (this.selectedLabels.length > 1) {
return this.$t('LABEL_MGMT.SUGGESTIONS.ADD_SELECTED_LABELS');
}
return this.$t('LABEL_MGMT.SUGGESTIONS.ADD_ALL_LABELS');
},
preparedLabels() {
return this.allLabels.filter(label =>
this.suggestedLabels.includes(label.title)
);
},
shouldShowSuggestions() {
if (this.isDismissed) return false;
if (!this.isAIIntegrationEnabled) return false;
return (
!this.fetchingSuggestions &&
this.preparedLabels.length &&
this.chatLabels.length === 0
);
},
},
watch: {
conversationId: {
immediate: true,
handler() {
this.selectedLabels = [];
this.isDismissed = this.isConversationDismissed();
},
},
},
methods: {
pushOrAddLabel(label) {
if (this.preparedLabels.length === 1) {
this.addAllLabels();
return;
}
if (!this.selectedLabels.includes(label)) {
this.selectedLabels.push(label);
} else {
this.selectedLabels = this.selectedLabels.filter(l => l !== label);
}
},
dismissSuggestions() {
const dismissed = this.getDismissedConversations(this.currentAccountId);
dismissed[this.currentAccountId].push(this.conversationId);
LocalStorage.set(
LOCAL_STORAGE_KEYS.DISMISSED_LABEL_SUGGESTIONS,
dismissed
);
// dismiss this once the values are set
this.isDismissed = true;
this.trackLabelEvent(OPEN_AI_EVENTS.DISMISS_LABEL_SUGGESTION);
},
isConversationDismissed() {
const dismissed = this.getDismissedConversations(this.currentAccountId);
return dismissed[this.currentAccountId].includes(this.conversationId);
},
addAllLabels() {
let labelsToAdd = this.selectedLabels;
if (!labelsToAdd.length) {
labelsToAdd = this.preparedLabels.map(label => label.title);
}
this.$store.dispatch('conversationLabels/update', {
conversationId: this.conversationId,
labels: labelsToAdd,
});
this.trackLabelEvent(OPEN_AI_EVENTS.LABEL_SUGGESTION_APPLIED);
},
trackLabelEvent(event) {
const payload = {
conversationId: this.conversationId,
account: this.currentAccountId,
suggestions: this.suggestedLabels,
labelsApplied: this.selectedLabels.length
? this.selectedLabels
: this.suggestedLabels,
};
this.$track(event, payload);
},
},
};
</script>
<style scoped lang="scss">
.wrap {
display: flex;
}
.label-suggestion {
flex-direction: row;
justify-content: flex-end;
margin-top: var(--space-normal);
.label-suggestion--container {
max-width: 300px;
}
.label-suggestion--options {
text-align: right;
display: flex;
align-items: center;
gap: var(--space-micro);
button.label-suggestion--option {
.label {
cursor: pointer;
margin-bottom: 0;
}
}
}
.chatwoot-ai-icon {
height: var(--font-size-mini);
width: var(--font-size-mini);
}
.label-suggestion--title {
color: var(--b-600);
margin-top: var(--space-micro);
font-size: var(--font-size-micro);
}
}
</style>
@@ -16,7 +16,10 @@ localVue.use(VTooltip);
localVue.component('fluent-icon', FluentIcon);
localVue.component('woot-button', Button);
const i18nConfig = new VueI18n({ locale: 'en', messages: i18n });
const i18nConfig = new VueI18n({
locale: 'en',
messages: i18n,
});
describe('MoveActions', () => {
let currentChat = { id: 8, muted: false };
@@ -44,22 +47,25 @@ describe('MoveActions', () => {
unmuteConversation = jest.fn(() => Promise.resolve());
modules = {
conversations: { actions: { muteConversation, unmuteConversation } },
conversations: {
actions: {
muteConversation,
unmuteConversation,
},
},
};
getters = { getSelectedChat: () => currentChat };
getters = {
getSelectedChat: () => currentChat,
};
store = new Vuex.Store({ state, modules, getters });
moreActions = mount(MoreActions, {
store,
localVue,
i18n: i18nConfig,
stubs: {
WootModal: { template: '<div><slot/> </div>' },
WootModalHeader: { template: '<div><slot/> </div>' },
},
store = new Vuex.Store({
state,
modules,
getters,
});
moreActions = mount(MoreActions, { store, localVue, i18n: i18nConfig });
});
describe('muting discussion', () => {
@@ -2,6 +2,4 @@ export const LOCAL_STORAGE_KEYS = {
DISMISSED_UPDATES: 'dismissedUpdates',
WIDGET_BUILDER: 'widgetBubble_',
DRAFT_MESSAGES: 'draftMessages',
COLOR_SCHEME: 'color_scheme',
DISMISSED_LABEL_SUGGESTIONS: 'dismissedLabelSuggestions',
};
@@ -81,12 +81,4 @@ export const OPEN_AI_EVENTS = Object.freeze({
SUMMARIZE: 'OpenAI: Used summarize',
REPLY_SUGGESTION: 'OpenAI: Used reply suggestion',
REPHRASE: 'OpenAI: Used rephrase',
FIX_SPELLING_AND_GRAMMAR: 'OpenAI: Used fix spelling and grammar',
SHORTEN: 'OpenAI: Used shorten',
EXPAND: 'OpenAI: Used expand',
MAKE_FRIENDLY: 'OpenAI: Used make friendly',
MAKE_FORMAL: 'OpenAI: Used make formal',
SIMPLIFY: 'OpenAI: Used simplify',
APPLY_LABEL_SUGGESTION: 'OpenAI: Apply label from suggestion',
DISMISS_LABEL_SUGGESTION: 'OpenAI: Dismiss label suggestions',
});
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -235,7 +235,7 @@
},
"INBOXES": {
"TITLE": "Connect Inboxes",
"DESCRIPTION": "Connect various channels through which your customers would be talking to you. It can be a website live-chat, your Facebook page or even your WhatsApp number.",
"DESCRIPTION": "Connect various channels through which your customers would be talking to you. It can be a website live-chat, your Facebook or Twitter page or even your WhatsApp number.",
"NEW_LINK": "Click here to create an inbox"
},
"LABELS": {
@@ -111,17 +111,6 @@
"PLACEHOLDER": "Please enter your Twilio Account SID",
"ERROR": "This field is required"
},
"API_KEY": {
"USE_API_KEY": "Use API Key Authentication",
"LABEL": "API Key SID",
"PLACEHOLDER": "Please enter your API Key SID",
"ERROR": "This field is required"
},
"API_KEY_SECRET": {
"LABEL": "API Key Secret",
"PLACEHOLDER": "Please enter your API Key Secret",
"ERROR": "This field is required"
},
"MESSAGING_SERVICE_SID": {
"LABEL": "Messaging Service SID",
"PLACEHOLDER": "Please enter your Twilio Messaging Service SID",
@@ -340,7 +329,7 @@
},
"AUTH": {
"TITLE": "Choose a channel",
"DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
"DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
},
"AGENTS": {
"TITLE": "Agents",
@@ -402,25 +391,6 @@
"ENABLED": "Enabled",
"DISABLED": "Disabled"
},
"SENDER_NAME_SECTION": {
"TITLE": "Sender name",
"SUB_TEXT": "Select the name shown to the your customer when they receive emails from your agents.",
"FOR_EG": "For eg:",
"FRIENDLY": {
"TITLE": "Friendly",
"FROM": "from",
"SUBTITLE": "Add the name of the agent who sent the reply in the sender name to make it friendly."
},
"PROFESSIONAL": {
"TITLE": "Professional",
"SUBTITLE": "Use only the configured business name as the sender name in the email header."
},
"BUSINESS_NAME": {
"BUTTON_TEXT": "+ Configure your business name",
"PLACEHOLDER": "Enter your business name",
"SAVE_BUTTON_TEXT": "Save"
}
},
"ALLOW_MESSAGES_AFTER_RESOLVED": {
"ENABLED": "Enabled",
"DISABLED": "Disabled"
@@ -484,9 +454,7 @@
"ENABLE_EMAIL_COLLECT_BOX_SUB_TEXT": "Enable or disable email collect box on new conversation",
"AUTO_ASSIGNMENT": "Enable auto assignment",
"ENABLE_CSAT": "Enable CSAT",
"SENDER_NAME_SECTION": "Enable Agent Name in Email",
"ENABLE_CSAT_SUB_TEXT": "Enable/Disable CSAT(Customer satisfaction) survey after resolving a conversation",
"SENDER_NAME_SECTION_TEXT": "Enable/Disable showing Agent's name in email, if disabled it will show business name",
"ENABLE_CONTINUITY_VIA_EMAIL": "Enable conversation continuity via email",
"ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT": "Conversations will continue over email if the contact email address is available.",
"LOCK_TO_SINGLE_CONVERSATION": "Lock to single conversation",
@@ -84,27 +84,6 @@
},
"OPEN_AI": {
"AI_ASSIST": "AI Assist",
"WITH_AI": " %{option} with AI ",
"OPTIONS": {
"REPLY_SUGGESTION": "Reply Suggestion",
"SUMMARIZE": "Summarize",
"REPHRASE": "Improve Writing",
"FIX_SPELLING_GRAMMAR": "Fix Spelling and Grammar",
"SHORTEN": "Shorten",
"EXPAND": "Expand",
"MAKE_FRIENDLY": "Change message tone to friendly",
"MAKE_FORMAL": "Use formal tone",
"SIMPLIFY": "Simplify"
},
"ASSISTANCE_MODAL": {
"DRAFT_TITLE": "Draft content",
"GENERATED_TITLE": "Generated content",
"AI_WRITING": "AI is writing",
"BUTTONS": {
"APPLY": "Use this suggestion",
"CANCEL": "Cancel"
}
},
"TITLE": "Improve With AI",
"SUMMARY_TITLE": "Summary with AI",
"REPLY_TITLE": "Reply suggestion with AI",
@@ -34,19 +34,6 @@
"DELETE": "Delete",
"CANCEL": "Cancel"
},
"SUGGESTIONS": {
"TOOLTIP": {
"SINGLE_SUGGESTION": "Add label to conversation",
"MULTIPLE_SUGGESTION": "Select this label",
"DESELECT": "Deselect label",
"DISMISS": "Dismiss suggestion"
},
"POWERED_BY": "Chatwoot AI",
"DISMISS": "Dismiss",
"ADD_SELECTED_LABELS": "Add selected labels",
"ADD_SELECTED_LABEL": "Add selected label",
"ADD_ALL_LABELS": "Add all labels"
},
"ADD": {
"TITLE": "Add label",
"DESC": "Labels let you group the conversations together.",
@@ -3,8 +3,7 @@
"TITLE": "Login to Chatwoot",
"EMAIL": {
"LABEL": "Email",
"PLACEHOLDER": "example@companyname.com",
"ERROR": "Please enter a valid email address"
"PLACEHOLDER": "example@companyname.com"
},
"PASSWORD": {
"LABEL": "Password",
@@ -21,7 +20,7 @@
"NO_ACCOUNT_FOUND": "We couldn't find an account for your email address."
},
"FORGOT_PASSWORD": "Forgot your password?",
"CREATE_NEW_ACCOUNT": "Create a new account",
"CREATE_NEW_ACCOUNT": "Create new account",
"SUBMIT": "Login"
}
}
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +0,0 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -1,65 +0,0 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
};
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};
@@ -2,7 +2,6 @@ import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
@@ -23,7 +22,6 @@ import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
@@ -35,7 +33,6 @@ export default {
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
@@ -56,7 +53,6 @@ export default {
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
@@ -1,65 +1,55 @@
import advancedFilters from './advancedFilters.json';
import agentBots from './agentBots.json';
import agentMgmt from './agentMgmt.json';
import attributesMgmt from './attributesMgmt.json';
import auditLogs from './auditLogs.json';
import automation from './automation.json';
import bulkActions from './bulkActions.json';
import campaign from './campaign.json';
import cannedMgmt from './cannedMgmt.json';
import chatlist from './chatlist.json';
import contact from './contact.json';
import contactFilters from './contactFilters.json';
import conversation from './conversation.json';
import csatMgmt from './csatMgmt.json';
import emoji from './emoji.json';
import generalSettings from './generalSettings.json';
import helpCenter from './helpCenter.json';
import inboxMgmt from './inboxMgmt.json';
import integrationApps from './integrationApps.json';
import integrations from './integrations.json';
import labelsMgmt from './labelsMgmt.json';
import login from './login.json';
import macros from './macros.json';
import report from './report.json';
import resetPassword from './resetPassword.json';
import search from './search.json';
import setNewPassword from './setNewPassword.json';
import settings from './settings.json';
import signup from './signup.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import { default as _advancedFilters } from './advancedFilters.json';
import { default as _agentMgmt } from './agentMgmt.json';
import { default as _attributesMgmt } from './attributesMgmt.json';
import { default as _automation } from './automation.json';
import { default as _bulkActions } from './bulkActions.json';
import { default as _campaign } from './campaign.json';
import { default as _cannedMgmt } from './cannedMgmt.json';
import { default as _chatlist } from './chatlist.json';
import { default as _contact } from './contact.json';
import { default as _contactFilters } from './contactFilters.json';
import { default as _conversation } from './conversation.json';
import { default as _csatMgmtMgmt } from './csatMgmt.json';
import { default as _generalSettings } from './generalSettings.json';
import { default as _inboxMgmt } from './inboxMgmt.json';
import { default as _integrationApps } from './integrationApps.json';
import { default as _integrations } from './integrations.json';
import { default as _labelsMgmt } from './labelsMgmt.json';
import { default as _login } from './login.json';
import { default as _report } from './report.json';
import { default as _resetPassword } from './resetPassword.json';
import { default as _setNewPassword } from './setNewPassword.json';
import { default as _settings } from './settings.json';
import { default as _signup } from './signup.json';
import { default as _teamsSettings } from './teamsSettings.json';
import { default as _whatsappTemplates } from './whatsappTemplates.json';
import { default as _helpCenter } from './helpCenter.json';
export default {
...advancedFilters,
...agentBots,
...agentMgmt,
...attributesMgmt,
...auditLogs,
...automation,
...bulkActions,
...campaign,
...cannedMgmt,
...chatlist,
...contact,
...contactFilters,
...conversation,
...csatMgmt,
...emoji,
...generalSettings,
...helpCenter,
...inboxMgmt,
...integrationApps,
...integrations,
...labelsMgmt,
...login,
...macros,
...report,
...resetPassword,
...search,
...setNewPassword,
...settings,
...signup,
...teamsSettings,
...whatsappTemplates,
..._advancedFilters,
..._agentMgmt,
..._attributesMgmt,
..._automation,
..._campaign,
..._cannedMgmt,
..._chatlist,
..._contact,
..._contactFilters,
..._conversation,
..._csatMgmtMgmt,
..._generalSettings,
..._inboxMgmt,
..._integrationApps,
..._integrations,
..._labelsMgmt,
..._login,
..._report,
..._resetPassword,
..._setNewPassword,
..._settings,
..._signup,
..._teamsSettings,
..._whatsappTemplates,
..._bulkActions,
..._helpCenter,
};

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