Compare commits

..
Author SHA1 Message Date
Sivin VargheseandGitHub 43e525d587 Merge branch 'develop' into fix/cw-4085 2025-09-25 18:19:27 +05:30
Muhsin KelothandGitHub cd2c58726f fix: Ensure message is always present in conversation_created webhook for WhatsApp attachment messages (#12507)
Fixes https://github.com/chatwoot/chatwoot/issues/11753 and
https://github.com/chatwoot/chatwoot/issues/12442

**Problem**
When a WhatsApp conversation started with a media message, the
conversation created webhook would sometimes fire before the message and
its relationships were fully committed to the database. This resulted in
the message being missing
from the webhook payload, breaking external automations that rely on
this field.

**Solution**

Added `ActiveRecord::Base.transaction` wrapper around the core message
processing operations in `Whatsapp::IncomingMessageBaseService` to
ensure atomic execution:

- `set_conversation` (creates conversation)
- `create_messages` (creates message with account_id)
- `clear_message_source_id_from_redis` (cleanup)

Now the webhook only triggers after all related data is fully persisted,
guaranteeing message availability.
2025-09-25 18:19:09 +05:30
iamsivin e1a3e8751c chore: Clean up 2025-09-25 18:09:05 +05:30
iamsivin c582f79b2c chore: Minor fix 2025-09-25 17:52:41 +05:30
iamsivin c18f7f0131 chore: Fix broken styles 2025-09-25 17:14:56 +05:30
Sivin VargheseandGitHub b98bb3f143 Merge branch 'develop' into fix/cw-4085 2025-09-25 17:04:19 +05:30
03d0688cc2 chore: Update translations (#12519)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-09-25 15:59:03 +05:30
Shivam MishraandGitHub b75ea7a762 feat: Use resolved contacts as base relation for filtering (#12520)
This PR has two changes to speed up contact filtering

### Updated Base Relation

Update the `base_relation` to use resolved contacts scope to improve
perf when filtering conversations. This narrows the search space
drastically, and what is usually a sequential scan becomes a index scan
for that `account_id`

ref: https://github.com/chatwoot/chatwoot/pull/9347
ref: https://github.com/chatwoot/chatwoot/pull/7175/

Result: https://explain.dalibo.com/plan/c8a8gb17f0275fgf#plan


## Selective filtering in Compose New Conversation

We also cost of filtering in compose new conversation dialog by reducing
the search space based on the search candidate. For instance, a search
term that obviously can’t be a phone, we exclude that from the filter.
Similarly we skip name lookups for email-shaped queries.

Removing the phone number took the query times from 50 seconds to under
1 seconds

### Comparison

1. Only Email: https://explain.dalibo.com/plan/h91a6844a4438a6a 
2. Email + Name: https://explain.dalibo.com/plan/beg3aah05ch9ade0
3. Email + Name + Phone:
https://explain.dalibo.com/plan/c8a8gb17f0275fgf
2025-09-25 15:26:44 +05:30
Muhsin KelothandGitHub 2e96fcea5c Merge branch 'develop' into fix/cw-4085 2025-09-09 20:57:27 +05:30
Muhsin KelothandGitHub 3e2b65abe4 Merge branch 'develop' into fix/cw-4085 2025-09-09 12:26:35 +05:30
Sivin VargheseandGitHub b934fa237b Merge branch 'develop' into fix/cw-4085 2025-08-20 20:06:18 +05:30
Shivam MishraandGitHub 6a3f61438d Merge branch 'develop' into fix/cw-4085 2025-08-20 17:54:32 +05:30
Shivam Mishra 0c1d16f324 fix: imports 2025-08-14 17:16:35 +05:30
Shivam MishraandGitHub c6642527e2 Merge branch 'develop' into fix/cw-4085 2025-08-14 17:13:25 +05:30
PranavandGitHub c0e398c642 Merge branch 'develop' into fix/cw-4085 2025-05-01 16:41:44 -07:00
PranavandGitHub ae5c71ffa1 Merge branch 'develop' into fix/cw-4085 2025-03-31 18:18:57 -07:00
Shivam MishraandGitHub 086a4d6254 Merge branch 'develop' into fix/cw-4085 2025-03-20 10:22:49 +05:30
Muhsin KelothandGitHub c49161d0a9 Merge branch 'develop' into fix/cw-4085 2025-03-18 15:22:46 +05:30
Shivam Mishra ce3f415002 fix: padding in rtl 2025-03-18 14:46:15 +05:30
Shivam Mishra cce7216c98 fix: unread message label i18n formatting 2025-03-18 14:45:09 +05:30
Shivam Mishra e93a61033f chore: remove unused keys 2025-03-18 14:38:12 +05:30
Shivam Mishra 2bd20f229d feat: use better counts 2025-03-18 14:37:58 +05:30
Shivam Mishra ebd5b84233 feat: enable new scroll behavior with a feature flag 2025-03-18 14:32:58 +05:30
Shivam Mishra 1dca649cd6 feat: add feature flag 2025-03-18 14:32:39 +05:30
Shivam Mishra 13a59a3901 chore: revert "feat: remove old message bubble UI"
This reverts commit 8640f1cfd2.
2025-03-18 14:14:49 +05:30
Shivam MishraandGitHub a036a1dbd8 Merge branch 'develop' into fix/cw-4085 2025-03-18 14:09:09 +05:30
Muhsin KelothandGitHub 57edf4ece5 Merge branch 'develop' into fix/cw-4085 2025-03-18 13:28:44 +05:30
Shivam Mishra 8640f1cfd2 feat: remove old message bubble UI 2025-03-17 18:21:11 +05:30
Shivam Mishra 8a09f28de8 test: Typing Indicator component 2025-03-17 18:07:42 +05:30
Shivam Mishra d8d65a0273 feat: debounce the scroll check 2025-03-17 17:47:10 +05:30
Shivam Mishra 8a0dc90a29 feat: add unread indicator
This also defers the marking of a message as unread until the user has actually scrolled to it
2025-03-17 17:41:45 +05:30
Shivam Mishra 0a79e37319 fix: i18n 2025-03-17 17:19:02 +05:30
Shivam Mishra c82ab3e8db chore: revert debug changes 2025-03-17 17:08:34 +05:30
Shivam Mishra 9aee8d1a1f feat: add separate typing indicator 2025-03-17 17:03:37 +05:30
Shivam MishraandGitHub 2759954897 Merge branch 'develop' into fix/cw-4085 2025-03-17 15:53:26 +05:30
Shivam MishraandGitHub 5b6f2145e8 Merge branch 'develop' into fix/cw-4085 2025-03-06 20:10:38 +05:30
Shivam Mishra 5fca6df52d feat: more sophisticated check for scroll position 2025-02-27 20:36:53 +05:30
Shivam Mishra 5d8a37fed5 chore: update comment 2025-02-26 15:35:21 +05:30
Shivam Mishra 5ad1a9c8c7 feat: don't scroll to bottom if user has scrolled 2025-02-26 15:30:30 +05:30
Shivam Mishra 594612e278 revert: emit change 2025-02-26 15:27:53 +05:30
Shivam MishraandGitHub 038942b8fc Merge branch 'develop' into fix/cw-4085 2025-02-25 20:07:35 +05:30
Shivam Mishra f9920fff68 feat: remove scroll to the bottom 2025-02-25 15:22:33 +05:30
173 changed files with 841 additions and 243 deletions
@@ -23,29 +23,24 @@ module RequestExceptionHandler
Current.reset
end
def render_error(message, status, error = nil)
log_handled_error(error) if error
render json: { error: message }, status: status
end
def render_unauthorized(message)
render_error(message, :unauthorized)
render json: { error: message }, status: :unauthorized
end
def render_not_found_error(message)
render_error(message, :not_found)
render json: { error: message }, status: :not_found
end
def render_could_not_create_error(message)
render_error(message, :unprocessable_entity)
render json: { error: message }, status: :unprocessable_entity
end
def render_payment_required(message)
render_error(message, :payment_required)
render json: { error: message }, status: :payment_required
end
def render_internal_server_error(message)
render_error(message, :internal_server_error)
render json: { error: message }, status: :internal_server_error
end
def render_record_invalid(exception)
@@ -62,23 +57,6 @@ module RequestExceptionHandler
end
def log_handled_error(exception)
return unless exception
logger.info("Handled error: #{exception.inspect}")
report_to_apms(exception)
end
def report_to_apms(exception)
apm_reporters = {
'NewRelic::Agent' => -> { ::NewRelic::Agent.notice_error(exception) },
'Datadog::Tracing' => -> { ::Datadog::Tracing.active_trace&.set_error(exception) },
'ElasticAPM' => -> { ::ElasticAPM.report(exception) },
'ScoutApm::Error' => -> { ::ScoutApm::Error.capture(exception) },
'Sentry' => -> { ::Sentry.capture_exception(exception) }
}
apm_reporters.each do |module_name, reporter|
reporter.call if Object.const_defined?(module_name)
end
end
end
@@ -0,0 +1,58 @@
<script setup>
import { useFunctionGetter, useMapGetter } from 'dashboard/composables/store';
import { useI18n } from 'vue-i18n';
import { computed } from 'vue';
const { t } = useI18n();
const getTypingUsersText = (users = []) => {
const count = users.length;
const [firstUser, secondUser] = users;
if (count === 1) {
return t('TYPING.ONE', { user: firstUser.name });
}
if (count === 2) {
return t('TYPING.TWO', {
user: firstUser.name,
secondUser: secondUser.name,
});
}
return t('TYPING.MULTIPLE', { user: firstUser.name, count: count - 1 });
};
const currentChat = useMapGetter('getSelectedChat');
const typingUsersList = useFunctionGetter(
'conversationTypingStatus/getUserList',
currentChat.value.id
);
const isAnyoneTyping = computed(() => {
return typingUsersList.value.length !== 0;
});
const typingUserNames = computed(() => {
if (typingUsersList.value.length) {
return getTypingUsersText(typingUsersList.value);
}
return '';
});
</script>
<template>
<div v-show="isAnyoneTyping">
<div
v-if="isAnyoneTyping"
class="flex py-1.5 ltr:pr-2 ltr:pl-3 rtl:pl-2 rtl:pr-3 rounded-full shadow-sm border border-n-weak bg-n-solid-2 text-xs font-semibold my-2.5 mx-auto"
>
{{ typingUserNames }}
<img
class="w-6 ltr:ml-2 rtl:mr-2"
src="assets/images/typing.gif"
alt="Someone is typing"
/>
</div>
</div>
</template>
@@ -0,0 +1,44 @@
<script setup>
import { vIntersectionObserver } from '@vueuse/components';
import { computed } from 'vue';
const props = defineProps({
label: {
type: String,
required: true,
},
variant: {
type: String,
default: 'primary',
validator: value => ['primary', 'secondary'].includes(value),
},
});
const emit = defineEmits(['click', 'intersect']);
function onIntersectionObserver([entry]) {
const isVisible = entry?.isIntersecting || false;
emit('intersect', isVisible);
}
const classToApply = computed(() => {
return {
'bg-n-solid-blue text-n-blue-text': props.variant === 'primary',
'shadow-sm border border-n-weak bg-n-solid-2 text-n-slate-11':
props.variant === 'secondary',
};
});
</script>
<template>
<div>
<div
v-intersection-observer="onIntersectionObserver"
:class="classToApply"
class="flex py-1.5 px-3 rounded-full text-xs font-semibold my-2.5 mx-auto"
@click="emit('click')"
>
{{ label }}
</div>
</div>
</template>
@@ -0,0 +1,75 @@
import { mount } from '@vue/test-utils';
import { createStore } from 'vuex';
import { nextTick } from 'vue';
import TypingIndicator from '../TypingIndicator.vue';
const createComponent = ({ typingUsers = [] } = {}) => {
const store = createStore({
modules: {
conversations: {
getters: {
getSelectedChat: () => ({ id: 1 }),
},
},
conversationTypingStatus: {
namespaced: true,
getters: {
getUserList: () => chatId => {
// Ensure we're returning the typing users for the correct chat
if (chatId === 1) {
return typingUsers;
}
return [];
},
},
},
},
});
return mount(TypingIndicator, {
global: {
plugins: [store],
},
});
};
describe('TypingIndicator', () => {
it('should not be visible when no one is typing', () => {
const wrapper = createComponent();
expect(wrapper.isVisible()).toBe(false);
});
it('should display the correct message when one user is typing', async () => {
const wrapper = createComponent({
typingUsers: [{ id: 1, name: 'John' }],
});
await nextTick();
expect(wrapper.isVisible()).toBe(true);
expect(wrapper.text()).toContain('John is typing');
});
it('should display the correct message when two users are typing', async () => {
const wrapper = createComponent({
typingUsers: [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
],
});
await nextTick();
expect(wrapper.isVisible()).toBe(true);
expect(wrapper.text()).toContain('John and Jane are typing');
});
it('should display the correct message when more than two users are typing', async () => {
const wrapper = createComponent({
typingUsers: [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 3, name: 'Bob' },
],
});
await nextTick();
expect(wrapper.isVisible()).toBe(true);
expect(wrapper.text()).toContain('John and 2 others are typing');
});
});
@@ -148,10 +148,21 @@ const isAnyDropdownActive = computed(() => {
const handleContactSearch = value => {
showContactsDropdown.value = true;
emit('searchContacts', {
keys: ['email', 'phone_number', 'name'],
query: value,
const query = typeof value === 'string' ? value.trim() : '';
const hasAlphabet = Array.from(query).some(char => {
const lower = char.toLowerCase();
const upper = char.toUpperCase();
return lower !== upper;
});
const isEmailLike = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(query);
const keys = ['email', 'phone_number', 'name'].filter(key => {
if (key === 'phone_number' && hasAlphabet) return false;
if (key === 'name' && isEmailLike) return false;
return true;
});
emit('searchContacts', { keys, query: value });
};
const handleDropdownUpdate = (type, value) => {
@@ -1,6 +1,7 @@
<script>
import { ref, provide } from 'vue';
// composable
import { useMapGetter } from 'dashboard/composables/store.js';
import { useConfig } from 'dashboard/composables/useConfig';
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
import { useAI } from 'dashboard/composables/useAI';
@@ -8,6 +9,8 @@ import { useSnakeCase } from 'dashboard/composables/useTransformKeys';
// components
import ReplyBox from './ReplyBox.vue';
import TypingIndicator from 'next/Conversation/Chips/TypingIndicator.vue';
import UnreadIndicator from 'next/Conversation/Chips/UnreadIndicator.vue';
import MessageList from 'next/message/MessageList.vue';
import ConversationLabelSuggestion from './conversation/LabelSuggestion.vue';
import Banner from 'dashboard/components/ui/Banner.vue';
@@ -21,7 +24,6 @@ import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
// utils
import { emitter } from 'shared/helpers/mitt';
import { getTypingUsersText } from '../../../helper/commons';
import { calculateScrollTop } from './helpers/scrollTopCalculationHelper';
import { LocalStorage } from 'shared/helpers/localStorage';
import {
@@ -29,6 +31,7 @@ import {
getReadMessages,
getUnreadMessages,
} from 'dashboard/helper/conversationHelper';
import { debounce } from '@chatwoot/utils';
// constants
import { BUS_EVENTS } from 'shared/constants/busEvents';
@@ -36,11 +39,14 @@ import { REPLY_POLICY } from 'shared/constants/links';
import wootConstants from 'dashboard/constants/globals';
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
import { INBOX_TYPES } from 'dashboard/helper/inbox';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
export default {
components: {
MessageList,
ReplyBox,
TypingIndicator,
UnreadIndicator,
Banner,
ConversationLabelSuggestion,
Spinner,
@@ -68,6 +74,16 @@ export default {
fetchLabelSuggestions,
} = useAI();
const currentAccountId = useMapGetter('getCurrentAccountId');
const isFeatureEnabledonAccount = useMapGetter(
'accounts/isFeatureEnabledonAccount'
);
const useNewScrollBehavior = isFeatureEnabledonAccount.value(
currentAccountId.value,
FEATURE_FLAGS.CHAT_PRESERVE_USER_SCROLL
);
provide('contextMenuElementTarget', conversationPanelRef);
return {
@@ -77,12 +93,16 @@ export default {
isLabelSuggestionFeatureEnabled,
fetchIntegrationsIfRequired,
fetchLabelSuggestions,
useNewScrollBehavior,
conversationPanelRef,
};
},
data() {
return {
isLoadingPrevious: true,
// this is only used to show a chip if the user has scrolled far enough
// we will hide this the moment the user reaches the start of the unread messages
showFloatingUnreadIndicator: false,
heightBeforeLoad: null,
conversationPanel: null,
hasUserScrolled: false,
@@ -116,25 +136,6 @@ export default {
inbox() {
return this.$store.getters['inboxes/getInbox'](this.inboxId);
},
typingUsersList() {
const userList = this.$store.getters[
'conversationTypingStatus/getUserList'
](this.currentChat.id);
return userList;
},
isAnyoneTyping() {
const userList = this.typingUsersList;
return userList.length !== 0;
},
typingUserNames() {
const userList = this.typingUsersList;
if (this.isAnyoneTyping) {
const [i18nKey, params] = getTypingUsersText(userList);
return this.$t(i18nKey, params);
}
return '';
},
getMessages() {
const messages = this.currentChat.messages || [];
if (this.isAWhatsAppChannel) {
@@ -227,13 +228,19 @@ export default {
return this.currentChat.unread_count || 0;
},
unreadMessageLabel() {
const count =
this.unreadMessageCount > 9 ? '9+' : this.unreadMessageCount;
const label =
this.unreadMessageCount > 1
? 'CONVERSATION.UNREAD_MESSAGES'
: 'CONVERSATION.UNREAD_MESSAGE';
return `${count} ${this.$t(label)}`;
if (this.unreadMessageCount <= 1) {
return this.$t('CONVERSATION.SINGLE_UNREAD_MESSAGE');
}
if (this.unreadMessageCount > 9) {
return this.$t('CONVERSATION.UNREAD_COUNT_OVERFLOW');
}
const formatter = new Intl.NumberFormat(navigator.language);
return this.$t('CONVERSATION.UNREAD_COUNT', {
count: formatter.format(this.unreadMessageCount),
});
},
inboxSupportsReplyTo() {
const incoming = this.inboxHasFeature(INBOX_FEATURES.REPLY_TO);
@@ -271,6 +278,7 @@ export default {
this.addScrollListener();
this.fetchAllAttachmentsFromCurrentChat();
this.fetchSuggestions();
this.debouncedMarkReadIfRequired = debounce(this.markReadIfRequired, 100);
},
unmounted() {
@@ -339,10 +347,20 @@ export default {
messageElement.scrollIntoView({ behavior: 'smooth' });
this.fetchPreviousMessages();
} else {
this.scrollToBottom();
// if there is not message id, that means, we are meant to scroll to bottom
// this is done when we get a new unread message
// however if the user has scrolled away to a certain degree
// we should preserve the scroll position
this.scrollToBottomIfNotScrolled();
}
});
this.makeMessagesRead();
// when new scroll behavior is activated
// we don't mark the message as read automatically
// since we preserve the user scroll position
if (!this.useNewScrollBehavior) {
this.makeMessagesRead();
}
},
addScrollListener() {
this.conversationPanel = this.$el.querySelector('.conversation-panel');
@@ -354,7 +372,39 @@ export default {
removeScrollListener() {
this.conversationPanel.removeEventListener('scroll', this.handleScroll);
},
isNearBottom(offset = 200) {
// In case the user has already scrolled to a point in the message, we should
// not scroll to the bottom against the intent of the user.
const clientHeight = this.conversationPanel.clientHeight;
const scrollHeight = this.conversationPanel.scrollHeight;
const scrollTop = this.conversationPanel.scrollTop;
// when scrolled at the bottom completely for any element
// scrollHeight = clientHeight + scrollTop
// so if we wanna see if the user has scrolled but not significantly
// we need to see if scrollTop > scrollHeight - clientHeight
// if the user is at the bottom or close to the bottom, we can skip scrolling
// we add a 200px margin, this has enough space to accomodate new messages
// while also resetting position to the bottom if the user has scrolled but not significantly
return scrollTop > scrollHeight - clientHeight - offset;
},
scrollToBottomIfNotScrolled() {
// we disable this behavior with a feature flag
// once the feature has been stable for a while, we can remove this guard
if (this.useNewScrollBehavior) {
const isNearBottom = this.isNearBottom();
if (this.hasUserScrolled && !isNearBottom) {
this.showFloatingUnreadIndicator = true;
return;
}
}
this.scrollToBottom();
},
scrollToBottom() {
this.showFloatingUnreadIndicator = false;
this.isProgrammaticScroll = true;
let relevantMessages = [];
@@ -381,6 +431,10 @@ export default {
).slice(-1);
}
if (this.unreadMessageCount !== 0 && this.useNewScrollBehavior) {
this.makeMessagesRead();
}
this.conversationPanel.scrollTop = calculateScrollTop(
this.conversationPanel.scrollHeight,
this.$el.scrollHeight,
@@ -431,13 +485,50 @@ export default {
} else {
this.hasUserScrolled = true;
}
// in case the user has scrolled to the bottom manually
// we trigger the makeMessagesRead method if there are unread messages
//
// for now this is activated by a feature flag only
if (this.useNewScrollBehavior) {
this.debouncedMarkReadIfRequired();
}
emitter.emit(BUS_EVENTS.ON_MESSAGE_LIST_SCROLL);
this.fetchPreviousMessages(e.target.scrollTop);
},
markReadIfRequired() {
if (this.isNearBottom(50) && this.unreadMessageCount !== 0) {
this.makeMessagesRead();
}
},
makeMessagesRead() {
this.$store.dispatch('markMessagesRead', { id: this.currentChat.id });
},
onUnreadBadgeIntersect(visible) {
if (!this.useNewScrollBehavior) return;
if (visible) {
// when the fixed unread badge is visible
// we trigger the makeMessagesRead method
this.showFloatingUnreadIndicator = false;
this.makeMessagesRead();
}
},
getInReplyToMessage(parentMessage) {
if (!parentMessage) return {};
const inReplyToMessageId = parentMessage.content_attributes?.in_reply_to;
if (!inReplyToMessageId) return {};
return this.currentChat?.messages.find(message => {
if (message.id === inReplyToMessageId) {
return true;
}
return false;
});
},
async handleMessageRetry(message) {
if (!message) return;
const payload = useSnakeCase(message);
@@ -486,13 +577,13 @@ export default {
<template #unreadBadge>
<li
v-show="unreadMessageCount != 0"
class="list-none flex justify-center items-center"
class="list-none flex justify-center"
>
<span
class="shadow-lg rounded-full bg-n-brand text-white text-xs font-medium my-2.5 mx-auto px-2.5 py-1.5"
>
{{ unreadMessageLabel }}
</span>
<UnreadIndicator
:label="unreadMessageLabel"
variant="primary"
@intersect="onUnreadBadgeIntersect"
/>
</li>
</template>
<template #after>
@@ -512,19 +603,16 @@ export default {
}"
>
<div
v-if="isAnyoneTyping"
class="absolute flex items-center w-full h-0 -top-7"
class="flex items-center justify-center gap-1 absolute w-full -top-7 h-0"
>
<div
class="flex py-2 pr-4 pl-5 shadow-md rounded-full bg-white dark:bg-n-solid-3 text-n-slate-11 text-xs font-semibold my-2.5 mx-auto"
>
{{ typingUserNames }}
<img
class="w-6 ltr:ml-2 rtl:mr-2"
src="assets/images/typing.gif"
alt="Someone is typing"
/>
</div>
<TypingIndicator />
<UnreadIndicator
v-if="showFloatingUnreadIndicator && unreadMessageCount > 0"
variant="secondary"
class="cursor-pointer"
:label="unreadMessageLabel"
@click="scrollToBottom"
/>
</div>
<ReplyBox
:pop-out-reply-box="isPopOutReplyBox"
+1
View File
@@ -39,6 +39,7 @@ export const FEATURE_FLAGS = {
CHANNEL_INSTAGRAM: 'channel_instagram',
CONTACT_CHATWOOT_SUPPORT_TEAM: 'contact_chatwoot_support_team',
CAPTAIN_V2: 'captain_integration_v2',
CHAT_PRESERVE_USER_SCROLL: 'chat_preserve_user_scroll',
SAML: 'saml',
};
@@ -25,24 +25,6 @@ export const isJSONValid = value => {
return true;
};
export const getTypingUsersText = (users = []) => {
const count = users.length;
const [firstUser, secondUser] = users;
if (count === 1) {
return ['TYPING.ONE', { user: firstUser.name }];
}
if (count === 2) {
return [
'TYPING.TWO',
{ user: firstUser.name, secondUser: secondUser.name },
];
}
return ['TYPING.MULTIPLE', { user: firstUser.name, count: count - 1 }];
};
export const createPendingMessage = data => {
const timestamp = Math.floor(new Date().getTime() / 1000);
const tempMessageId = getUuid();
@@ -1,5 +1,4 @@
import {
getTypingUsersText,
createPendingMessage,
convertToAttributeSlug,
convertToCategorySlug,
@@ -8,32 +7,6 @@ import {
formatToTitleCase,
} from '../commons';
describe('#getTypingUsersText', () => {
it('returns the correct text is there is only one typing user', () => {
expect(getTypingUsersText([{ name: 'Pranav' }])).toEqual([
'TYPING.ONE',
{ user: 'Pranav' },
]);
});
it('returns the correct text is there are two typing users', () => {
expect(
getTypingUsersText([{ name: 'Pranav' }, { name: 'Nithin' }])
).toEqual(['TYPING.TWO', { user: 'Pranav', secondUser: 'Nithin' }]);
});
it('returns the correct text is there are more than two users are typing', () => {
expect(
getTypingUsersText([
{ name: 'Pranav' },
{ name: 'Nithin' },
{ name: 'Subin' },
{ name: 'Sojan' },
])
).toEqual(['TYPING.MULTIPLE', { user: 'Pranav', count: 3 }]);
});
});
describe('#createPendingMessage', () => {
const message = {
message: 'hi',
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Search",
"EMPTY_STATE": "No results found"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agents",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "بحث",
"EMPTY_STATE": "لم يتم العثور على النتائج"
},
"CLOSE": "أغلق"
"CLOSE": "أغلق",
"BETA": "تجريبي",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "كود \"الماسنجر\"",
"MESSENGER_SUB_HEAD": "ضع هذا الكود داخل وسم الـ body في موقعك",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "وكيل الدعم",
"INBOX_AGENTS_SUB_TEXT": "إضافة أو إزالة وكلاء من صندوق الوارد هذا",
"AGENT_ASSIGNMENT": "تعيين المحادثة",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Search",
"EMPTY_STATE": "No results found"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agents",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Търсене",
"EMPTY_STATE": "Няма намерени резултати"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Агенти",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Cercar",
"EMPTY_STATE": "No s'ha trobat agents"
},
"CLOSE": "Tanca"
"CLOSE": "Tanca",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Script del missatger",
"MESSENGER_SUB_HEAD": "Col·loca aquest botó dins de l'etiqueta body",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agents",
"INBOX_AGENTS_SUB_TEXT": "Afegir o eliminar agents d'aquesta safata d'entrada",
"AGENT_ASSIGNMENT": "Conversació Assignada",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Hledat",
"EMPTY_STATE": "Žádné výsledky"
},
"CLOSE": "Zavřít"
"CLOSE": "Zavřít",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger skript",
"MESSENGER_SUB_HEAD": "Umístěte toto tlačítko dovnitř vašeho tělesného štítku",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agenti",
"INBOX_AGENTS_SUB_TEXT": "Přidat nebo odebrat agenty z této složky doručené pošty",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Søg",
"EMPTY_STATE": "Ingen resultater fundet"
},
"CLOSE": "Luk"
"CLOSE": "Luk",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger- Script",
"MESSENGER_SUB_HEAD": "Placer denne knap inde i din body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agenter",
"INBOX_AGENTS_SUB_TEXT": "Tilføj eller fjern agenter fra denne indbakke",
"AGENT_ASSIGNMENT": "Samtale Tildeling",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Suchen",
"EMPTY_STATE": "Keine Ergebnisse gefunden"
},
"CLOSE": "Schließen"
"CLOSE": "Schließen",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger-Skript",
"MESSENGER_SUB_HEAD": "Platzieren Sie diese Schaltfläche in Ihrem Body-Tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agenten",
"INBOX_AGENTS_SUB_TEXT": "Hinzufügen oder Entfernen von Agenten zu diesem Posteingang",
"AGENT_ASSIGNMENT": "Konversationssauftrag",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Αναζήτηση",
"EMPTY_STATE": "Δεν βρέθηκαν αποτελέσματα"
},
"CLOSE": "Κλείσιμο"
"CLOSE": "Κλείσιμο",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Κώδικας (Script)",
"MESSENGER_SUB_HEAD": "Τοποθετήσετε αυτόν τον κώδικα μέσα στο body tag της ιστοσελίδας σας",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Πράκτορες",
"INBOX_AGENTS_SUB_TEXT": "Προσθέστε ή αφαιρέστε πράκτορες σε αυτό το κιβώτιο",
"AGENT_ASSIGNMENT": "Ανάθεση Συνομιλίας",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -25,8 +25,9 @@
"PLACEHOLDER": "Type any text to search messages",
"NO_MATCHING_RESULTS": "No results found."
},
"UNREAD_MESSAGES": "Unread Messages",
"UNREAD_MESSAGE": "Unread Message",
"UNREAD_COUNT": "{count} Unread Messages",
"UNREAD_COUNT_OVERFLOW": "9+ Unread Messages",
"SINGLE_UNREAD_MESSAGE": "1 Unread Message",
"CLICK_HERE": "Click here",
"LOADING_INBOXES": "Loading inboxes",
"LOADING_CONVERSATIONS": "Loading Conversations",
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Buscar",
"EMPTY_STATE": "No se encontraron resultados"
},
"CLOSE": "Cerrar"
"CLOSE": "Cerrar",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Script de Messenger",
"MESSENGER_SUB_HEAD": "Coloca este botón dentro de tu etiqueta cuerpo",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agentes",
"INBOX_AGENTS_SUB_TEXT": "Añadir o quitar agentes de esta bandeja de entrada",
"AGENT_ASSIGNMENT": "Asignación de conversación",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "جستجو",
"EMPTY_STATE": "نتیجه‌ای یافت نشد"
},
"CLOSE": "بستن"
"CLOSE": "بستن",
"BETA": "آزمایشی",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "اسکریپت ویجت",
"MESSENGER_SUB_HEAD": "این دکمه را در تگ body قرار دهید",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "ایجنت ها",
"INBOX_AGENTS_SUB_TEXT": "اضافه کردن یا حذف کردن دسترسی ایجنت به صندوق ورودی",
"AGENT_ASSIGNMENT": "اختصاص گفتگو",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Etsi",
"EMPTY_STATE": "Tuloksia ei löytynyt"
},
"CLOSE": "Sulje"
"CLOSE": "Sulje",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger-skripti",
"MESSENGER_SUB_HEAD": "Aseta tämä painike body-tagiisi",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Edustajat",
"INBOX_AGENTS_SUB_TEXT": "Lisää tai poista edustajia tästä saapuneet-kansiosta",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Rechercher",
"EMPTY_STATE": "Aucun résultat trouvé"
},
"CLOSE": "Fermer"
"CLOSE": "Fermer",
"BETA": "Bêta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Script du Widget Web",
"MESSENGER_SUB_HEAD": "Placez ce code avant la fermeture de votre balise body",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agents",
"INBOX_AGENTS_SUB_TEXT": "Ajouter ou supprimer des agents de cette boîte de réception",
"AGENT_ASSIGNMENT": "Konversationsauftrag",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "חפש",
"EMPTY_STATE": "לא נמצאו תוצאות"
},
"CLOSE": "סגור"
"CLOSE": "סגור",
"BETA": "בטא",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "סקריפט מסנג'ר",
"MESSENGER_SUB_HEAD": "מקם את הכפתור הזה בתוך תג הגוף שלך",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "סוכנים",
"INBOX_AGENTS_SUB_TEXT": "הוסף או הסר נציגים מתיבת הדואר הנכנס הזו",
"AGENT_ASSIGNMENT": "שיוך שיחה",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Search",
"EMPTY_STATE": "No results found"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agents",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Search",
"EMPTY_STATE": "Nisu pronađeni rezultati"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agenti",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Keresés",
"EMPTY_STATE": "Nincs találat"
},
"CLOSE": "Bezárás"
"CLOSE": "Bezárás",
"BETA": "Béta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger szkript",
"MESSENGER_SUB_HEAD": "Ezt a gombot a body tag-en belül helyezd el",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Ügynökök",
"INBOX_AGENTS_SUB_TEXT": "Ügynökök hosszáadása vagy eltávolítása az inboxból",
"AGENT_ASSIGNMENT": "Beszélgetés hozzárendelés",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Search",
"EMPTY_STATE": "No results found"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agents",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Cari",
"EMPTY_STATE": "Tidak ada hasil ditemukan"
},
"CLOSE": "Tutup"
"CLOSE": "Tutup",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Tempatkan tombol ini di dalam tag <body> Anda",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agen",
"INBOX_AGENTS_SUB_TEXT": "Tambahkan atau hapus agen dari kotak masuk ini",
"AGENT_ASSIGNMENT": "Tugas Percakapan",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Leit",
"EMPTY_STATE": "Engar niðurstöður fundust"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Þjónustufulltrúar",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Cerca",
"EMPTY_STATE": "Nessun risultato trovato"
},
"CLOSE": "Chiudi"
"CLOSE": "Chiudi",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Script Messenger",
"MESSENGER_SUB_HEAD": "Posiziona questo pulsante all'interno del tuo tag body",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agenti",
"INBOX_AGENTS_SUB_TEXT": "Aggiungi o rimuovi agenti da questa casella",
"AGENT_ASSIGNMENT": "Assegnazione conversazione",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "検索",
"EMPTY_STATE": "結果が見つかりませんでした。"
},
"CLOSE": "閉じる"
"CLOSE": "閉じる",
"BETA": "ベータ版",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messengerスクリプト",
"MESSENGER_SUB_HEAD": "このボタンをbodyタグの中に配置してください。",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "担当者",
"INBOX_AGENTS_SUB_TEXT": "この受信トレイから担当者を追加または削除する",
"AGENT_ASSIGNMENT": "会話の割り当て",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Search",
"EMPTY_STATE": "No results found"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agents",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "검색",
"EMPTY_STATE": "검색 결과가 없습니다"
},
"CLOSE": "닫기"
"CLOSE": "닫기",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "메신저 스크립트",
"MESSENGER_SUB_HEAD": "이 버튼을 당신의 body 태그 안에 넣으세요.",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "에이전트",
"INBOX_AGENTS_SUB_TEXT": "받은 메시지함에서 에이전트 추가 또는 제거",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Ieškoti",
"EMPTY_STATE": "Nieko nerasta"
},
"CLOSE": "Uždaryti"
"CLOSE": "Uždaryti",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger scenarijus",
"MESSENGER_SUB_HEAD": "Įdėkite šį mygtuką savo <body> žymos viduje",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agentai",
"INBOX_AGENTS_SUB_TEXT": "Pridėti ar pašalinti agentus iš gautų laiškų aplanko",
"AGENT_ASSIGNMENT": "Pokalbio paskirstymas",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Meklēt",
"EMPTY_STATE": "Nav atrasts"
},
"CLOSE": "Aizvērt"
"CLOSE": "Aizvērt",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Skripts",
"MESSENGER_SUB_HEAD": "Ievietojiet šo pogu savā body tagā",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Aģenti",
"INBOX_AGENTS_SUB_TEXT": "Pievienot vai noņemt aģentus no šīs iesūtnes",
"AGENT_ASSIGNMENT": "Sarunas Piešķiršana",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "തിരയുക",
"EMPTY_STATE": "ഒരു ഫലവും കണ്ടെത്താനായില്ല"
},
"CLOSE": "അടയ്ക്കുക"
"CLOSE": "അടയ്ക്കുക",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "മെസഞ്ചർ സ്ക്രിപ്റ്റ്",
"MESSENGER_SUB_HEAD": "ഈ ബട്ടൺ നിങ്ങളുടെ ബോഡി ടാഗിനുള്ളിൽ സ്ഥാപിക്കുക",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "ഏജന്റുമാർ",
"INBOX_AGENTS_SUB_TEXT": "ഈ ഇൻ‌ബോക്സിൽ നിന്ന് ഏജന്റുമാരെ ചേർക്കുക അല്ലെങ്കിൽ നീക്കംചെയ്യുക",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Search",
"EMPTY_STATE": "Tiada dijumpa"
},
"CLOSE": "Close"
"CLOSE": "Close",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Ejen",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Search",
"EMPTY_STATE": "No results found"
},
"CLOSE": "बन्दा गार्नुहोस्"
"CLOSE": "बन्दा गार्नुहोस्",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Place this button inside your body tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agents",
"INBOX_AGENTS_SUB_TEXT": "Add or remove agents from this inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}
@@ -5,6 +5,8 @@
"PLACEHOLDER": "Zoeken",
"EMPTY_STATE": "Geen resultaten gevonden"
},
"CLOSE": "Sluiten"
"CLOSE": "Sluiten",
"BETA": "Beta",
"BETA_DESCRIPTION": "This feature is in beta and may change as we improve it."
}
}
@@ -618,6 +618,11 @@
"SETTINGS_POPUP": {
"MESSENGER_HEADING": "Messenger Script",
"MESSENGER_SUB_HEAD": "Plaats deze knop in je lichaam tag",
"ALLOWED_DOMAINS": {
"TITLE": "Allowed Domains",
"SUBTITLE": "Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER": "Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
},
"INBOX_AGENTS": "Agenten",
"INBOX_AGENTS_SUB_TEXT": "Voeg agenten toe of verwijder ze uit deze inbox",
"AGENT_ASSIGNMENT": "Conversation Assignment",
@@ -34,7 +34,7 @@
},
"SUBMIT": "Continue with SSO",
"API": {
"ERROR_MESSAGE": "SSO authentication failed"
"ERROR_MESSAGE": "SSO authentication failed. Please check your credentials and try again."
}
}
}

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