Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9def5f80a3 | ||
|
|
d92b278b60 | ||
|
|
d26b6e0e64 | ||
|
|
dd641aeef3 | ||
|
|
9119e947f0 | ||
|
|
ac60784f95 | ||
|
|
9ab45ad50e | ||
|
|
1e3be0ac24 | ||
|
|
35715ae841 | ||
|
|
27fd2b8d86 | ||
|
|
c71bcc2428 | ||
|
|
b5db3c9512 |
+1
-1
@@ -1 +1 @@
|
||||
4.13.0
|
||||
4.12.1
|
||||
|
||||
@@ -103,7 +103,6 @@ const showPagination = computed(() => {
|
||||
<ContactsActiveFiltersPreview
|
||||
v-if="showActiveFiltersPreview"
|
||||
:active-segment="activeSegment"
|
||||
class="mb-1"
|
||||
@clear-filters="emit('clearFilters')"
|
||||
@open-filter="openFilter"
|
||||
/>
|
||||
|
||||
@@ -57,7 +57,7 @@ useKeyboardEvents(keyboardEvents);
|
||||
|
||||
<template>
|
||||
<ButtonGroup
|
||||
class="flex flex-col justify-center items-center absolute top-36 xl:top-24 ltr:right-2 rtl:left-2 bg-n-solid-2/90 backdrop-blur-lg border border-n-weak/50 rounded-full gap-1.5 p-1.5 shadow-sm transition-shadow duration-200 hover:shadow !z-20"
|
||||
class="flex flex-col justify-center items-center absolute top-36 xl:top-24 ltr:right-2 rtl:left-2 bg-n-solid-2/90 backdrop-blur-lg border border-n-weak/50 rounded-full gap-1.5 p-1.5 shadow-sm transition-shadow duration-200 hover:shadow"
|
||||
>
|
||||
<Button
|
||||
v-tooltip.top="$t('CONVERSATION.SIDEBAR.CONTACT')"
|
||||
|
||||
+32
-25
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { debounce } from '@chatwoot/utils';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ARTICLE_EDITOR_MENU_OPTIONS } from 'dashboard/constants/editor';
|
||||
@@ -27,6 +27,7 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits([
|
||||
'saveArticle',
|
||||
'saveArticleAsync',
|
||||
'goBack',
|
||||
'setAuthor',
|
||||
'setCategory',
|
||||
@@ -38,37 +39,42 @@ const { t } = useI18n();
|
||||
|
||||
const isNewArticle = computed(() => !props.article?.id);
|
||||
|
||||
const localTitle = ref(props.article?.title ?? '');
|
||||
const localContent = ref(props.article?.content ?? '');
|
||||
const saveAndSync = value => {
|
||||
emit('saveArticle', value);
|
||||
};
|
||||
|
||||
// Sync local state when navigating to a different article or on initial fetch
|
||||
watch(
|
||||
() => props.article?.id,
|
||||
newId => {
|
||||
if (newId) {
|
||||
localTitle.value = props.article?.title ?? '';
|
||||
localContent.value = props.article?.content ?? '';
|
||||
}
|
||||
}
|
||||
// this will only send the data to the backend
|
||||
// but will not update the local state preventing unnecessary re-renders
|
||||
// since the data is already saved and we keep the editor text as the source of truth
|
||||
const quickSave = debounce(
|
||||
value => emit('saveArticleAsync', value),
|
||||
400,
|
||||
false
|
||||
);
|
||||
|
||||
const debouncedSave = debounce(value => emit('saveArticle', value), 500, false);
|
||||
// 2.5 seconds is enough to know that the user has stopped typing and is taking a pause
|
||||
// so we can save the data to the backend and retrieve the updated data
|
||||
// this will update the local state with response data
|
||||
// Only use to save for existing articles
|
||||
const saveAndSyncDebounced = debounce(saveAndSync, 2500, false);
|
||||
|
||||
const handleSave = value => {
|
||||
if (isNewArticle.value) return;
|
||||
debouncedSave(value);
|
||||
quickSave(value);
|
||||
saveAndSyncDebounced(value);
|
||||
};
|
||||
|
||||
const articleTitle = computed({
|
||||
get: () => localTitle.value,
|
||||
get: () => props.article.title,
|
||||
set: value => {
|
||||
localTitle.value = value;
|
||||
handleSave({ title: value });
|
||||
},
|
||||
});
|
||||
|
||||
const localContent = ref(props.article.content || '');
|
||||
|
||||
const articleContent = computed({
|
||||
get: () => localContent.value,
|
||||
get: () => props.article.content,
|
||||
set: content => {
|
||||
localContent.value = content;
|
||||
handleSave({ content });
|
||||
@@ -126,7 +132,7 @@ const handleCreateArticle = event => {
|
||||
/>
|
||||
<ArticleEditorControls
|
||||
:article="article"
|
||||
@save-article="values => emit('saveArticle', values)"
|
||||
@save-article="saveAndSync"
|
||||
@set-author="setAuthorId"
|
||||
@set-category="setCategoryId"
|
||||
/>
|
||||
@@ -161,12 +167,8 @@ const handleCreateArticle = event => {
|
||||
}
|
||||
|
||||
.editor-root .has-selection {
|
||||
.ProseMirror-menubar:not(:has(*)) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ProseMirror-menubar {
|
||||
@apply rounded-lg !px-3 !py-1.5 z-50 bg-n-background items-center gap-4 ml-0 mb-0 shadow-md outline outline-1 outline-n-weak;
|
||||
@apply h-8 rounded-lg !px-2 z-50 bg-n-solid-3 items-center gap-4 ml-0 mb-0 shadow-md outline outline-1 outline-n-weak;
|
||||
display: flex;
|
||||
top: var(--selection-top, auto) !important;
|
||||
left: var(--selection-left, 0) !important;
|
||||
@@ -174,10 +176,15 @@ const handleCreateArticle = event => {
|
||||
position: absolute !important;
|
||||
|
||||
.ProseMirror-menuitem {
|
||||
@apply ltr:mr-0 rtl:ml-0 size-4 flex items-center;
|
||||
@apply mr-0;
|
||||
|
||||
.ProseMirror-icon {
|
||||
@apply p-0.5 flex-shrink-0 ltr:mr-2 rtl:ml-2;
|
||||
@apply p-0 mt-0 !mr-0;
|
||||
|
||||
svg {
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, useSlots } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
@@ -30,40 +30,23 @@ const modelValue = defineModel({
|
||||
});
|
||||
|
||||
const selectedCount = computed(() => modelValue.value.size);
|
||||
const visibleItemIds = computed(() => props.allItems.map(item => item.id));
|
||||
const visibleItemCount = computed(() => visibleItemIds.value.length);
|
||||
const selectedVisibleCount = computed(
|
||||
() => visibleItemIds.value.filter(id => modelValue.value.has(id)).length
|
||||
);
|
||||
const totalCount = computed(() => props.allItems.length);
|
||||
|
||||
const hasSelected = computed(() => selectedCount.value > 0);
|
||||
const isIndeterminate = computed(
|
||||
() =>
|
||||
selectedVisibleCount.value > 0 &&
|
||||
selectedVisibleCount.value < visibleItemCount.value
|
||||
() => hasSelected.value && selectedCount.value < totalCount.value
|
||||
);
|
||||
const allSelected = computed(
|
||||
() =>
|
||||
visibleItemCount.value > 0 &&
|
||||
selectedVisibleCount.value === visibleItemCount.value
|
||||
() => totalCount.value > 0 && selectedCount.value === totalCount.value
|
||||
);
|
||||
|
||||
const slots = useSlots();
|
||||
const hasSecondaryActions = computed(() => Boolean(slots['secondary-actions']));
|
||||
|
||||
const bulkCheckboxState = computed({
|
||||
get: () => allSelected.value,
|
||||
set: shouldSelectAll => {
|
||||
if (!visibleItemCount.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedSelection = new Set(modelValue.value);
|
||||
if (shouldSelectAll) {
|
||||
visibleItemIds.value.forEach(id => updatedSelection.add(id));
|
||||
} else {
|
||||
visibleItemIds.value.forEach(id => updatedSelection.delete(id));
|
||||
}
|
||||
modelValue.value = updatedSelection;
|
||||
const newSelectedIds = shouldSelectAll
|
||||
? new Set(props.allItems.map(item => item.id))
|
||||
: new Set();
|
||||
modelValue.value = newSelectedIds;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -80,7 +63,7 @@ const bulkCheckboxState = computed({
|
||||
v-if="hasSelected"
|
||||
class="flex items-center gap-3 py-1 ltr:pl-3 rtl:pr-3 ltr:pr-4 rtl:pl-4 rounded-lg bg-n-solid-2 outline outline-1 outline-n-container shadow"
|
||||
>
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center gap-1.5 min-w-0">
|
||||
<Checkbox
|
||||
v-model="bulkCheckboxState"
|
||||
@@ -95,23 +78,21 @@ const bulkCheckboxState = computed({
|
||||
<span class="text-sm text-n-slate-10 truncate tabular-nums">
|
||||
{{ selectedCountLabel }}
|
||||
</span>
|
||||
<div class="h-4 w-px bg-n-strong" />
|
||||
<slot name="secondary-actions" />
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<slot v-if="hasSecondaryActions" name="secondary-actions" />
|
||||
<div v-if="hasSecondaryActions" class="h-4 w-px bg-n-strong" />
|
||||
<div class="flex items-center gap-3">
|
||||
<slot name="actions" :selected-count="selectedCount">
|
||||
<Button
|
||||
:label="deleteLabel"
|
||||
sm
|
||||
ruby
|
||||
ghost
|
||||
class="!px-1.5"
|
||||
icon="i-lucide-trash"
|
||||
@click="emit('bulkDelete')"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
<slot name="actions" :selected-count="selectedCount">
|
||||
<Button
|
||||
:label="deleteLabel"
|
||||
sm
|
||||
ruby
|
||||
ghost
|
||||
class="!px-1.5"
|
||||
icon="i-lucide-trash"
|
||||
@click="emit('bulkDelete')"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex items-center gap-3">
|
||||
|
||||
@@ -226,10 +226,4 @@ const handleSeeOriginal = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Email clients (Gmail, Outlook) hardcode dir="ltr" on wrapper elements.
|
||||
// In RTL apps this forces email content LTR regardless of actual text.
|
||||
[dir='rtl'] .letter-render [dir='ltr'] {
|
||||
direction: inherit;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -250,12 +250,6 @@ const menuItems = computed(() => {
|
||||
activeOn: ['conversation_through_mentions'],
|
||||
to: accountScopedRoute('conversation_mentions'),
|
||||
},
|
||||
{
|
||||
name: 'Participating',
|
||||
label: t('SIDEBAR.PARTICIPATING_CONVERSATIONS'),
|
||||
activeOn: ['conversation_through_participating'],
|
||||
to: accountScopedRoute('conversation_participating'),
|
||||
},
|
||||
{
|
||||
name: 'Unattended',
|
||||
activeOn: ['conversation_through_unattended'],
|
||||
|
||||
@@ -158,11 +158,9 @@ const activeChild = computed(() => {
|
||||
return rankedPage ?? activeOnPages[0];
|
||||
}
|
||||
|
||||
return navigableChildren.value.find(child => {
|
||||
if (!child.to) return false;
|
||||
const childPath = resolvePath(child.to);
|
||||
return route.path === childPath || route.path.startsWith(`${childPath}/`);
|
||||
});
|
||||
return navigableChildren.value.find(
|
||||
child => child.to && route.path.startsWith(resolvePath(child.to))
|
||||
);
|
||||
});
|
||||
|
||||
const hasActiveChild = computed(() => {
|
||||
|
||||
@@ -56,7 +56,6 @@ import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHe
|
||||
import { conversationListPageURL } from '../helper/URLHelper';
|
||||
import {
|
||||
isOnMentionsView,
|
||||
isOnParticipatingView,
|
||||
isOnUnattendedView,
|
||||
} from '../store/modules/conversations/helpers/actionHelpers';
|
||||
import {
|
||||
@@ -114,7 +113,6 @@ const chatLists = useMapGetter('getFilteredConversations');
|
||||
const mineChatsList = useMapGetter('getMineChats');
|
||||
const allChatList = useMapGetter('getAllStatusChats');
|
||||
const unAssignedChatsList = useMapGetter('getUnAssignedChats');
|
||||
const participatingChatsList = useMapGetter('getParticipatingChats');
|
||||
const chatListLoading = useMapGetter('getChatListLoadingStatus');
|
||||
const activeInbox = useMapGetter('getSelectedInbox');
|
||||
const conversationStats = useMapGetter('conversationStats/getStats');
|
||||
@@ -298,15 +296,13 @@ const pageTitle = computed(() => {
|
||||
if (props.label) {
|
||||
return `#${props.label}`;
|
||||
}
|
||||
if (props.conversationType === wootConstants.CONVERSATION_TYPE.MENTION) {
|
||||
if (props.conversationType === 'mention') {
|
||||
return t('CHAT_LIST.MENTION_HEADING');
|
||||
}
|
||||
if (
|
||||
props.conversationType === wootConstants.CONVERSATION_TYPE.PARTICIPATING
|
||||
) {
|
||||
if (props.conversationType === 'participating') {
|
||||
return t('CONVERSATION_PARTICIPANTS.SIDEBAR_MENU_TITLE');
|
||||
}
|
||||
if (props.conversationType === wootConstants.CONVERSATION_TYPE.UNATTENDED) {
|
||||
if (props.conversationType === 'unattended') {
|
||||
return t('CHAT_LIST.UNATTENDED_HEADING');
|
||||
}
|
||||
if (hasActiveFolders.value) {
|
||||
@@ -315,30 +311,12 @@ const pageTitle = computed(() => {
|
||||
return t('CHAT_LIST.TAB_HEADING');
|
||||
});
|
||||
|
||||
function filterByAssigneeTab(conversations) {
|
||||
if (activeAssigneeTab.value === wootConstants.ASSIGNEE_TYPE.ME) {
|
||||
return conversations.filter(
|
||||
c => c.meta?.assignee?.id === currentUser.value?.id
|
||||
);
|
||||
}
|
||||
if (activeAssigneeTab.value === wootConstants.ASSIGNEE_TYPE.UNASSIGNED) {
|
||||
return conversations.filter(c => !c.meta?.assignee);
|
||||
}
|
||||
return [...conversations];
|
||||
}
|
||||
|
||||
const conversationList = computed(() => {
|
||||
let localConversationList = [];
|
||||
|
||||
if (!hasAppliedFiltersOrActiveFolders.value) {
|
||||
const filters = conversationFilters.value;
|
||||
if (
|
||||
props.conversationType === wootConstants.CONVERSATION_TYPE.PARTICIPATING
|
||||
) {
|
||||
localConversationList = filterByAssigneeTab(
|
||||
participatingChatsList.value(filters)
|
||||
);
|
||||
} else if (activeAssigneeTab.value === 'me') {
|
||||
if (activeAssigneeTab.value === 'me') {
|
||||
localConversationList = [...mineChatsList.value(filters)];
|
||||
} else if (activeAssigneeTab.value === 'unassigned') {
|
||||
localConversationList = [...unAssignedChatsList.value(filters)];
|
||||
@@ -659,11 +637,9 @@ function redirectToConversationList() {
|
||||
|
||||
let conversationType = '';
|
||||
if (isOnMentionsView({ route: { name } })) {
|
||||
conversationType = wootConstants.CONVERSATION_TYPE.MENTION;
|
||||
} else if (isOnParticipatingView({ route: { name } })) {
|
||||
conversationType = wootConstants.CONVERSATION_TYPE.PARTICIPATING;
|
||||
conversationType = 'mention';
|
||||
} else if (isOnUnattendedView({ route: { name } })) {
|
||||
conversationType = wootConstants.CONVERSATION_TYPE.UNATTENDED;
|
||||
conversationType = 'unattended';
|
||||
}
|
||||
router.push(
|
||||
conversationListPageURL({
|
||||
|
||||
@@ -27,6 +27,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isPopout: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
@@ -204,7 +208,10 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="space-y-2 mb-4">
|
||||
<div class="overflow-y-auto max-h-56">
|
||||
<div
|
||||
class="overflow-y-auto"
|
||||
:class="{ 'max-h-96': isPopout, 'max-h-56': !isPopout }"
|
||||
>
|
||||
<p
|
||||
v-dompurify-html="formatMessage(generatedContent, false)"
|
||||
class="text-n-iris-12 text-sm prose-sm font-normal !mb-4"
|
||||
|
||||
@@ -984,32 +984,7 @@ useEmitter(BUS_EVENTS.INSERT_INTO_RICH_EDITOR, insertContentIntoEditor);
|
||||
}
|
||||
|
||||
.ProseMirror-woot-style {
|
||||
@apply overflow-auto;
|
||||
}
|
||||
|
||||
.ProseMirror-woot-style:not(
|
||||
:where(.resizable-editor-wrapper .ProseMirror-woot-style)
|
||||
) {
|
||||
@apply min-h-[5rem] max-h-[7.5rem];
|
||||
}
|
||||
|
||||
// Resizable editor wrapper styles
|
||||
.resizable-editor-wrapper {
|
||||
.ProseMirror-woot-style {
|
||||
min-height: clamp(
|
||||
var(--editor-min-allowed, var(--editor-min-height, 5rem)),
|
||||
var(--editor-height, var(--editor-min-height, 5rem)),
|
||||
var(--editor-max-allowed, var(--editor-max-height, 7.5rem))
|
||||
);
|
||||
max-height: clamp(
|
||||
var(--editor-min-allowed, var(--editor-min-height, 5rem)),
|
||||
var(--editor-height, var(--editor-min-height, 5rem)),
|
||||
var(--editor-max-allowed, var(--editor-max-height, 7.5rem))
|
||||
);
|
||||
transition:
|
||||
min-height var(--editor-height-transition, 180ms ease),
|
||||
max-height var(--editor-height-transition, 180ms ease);
|
||||
}
|
||||
@apply overflow-auto min-h-[5rem] max-h-[7.5rem];
|
||||
}
|
||||
|
||||
.ProseMirror-prompt-backdrop::backdrop {
|
||||
|
||||
@@ -8,22 +8,13 @@ import {
|
||||
EditorState,
|
||||
Selection,
|
||||
} from '@chatwoot/prosemirror-schema';
|
||||
import {
|
||||
suggestionsPlugin,
|
||||
triggerCharacters,
|
||||
} from '@chatwoot/prosemirror-schema/src/mentions/plugin';
|
||||
import imagePastePlugin from '@chatwoot/prosemirror-schema/src/plugins/image';
|
||||
import { toggleMark } from 'prosemirror-commands';
|
||||
import { wrapInList } from 'prosemirror-schema-list';
|
||||
import { toggleBlockType } from '@chatwoot/prosemirror-schema/src/menu/common';
|
||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||
import SlashCommandMenu from './SlashCommandMenu.vue';
|
||||
|
||||
const MAXIMUM_FILE_UPLOAD_SIZE = 4; // in MB
|
||||
const SLASH_MENU_OFFSET = 4;
|
||||
const createState = (
|
||||
content,
|
||||
placeholder,
|
||||
@@ -49,7 +40,6 @@ let editorView = null;
|
||||
let state;
|
||||
|
||||
export default {
|
||||
components: { SlashCommandMenu },
|
||||
mixins: [keyboardEventListenerMixins],
|
||||
props: {
|
||||
modelValue: { type: String, default: '' },
|
||||
@@ -72,15 +62,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
plugins: [
|
||||
imagePastePlugin(this.handleImageUpload),
|
||||
this.createSlashPlugin(),
|
||||
],
|
||||
plugins: [imagePastePlugin(this.handleImageUpload)],
|
||||
isTextSelected: false, // Tracks text selection and prevents unnecessary re-renders on mouse selection
|
||||
showSlashMenu: false,
|
||||
slashSearchTerm: '',
|
||||
slashRange: null,
|
||||
slashMenuPosition: null,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -112,126 +95,6 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createSlashPlugin() {
|
||||
return suggestionsPlugin({
|
||||
matcher: triggerCharacters('/', 0),
|
||||
suggestionClass: '',
|
||||
onEnter: args => {
|
||||
this.showSlashMenu = true;
|
||||
this.slashRange = args.range;
|
||||
this.slashSearchTerm = args.text || '';
|
||||
this.updateSlashMenuPosition(args.range.from);
|
||||
return false;
|
||||
},
|
||||
onChange: args => {
|
||||
this.slashRange = args.range;
|
||||
this.slashSearchTerm = args.text;
|
||||
return false;
|
||||
},
|
||||
onExit: () => {
|
||||
this.slashSearchTerm = '';
|
||||
this.showSlashMenu = false;
|
||||
this.slashMenuPosition = null;
|
||||
return false;
|
||||
},
|
||||
onKeyDown: ({ event }) => {
|
||||
return (
|
||||
event.keyCode === 13 &&
|
||||
this.showSlashMenu &&
|
||||
this.$refs.slashMenu?.hasItems
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
updateSlashMenuPosition(pos) {
|
||||
if (!editorView) return;
|
||||
const coords = editorView.coordsAtPos(pos);
|
||||
const editorRect = this.$refs.editor.getBoundingClientRect();
|
||||
const isRtl = getComputedStyle(this.$refs.editor).direction === 'rtl';
|
||||
this.slashMenuPosition = {
|
||||
top: coords.bottom - editorRect.top + SLASH_MENU_OFFSET,
|
||||
...(isRtl
|
||||
? { right: editorRect.right - coords.right }
|
||||
: { left: coords.left - editorRect.left }),
|
||||
};
|
||||
},
|
||||
removeSlashTriggerText() {
|
||||
if (!editorView || !this.slashRange) return;
|
||||
const { from, to } = this.slashRange;
|
||||
editorView.dispatch(editorView.state.tr.delete(from, to));
|
||||
state = editorView.state;
|
||||
},
|
||||
executeSlashCommand(actionKey) {
|
||||
if (!editorView) return;
|
||||
|
||||
this.removeSlashTriggerText();
|
||||
|
||||
const { schema } = editorView.state;
|
||||
const commandMap = {
|
||||
strong: () =>
|
||||
toggleMark(schema.marks.strong)(
|
||||
editorView.state,
|
||||
editorView.dispatch
|
||||
),
|
||||
em: () =>
|
||||
toggleMark(schema.marks.em)(editorView.state, editorView.dispatch),
|
||||
strike: () =>
|
||||
toggleMark(schema.marks.strike)(
|
||||
editorView.state,
|
||||
editorView.dispatch
|
||||
),
|
||||
code: () =>
|
||||
toggleMark(schema.marks.code)(editorView.state, editorView.dispatch),
|
||||
h1: () =>
|
||||
toggleBlockType(schema.nodes.heading, { level: 1 })(
|
||||
editorView.state,
|
||||
editorView.dispatch
|
||||
),
|
||||
h2: () =>
|
||||
toggleBlockType(schema.nodes.heading, { level: 2 })(
|
||||
editorView.state,
|
||||
editorView.dispatch
|
||||
),
|
||||
h3: () =>
|
||||
toggleBlockType(schema.nodes.heading, { level: 3 })(
|
||||
editorView.state,
|
||||
editorView.dispatch
|
||||
),
|
||||
bulletList: () =>
|
||||
wrapInList(schema.nodes.bullet_list)(
|
||||
editorView.state,
|
||||
editorView.dispatch
|
||||
),
|
||||
orderedList: () =>
|
||||
wrapInList(schema.nodes.ordered_list)(
|
||||
editorView.state,
|
||||
editorView.dispatch
|
||||
),
|
||||
insertTable: () => {
|
||||
const { table, table_row, table_header, table_cell, paragraph } =
|
||||
schema.nodes;
|
||||
const headerCells = [0, 1, 2].map(() =>
|
||||
table_header.createAndFill(null, paragraph.create())
|
||||
);
|
||||
const dataCells = [0, 1, 2].map(() =>
|
||||
table_cell.createAndFill(null, paragraph.create())
|
||||
);
|
||||
const headerRow = table_row.create(null, headerCells);
|
||||
const dataRow = table_row.create(null, dataCells);
|
||||
const tableNode = table.create(null, [headerRow, dataRow]);
|
||||
const tr = editorView.state.tr.replaceSelectionWith(tableNode);
|
||||
editorView.dispatch(tr.scrollIntoView());
|
||||
},
|
||||
};
|
||||
|
||||
const command = commandMap[actionKey];
|
||||
if (command) {
|
||||
command();
|
||||
state = editorView.state;
|
||||
this.emitOnChange();
|
||||
editorView.focus();
|
||||
}
|
||||
},
|
||||
contentFromEditor() {
|
||||
if (editorView) {
|
||||
return ArticleMarkdownSerializer.serialize(editorView.state.doc);
|
||||
@@ -399,8 +262,7 @@ export default {
|
||||
|
||||
// Get the editor's width
|
||||
const editorWidth = editor.offsetWidth;
|
||||
const menubar = editor.querySelector('.ProseMirror-menubar');
|
||||
const menubarWidth = menubar ? menubar.scrollWidth : 480;
|
||||
const menubarWidth = 480; // Menubar width (adjust as needed (px))
|
||||
|
||||
// Get the end position of the selection
|
||||
const { bottom: endBottom, right: endRight } = editorView.coordsAtPos(to);
|
||||
@@ -428,15 +290,7 @@ export default {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="editor-root editor--article relative">
|
||||
<SlashCommandMenu
|
||||
v-if="showSlashMenu"
|
||||
ref="slashMenu"
|
||||
:search-key="slashSearchTerm"
|
||||
:enabled-menu-options="enabledMenuOptions"
|
||||
:position="slashMenuPosition"
|
||||
@select-action="executeSlashCommand"
|
||||
/>
|
||||
<div class="editor-root editor--article">
|
||||
<input
|
||||
ref="imageUploadInput"
|
||||
type="file"
|
||||
|
||||
@@ -54,7 +54,7 @@ export default {
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: ['setReplyMode', 'toggleEditorSize', 'executeCopilotAction'],
|
||||
emits: ['setReplyMode', 'togglePopout', 'executeCopilotAction'],
|
||||
setup(props, { emit }) {
|
||||
const setReplyMode = mode => {
|
||||
emit('setReplyMode', mode);
|
||||
@@ -189,7 +189,7 @@ export default {
|
||||
class="text-n-slate-11"
|
||||
sm
|
||||
icon="i-lucide-maximize-2"
|
||||
@click="$emit('toggleEditorSize')"
|
||||
@click="$emit('togglePopout')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, computed, watch, nextTick } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useKeyboardNavigableList } from 'dashboard/composables/useKeyboardNavigableList';
|
||||
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||
|
||||
const props = defineProps({
|
||||
searchKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
enabledMenuOptions: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
position: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['selectAction']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const EDITOR_ACTIONS = [
|
||||
{
|
||||
value: 'h1',
|
||||
labelKey: 'SLASH_COMMANDS.HEADING_1',
|
||||
icon: 'i-lucide-heading-1',
|
||||
menuKey: 'h1',
|
||||
},
|
||||
{
|
||||
value: 'h2',
|
||||
labelKey: 'SLASH_COMMANDS.HEADING_2',
|
||||
icon: 'i-lucide-heading-2',
|
||||
menuKey: 'h2',
|
||||
},
|
||||
{
|
||||
value: 'h3',
|
||||
labelKey: 'SLASH_COMMANDS.HEADING_3',
|
||||
icon: 'i-lucide-heading-3',
|
||||
menuKey: 'h3',
|
||||
},
|
||||
{
|
||||
value: 'strong',
|
||||
labelKey: 'SLASH_COMMANDS.BOLD',
|
||||
icon: 'i-lucide-bold',
|
||||
menuKey: 'strong',
|
||||
},
|
||||
{
|
||||
value: 'em',
|
||||
labelKey: 'SLASH_COMMANDS.ITALIC',
|
||||
icon: 'i-lucide-italic',
|
||||
menuKey: 'em',
|
||||
},
|
||||
{
|
||||
value: 'insertTable',
|
||||
labelKey: 'SLASH_COMMANDS.TABLE',
|
||||
icon: 'i-lucide-table',
|
||||
menuKey: 'insertTable',
|
||||
},
|
||||
{
|
||||
value: 'strike',
|
||||
labelKey: 'SLASH_COMMANDS.STRIKETHROUGH',
|
||||
icon: 'i-lucide-strikethrough',
|
||||
menuKey: 'strike',
|
||||
},
|
||||
{
|
||||
value: 'code',
|
||||
labelKey: 'SLASH_COMMANDS.CODE',
|
||||
icon: 'i-lucide-code',
|
||||
menuKey: 'code',
|
||||
},
|
||||
{
|
||||
value: 'bulletList',
|
||||
labelKey: 'SLASH_COMMANDS.BULLET_LIST',
|
||||
icon: 'i-lucide-list',
|
||||
menuKey: 'bulletList',
|
||||
},
|
||||
{
|
||||
value: 'orderedList',
|
||||
labelKey: 'SLASH_COMMANDS.ORDERED_LIST',
|
||||
icon: 'i-lucide-list-ordered',
|
||||
menuKey: 'orderedList',
|
||||
},
|
||||
];
|
||||
|
||||
const listContainerRef = ref(null);
|
||||
const selectedIndex = ref(0);
|
||||
|
||||
const items = computed(() => {
|
||||
const search = props.searchKey.toLowerCase();
|
||||
return EDITOR_ACTIONS.filter(action => {
|
||||
if (!props.enabledMenuOptions.includes(action.menuKey)) return false;
|
||||
if (!search) return true;
|
||||
return t(action.labelKey).toLowerCase().includes(search);
|
||||
});
|
||||
});
|
||||
|
||||
const hasItems = computed(() => items.value.length > 0);
|
||||
|
||||
const menuStyle = computed(() => {
|
||||
if (!props.position) return {};
|
||||
const style = { top: `${props.position.top}px` };
|
||||
if (props.position.right != null) {
|
||||
style.right = `${props.position.right}px`;
|
||||
} else {
|
||||
style.left = `${props.position.left}px`;
|
||||
}
|
||||
return style;
|
||||
});
|
||||
|
||||
const adjustScroll = () => {
|
||||
nextTick(() => {
|
||||
const container = listContainerRef.value;
|
||||
if (!container) return;
|
||||
const el = container.querySelector(`#slash-item-${selectedIndex.value}`);
|
||||
if (el) {
|
||||
el.scrollIntoView({ block: 'nearest', behavior: 'auto' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onSelect = () => {
|
||||
const item = items.value[selectedIndex.value];
|
||||
if (item) emit('selectAction', item.value);
|
||||
};
|
||||
|
||||
useKeyboardNavigableList({
|
||||
items,
|
||||
onSelect,
|
||||
adjustScroll,
|
||||
selectedIndex,
|
||||
});
|
||||
|
||||
// Reset selection when filtered items change
|
||||
watch(items, () => {
|
||||
selectedIndex.value = 0;
|
||||
});
|
||||
|
||||
const onHover = index => {
|
||||
selectedIndex.value = index;
|
||||
};
|
||||
|
||||
const onItemClick = index => {
|
||||
selectedIndex.value = index;
|
||||
onSelect();
|
||||
};
|
||||
|
||||
defineExpose({ hasItems });
|
||||
</script>
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-root-v-if -->
|
||||
<template>
|
||||
<div
|
||||
v-if="hasItems"
|
||||
ref="listContainerRef"
|
||||
class="bg-n-alpha-3 backdrop-blur-[100px] outline outline-1 outline-n-container absolute rounded-xl z-50 flex flex-col min-w-[10rem] shadow-lg p-2 overflow-auto max-h-[15rem]"
|
||||
:style="menuStyle"
|
||||
>
|
||||
<button
|
||||
v-for="(item, index) in items"
|
||||
:id="`slash-item-${index}`"
|
||||
:key="item.value"
|
||||
type="button"
|
||||
class="inline-flex items-center justify-start w-full h-8 min-w-0 gap-2 px-2 py-1.5 border-0 rounded-lg text-n-slate-12 hover:bg-n-alpha-1 dark:hover:bg-n-alpha-2"
|
||||
:class="{
|
||||
'bg-n-alpha-1 dark:bg-n-alpha-2': index === selectedIndex,
|
||||
}"
|
||||
@mouseover="onHover(index)"
|
||||
@click="onItemClick(index)"
|
||||
>
|
||||
<Icon :icon="item.icon" class="flex-shrink-0 size-3.5" />
|
||||
<span class="min-w-0 text-sm truncate">
|
||||
{{ t(item.labelKey) }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -45,7 +45,6 @@ const backButtonUrl = computed(() => {
|
||||
|
||||
const conversationTypeMap = {
|
||||
conversation_through_mentions: 'mention',
|
||||
conversation_through_participating: 'participating',
|
||||
conversation_through_unattended: 'unattended',
|
||||
};
|
||||
return conversationListPageURL({
|
||||
|
||||
@@ -16,6 +16,10 @@ defineProps({
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isPopout: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
@@ -65,6 +69,7 @@ const onSend = () => {
|
||||
:generated-content="generatedContent"
|
||||
:min-height="4"
|
||||
:enabled-menu-options="[]"
|
||||
:is-popout="isPopout"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@clear-selection="clearEditorSelection"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { ref, provide, useTemplateRef } from 'vue';
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
import { ref, provide } from 'vue';
|
||||
// composable
|
||||
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||
import { useLabelSuggestions } from 'dashboard/composables/useLabelSuggestions';
|
||||
import { useSnakeCase } from 'dashboard/composables/useTransformKeys';
|
||||
|
||||
@@ -11,7 +11,6 @@ import MessageList from 'next/message/MessageList.vue';
|
||||
import ConversationLabelSuggestion from './conversation/LabelSuggestion.vue';
|
||||
import Banner from 'dashboard/components/ui/Banner.vue';
|
||||
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
||||
import ResizableEditorWrapper from './ResizableEditorWrapper.vue';
|
||||
|
||||
// stores and apis
|
||||
import { mapGetters } from 'vuex';
|
||||
@@ -44,16 +43,21 @@ export default {
|
||||
Banner,
|
||||
ConversationLabelSuggestion,
|
||||
Spinner,
|
||||
ResizableEditorWrapper,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
setup() {
|
||||
const isPopOutReplyBox = ref(false);
|
||||
const conversationPanelRef = ref(null);
|
||||
const resizableEditorWrapperRef = ref(null);
|
||||
const messagesViewRef = useTemplateRef('messagesViewRef');
|
||||
const topBannerRef = useTemplateRef('topBannerRef');
|
||||
const { height: containerHeight } = useElementSize(messagesViewRef);
|
||||
const { height: topBannerHeight } = useElementSize(topBannerRef);
|
||||
|
||||
const keyboardEvents = {
|
||||
Escape: {
|
||||
action: () => {
|
||||
isPopOutReplyBox.value = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
useKeyboardEvents(keyboardEvents);
|
||||
|
||||
const {
|
||||
captainTasksEnabled,
|
||||
@@ -64,15 +68,11 @@ export default {
|
||||
provide('contextMenuElementTarget', conversationPanelRef);
|
||||
|
||||
return {
|
||||
isPopOutReplyBox,
|
||||
captainTasksEnabled,
|
||||
getLabelSuggestions,
|
||||
isLabelSuggestionFeatureEnabled,
|
||||
conversationPanelRef,
|
||||
resizableEditorWrapperRef,
|
||||
messagesViewRef,
|
||||
topBannerRef,
|
||||
containerHeight,
|
||||
topBannerHeight,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
@@ -254,7 +254,6 @@ export default {
|
||||
this.fetchAllAttachmentsFromCurrentChat();
|
||||
this.fetchSuggestions();
|
||||
this.messageSentSinceOpened = false;
|
||||
this.resetReplyEditorHeight();
|
||||
},
|
||||
},
|
||||
|
||||
@@ -438,37 +437,26 @@ export default {
|
||||
const payload = useSnakeCase(message);
|
||||
await this.$store.dispatch('sendMessageWithData', payload);
|
||||
},
|
||||
toggleReplyEditorSize() {
|
||||
this.resizableEditorWrapperRef?.toggleEditorExpand?.();
|
||||
},
|
||||
resetReplyEditorHeight() {
|
||||
this.resizableEditorWrapperRef?.resetEditorHeight?.();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="messagesViewRef"
|
||||
class="flex flex-col justify-between flex-grow h-full min-w-0 m-0"
|
||||
>
|
||||
<div ref="topBannerRef">
|
||||
<Banner
|
||||
v-if="!currentChat.can_reply"
|
||||
color-scheme="alert"
|
||||
class="mx-2 mt-2 overflow-hidden rounded-lg"
|
||||
:banner-message="replyWindowBannerMessage"
|
||||
:href-link="replyWindowLink"
|
||||
:href-link-text="replyWindowLinkText"
|
||||
/>
|
||||
<Banner
|
||||
v-else-if="hasDuplicateInstagramInbox"
|
||||
color-scheme="alert"
|
||||
class="mx-2 mt-2 overflow-hidden rounded-lg"
|
||||
:banner-message="$t('CONVERSATION.OLD_INSTAGRAM_INBOX_REPLY_BANNER')"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between flex-grow h-full min-w-0 m-0">
|
||||
<Banner
|
||||
v-if="!currentChat.can_reply"
|
||||
color-scheme="alert"
|
||||
class="mx-2 mt-2 overflow-hidden rounded-lg"
|
||||
:banner-message="replyWindowBannerMessage"
|
||||
:href-link="replyWindowLink"
|
||||
:href-link-text="replyWindowLinkText"
|
||||
/>
|
||||
<Banner
|
||||
v-else-if="hasDuplicateInstagramInbox"
|
||||
color-scheme="alert"
|
||||
class="mx-2 mt-2 overflow-hidden rounded-lg"
|
||||
:banner-message="$t('CONVERSATION.OLD_INSTAGRAM_INBOX_REPLY_BANNER')"
|
||||
/>
|
||||
<MessageList
|
||||
ref="conversationPanelRef"
|
||||
class="conversation-panel flex-shrink flex-grow basis-px flex flex-col overflow-y-auto relative h-full m-0 pb-4"
|
||||
@@ -510,7 +498,13 @@ export default {
|
||||
/>
|
||||
</template>
|
||||
</MessageList>
|
||||
<div class="flex relative flex-col bg-n-surface-1">
|
||||
<div
|
||||
class="flex relative flex-col"
|
||||
:class="{
|
||||
'modal-mask': isPopOutReplyBox,
|
||||
'bg-n-surface-1': !isPopOutReplyBox,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="isAnyoneTyping"
|
||||
class="absolute flex items-center w-full h-0 -top-7"
|
||||
@@ -526,12 +520,42 @@ export default {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ResizableEditorWrapper
|
||||
ref="resizableEditorWrapperRef"
|
||||
:container-height="Math.max(0, containerHeight - topBannerHeight)"
|
||||
>
|
||||
<ReplyBox @toggle-editor-size="toggleReplyEditorSize" />
|
||||
</ResizableEditorWrapper>
|
||||
<ReplyBox
|
||||
:pop-out-reply-box="isPopOutReplyBox"
|
||||
@update:pop-out-reply-box="isPopOutReplyBox = $event"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.modal-mask {
|
||||
@apply fixed;
|
||||
|
||||
&::v-deep {
|
||||
.ProseMirror-woot-style {
|
||||
@apply max-h-[25rem];
|
||||
}
|
||||
|
||||
.reply-box {
|
||||
@apply border border-n-weak max-w-[75rem] w-[70%];
|
||||
|
||||
&.is-private {
|
||||
@apply dark:border-n-amber-3/30 border-n-amber-12/5;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-box .reply-box__top {
|
||||
@apply relative min-h-[27.5rem];
|
||||
}
|
||||
|
||||
.reply-box__top .input {
|
||||
@apply min-h-[27.5rem];
|
||||
}
|
||||
|
||||
.emoji-dialog {
|
||||
@apply absolute ltr:left-auto rtl:right-auto bottom-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -81,7 +81,13 @@ export default {
|
||||
CopilotReplyBottomPanel,
|
||||
},
|
||||
mixins: [inboxMixin, fileUploadMixin, keyboardEventListenerMixins],
|
||||
emits: ['toggleEditorSize'],
|
||||
props: {
|
||||
popOutReplyBox: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:popOutReplyBox'],
|
||||
setup() {
|
||||
const {
|
||||
uiSettings,
|
||||
@@ -791,6 +797,7 @@ export default {
|
||||
|
||||
this.clearMessage();
|
||||
this.hideEmojiPicker();
|
||||
this.$emit('update:popOutReplyBox', false);
|
||||
}
|
||||
},
|
||||
sendMessageAsMultipleMessages(message, copilotAcceptedMessage = '') {
|
||||
@@ -1210,9 +1217,8 @@ export default {
|
||||
file => !file?.isRecordedAudio
|
||||
);
|
||||
},
|
||||
toggleEditorSize() {
|
||||
this.$emit('toggleEditorSize');
|
||||
this.$nextTick(() => this.messageEditor?.focusEditorInputField());
|
||||
togglePopout() {
|
||||
this.$emit('update:popOutReplyBox', !this.popOutReplyBox);
|
||||
},
|
||||
onSubmitCopilotReply() {
|
||||
const acceptedMessage = this.copilot.accept();
|
||||
@@ -1238,8 +1244,9 @@ export default {
|
||||
:is-message-length-reaching-threshold="isMessageLengthReachingThreshold"
|
||||
:characters-remaining="charactersRemaining"
|
||||
:editor-content="message"
|
||||
:popout-reply-box="popOutReplyBox"
|
||||
@set-reply-mode="setReplyMode"
|
||||
@toggle-editor-size="toggleEditorSize"
|
||||
@toggle-popout="togglePopout"
|
||||
@toggle-copilot="copilot.toggleEditor"
|
||||
@execute-copilot-action="executeCopilotAction"
|
||||
/>
|
||||
@@ -1268,7 +1275,7 @@ export default {
|
||||
v-if="showEmojiPicker"
|
||||
v-on-clickaway="hideEmojiPicker"
|
||||
:class="{
|
||||
'emoji-dialog--expanded': isOnExpandedLayout,
|
||||
'emoji-dialog--expanded': isOnExpandedLayout || popOutReplyBox,
|
||||
}"
|
||||
:on-click="addIntoEditor"
|
||||
/>
|
||||
@@ -1292,6 +1299,7 @@ export default {
|
||||
:show-copilot-editor="copilot.showEditor.value"
|
||||
:is-generating-content="copilot.isGenerating.value"
|
||||
:generated-content="copilot.generatedContent.value"
|
||||
:is-popout="popOutReplyBox"
|
||||
:placeholder="$t('CONVERSATION.FOOTER.COPILOT_MSG_INPUT')"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount, useTemplateRef } from 'vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
|
||||
const props = defineProps({
|
||||
containerHeight: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const DEFAULT_HEIGHT = 120;
|
||||
const MIN_HEIGHT = 80;
|
||||
const MIN_MESSAGES_HEIGHT = 200;
|
||||
const EXPAND_RATIO = 0.5;
|
||||
const RESET_DELAY_MS = 120;
|
||||
|
||||
const wrapperRef = useTemplateRef('wrapperRef');
|
||||
const surroundingHeight = ref(0);
|
||||
const editorHeight = ref(DEFAULT_HEIGHT);
|
||||
const isResizing = ref(false);
|
||||
const startY = ref(0);
|
||||
const startHeight = ref(0);
|
||||
let resetTimeoutId = null;
|
||||
|
||||
const clamp = (val, min, max) => Math.min(Math.max(val, min), max);
|
||||
|
||||
// Measure height of elements surrounding the editor (top panel, email fields, bottom panel)
|
||||
const measureSurroundingHeight = () => {
|
||||
if (wrapperRef.value) {
|
||||
surroundingHeight.value = Math.max(
|
||||
0,
|
||||
wrapperRef.value.offsetHeight - editorHeight.value
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const isContainerReady = computed(() => props.containerHeight > 0);
|
||||
|
||||
const sizeBounds = computed(() => {
|
||||
const h = props.containerHeight;
|
||||
const s = surroundingHeight.value;
|
||||
const max = Math.max(MIN_HEIGHT, h - MIN_MESSAGES_HEIGHT - s);
|
||||
const expanded = clamp(Math.floor(h * EXPAND_RATIO - s / 2), MIN_HEIGHT, max);
|
||||
return {
|
||||
min: MIN_HEIGHT,
|
||||
max: isContainerReady.value ? max : DEFAULT_HEIGHT,
|
||||
expanded,
|
||||
default: clamp(DEFAULT_HEIGHT, MIN_HEIGHT, max),
|
||||
};
|
||||
});
|
||||
|
||||
const clampToBounds = val =>
|
||||
clamp(val, sizeBounds.value.min, sizeBounds.value.max);
|
||||
|
||||
const clearDragStyles = () => {
|
||||
Object.assign(document.body.style, { cursor: '', userSelect: '' });
|
||||
};
|
||||
|
||||
const getClientY = e => (e.touches ? e.touches[0].clientY : e.clientY);
|
||||
|
||||
const onResizeStart = event => {
|
||||
editorHeight.value = clampToBounds(editorHeight.value);
|
||||
measureSurroundingHeight();
|
||||
isResizing.value = true;
|
||||
startY.value = getClientY(event);
|
||||
startHeight.value = clampToBounds(editorHeight.value);
|
||||
editorHeight.value = startHeight.value;
|
||||
Object.assign(document.body.style, {
|
||||
cursor: 'row-resize',
|
||||
userSelect: 'none',
|
||||
});
|
||||
};
|
||||
|
||||
const onResizeMove = event => {
|
||||
if (!isResizing.value) return;
|
||||
if (event.touches) event.preventDefault();
|
||||
editorHeight.value = clampToBounds(
|
||||
startHeight.value + startY.value - getClientY(event)
|
||||
);
|
||||
};
|
||||
|
||||
const onResizeEnd = () => {
|
||||
if (!isResizing.value) return;
|
||||
isResizing.value = false;
|
||||
clearDragStyles();
|
||||
};
|
||||
|
||||
const resetEditorHeight = () => {
|
||||
editorHeight.value = sizeBounds.value.default;
|
||||
};
|
||||
|
||||
const toggleEditorExpand = () => {
|
||||
editorHeight.value = clampToBounds(editorHeight.value);
|
||||
measureSurroundingHeight();
|
||||
const { expanded, max, default: defaultHeight } = sizeBounds.value;
|
||||
const isExpanded = editorHeight.value > defaultHeight;
|
||||
// If expanded is too close to default, use max so the toggle is always noticeable
|
||||
const target = expanded - defaultHeight < 100 ? max : expanded;
|
||||
editorHeight.value = isExpanded ? defaultHeight : target;
|
||||
};
|
||||
|
||||
const handleMessageSent = () => {
|
||||
clearTimeout(resetTimeoutId);
|
||||
resetTimeoutId = setTimeout(resetEditorHeight, RESET_DELAY_MS);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(BUS_EVENTS.MESSAGE_SENT, handleMessageSent);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
emitter.off(BUS_EVENTS.MESSAGE_SENT, handleMessageSent);
|
||||
clearTimeout(resetTimeoutId);
|
||||
if (isResizing.value) {
|
||||
isResizing.value = false;
|
||||
clearDragStyles();
|
||||
}
|
||||
});
|
||||
|
||||
useEventListener(document, 'mousemove', onResizeMove);
|
||||
useEventListener(document, 'mouseup', onResizeEnd);
|
||||
useEventListener(document, 'touchmove', onResizeMove, { passive: false });
|
||||
useEventListener(document, 'touchend', onResizeEnd);
|
||||
useEventListener(document, 'touchcancel', onResizeEnd);
|
||||
useEventListener(window, 'blur', onResizeEnd);
|
||||
|
||||
defineExpose({ toggleEditorExpand, resetEditorHeight });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="wrapperRef"
|
||||
class="relative resizable-editor-wrapper"
|
||||
:style="{
|
||||
'--editor-height': editorHeight + 'px',
|
||||
'--editor-min-allowed': sizeBounds.min + 'px',
|
||||
'--editor-max-allowed': sizeBounds.max + 'px',
|
||||
'--editor-height-transition': isResizing ? 'none' : '180ms ease',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="group absolute inset-x-0 -top-4 z-10 flex h-4 cursor-row-resize select-none items-center justify-center bg-gradient-to-b from-transparent from-10% dark:to-n-surface-1/80 to-n-surface-1/90 backdrop-blur-[0.01875rem]"
|
||||
@mousedown="onResizeStart"
|
||||
@touchstart.prevent="onResizeStart"
|
||||
@dblclick="resetEditorHeight"
|
||||
>
|
||||
<div
|
||||
class="w-8 h-0.5 mt-1 rounded-full bg-n-slate-6 group-hover:bg-n-slate-8 transition-all duration-200 motion-safe:group-hover:animate-bounce"
|
||||
:class="{ 'bg-n-slate-8 animate-bounce': isResizing }"
|
||||
/>
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -92,9 +92,7 @@ describe('useAutomation', () => {
|
||||
case 'assign_team':
|
||||
return teams;
|
||||
case 'assign_agent':
|
||||
return options.addNoneToListFn
|
||||
? options.addNoneToListFn(options.agents)
|
||||
: options.agents;
|
||||
return agents;
|
||||
case 'send_email_to_team':
|
||||
return teams;
|
||||
case 'send_message':
|
||||
@@ -242,11 +240,7 @@ describe('useAutomation', () => {
|
||||
|
||||
expect(getActionDropdownValues('add_label')).toEqual(labels);
|
||||
expect(getActionDropdownValues('assign_team')).toEqual(teams);
|
||||
expect(getActionDropdownValues('assign_agent')).toEqual([
|
||||
{ id: 'nil', name: 'AUTOMATION.NONE_OPTION' },
|
||||
{ id: 'last_responding_agent', name: 'AUTOMATION.LAST_RESPONDING_AGENT' },
|
||||
...agents,
|
||||
]);
|
||||
expect(getActionDropdownValues('assign_agent')).toEqual(agents);
|
||||
expect(getActionDropdownValues('send_email_to_team')).toEqual(teams);
|
||||
expect(getActionDropdownValues('send_message')).toEqual([]);
|
||||
expect(getActionDropdownValues('add_sla')).toEqual(slaPolicies);
|
||||
|
||||
@@ -16,20 +16,7 @@ describe('useEditableAutomation', () => {
|
||||
|
||||
return [];
|
||||
}),
|
||||
getActionDropdownValues: vi.fn(actionName => {
|
||||
if (actionName === 'assign_agent') {
|
||||
return [
|
||||
{ id: 'nil', name: 'None' },
|
||||
{
|
||||
id: 'last_responding_agent',
|
||||
name: 'Last Responding Agent',
|
||||
},
|
||||
{ id: 1, name: 'Agent 1' },
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}),
|
||||
getActionDropdownValues: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,35 +51,4 @@ describe('useEditableAutomation', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('rehydrates last responding agent as a selected action option', () => {
|
||||
const automation = {
|
||||
event_name: 'conversation_created',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
action_name: 'assign_agent',
|
||||
action_params: ['last_responding_agent'],
|
||||
},
|
||||
],
|
||||
};
|
||||
const automationActionTypes = [
|
||||
{ key: 'assign_agent', inputType: 'search_select' },
|
||||
];
|
||||
|
||||
const { formatAutomation } = useEditableAutomation();
|
||||
const result = formatAutomation(automation, [], {}, automationActionTypes);
|
||||
|
||||
expect(result.actions).toEqual([
|
||||
{
|
||||
action_name: 'assign_agent',
|
||||
action_params: [
|
||||
{
|
||||
id: 'last_responding_agent',
|
||||
name: 'Last Responding Agent',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,30 +119,24 @@ describe('useMacros', () => {
|
||||
const { getMacroDropdownValues } = useMacros();
|
||||
expect(getMacroDropdownValues('add_label')).toHaveLength(mockLabels.length);
|
||||
expect(getMacroDropdownValues('assign_team')).toHaveLength(
|
||||
mockTeams.length + 1
|
||||
); // +1 for "None"
|
||||
mockTeams.length
|
||||
);
|
||||
expect(getMacroDropdownValues('assign_agent')).toHaveLength(
|
||||
mockAgents.length + 2
|
||||
); // +2 for "None" and "Self"
|
||||
mockAgents.length + 1
|
||||
); // +1 for "Self"
|
||||
});
|
||||
|
||||
it('returns teams with "None" option for assign_team and teams only for send_email_to_team', () => {
|
||||
it('returns teams for assign_team and send_email_to_team types', () => {
|
||||
const { getMacroDropdownValues } = useMacros();
|
||||
const assignTeamResult = getMacroDropdownValues('assign_team');
|
||||
expect(assignTeamResult[0]).toEqual({
|
||||
id: 'nil',
|
||||
name: 'AUTOMATION.NONE_OPTION',
|
||||
});
|
||||
expect(assignTeamResult.slice(1)).toEqual(mockTeams);
|
||||
expect(getMacroDropdownValues('assign_team')).toEqual(mockTeams);
|
||||
expect(getMacroDropdownValues('send_email_to_team')).toEqual(mockTeams);
|
||||
});
|
||||
|
||||
it('returns agents with "None" and "Self" options for assign_agent type', () => {
|
||||
it('returns agents with "Self" option for assign_agent type', () => {
|
||||
const { getMacroDropdownValues } = useMacros();
|
||||
const result = getMacroDropdownValues('assign_agent');
|
||||
expect(result[0]).toEqual({ id: 'nil', name: 'AUTOMATION.NONE_OPTION' });
|
||||
expect(result[1]).toEqual({ id: 'self', name: 'Self' });
|
||||
expect(result.slice(2)).toEqual(mockAgents);
|
||||
expect(result[0]).toEqual({ id: 'self', name: 'Self' });
|
||||
expect(result.slice(1)).toEqual(mockAgents);
|
||||
});
|
||||
|
||||
it('returns formatted labels for add_label and remove_label types', () => {
|
||||
@@ -178,11 +172,8 @@ describe('useMacros', () => {
|
||||
|
||||
const { getMacroDropdownValues } = useMacros();
|
||||
expect(getMacroDropdownValues('add_label')).toEqual([]);
|
||||
expect(getMacroDropdownValues('assign_team')).toEqual([
|
||||
{ id: 'nil', name: 'AUTOMATION.NONE_OPTION' },
|
||||
]);
|
||||
expect(getMacroDropdownValues('assign_team')).toEqual([]);
|
||||
expect(getMacroDropdownValues('assign_agent')).toEqual([
|
||||
{ id: 'nil', name: 'AUTOMATION.NONE_OPTION' },
|
||||
{ id: 'self', name: 'Self' },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -121,19 +121,8 @@ export default function useAutomationValues() {
|
||||
* @returns {Array} An array of action dropdown values.
|
||||
*/
|
||||
const getActionDropdownValues = type => {
|
||||
let agentsList = agents.value;
|
||||
if (type === 'assign_agent') {
|
||||
agentsList = [
|
||||
{
|
||||
id: 'last_responding_agent',
|
||||
name: t('AUTOMATION.LAST_RESPONDING_AGENT'),
|
||||
},
|
||||
...agentsList,
|
||||
];
|
||||
}
|
||||
|
||||
return getActionOptions({
|
||||
agents: agentsList,
|
||||
agents: agents.value,
|
||||
labels: labels.value,
|
||||
teams: teams.value,
|
||||
slaPolicies: slaPolicies.value,
|
||||
|
||||
@@ -12,14 +12,11 @@ import { useKeyboardEvents } from './useKeyboardEvents';
|
||||
|
||||
/**
|
||||
* Wrap the action in a function that calls the action and prevents the default event behavior.
|
||||
* Only prevents default when items are available to navigate.
|
||||
* @param {Function} action - The action to be called.
|
||||
* @param {import('vue').Ref<Array>} items - A ref to the array of selectable items.
|
||||
* @returns {{action: Function, allowOnFocusedInput: boolean}} An object containing the action and a flag to allow the event on focused input.
|
||||
*/
|
||||
const createAction = (action, items) => ({
|
||||
const createAction = action => ({
|
||||
action: e => {
|
||||
if (!items.value?.length) return;
|
||||
action();
|
||||
e.preventDefault();
|
||||
},
|
||||
@@ -41,14 +38,15 @@ const createKeyboardEvents = (
|
||||
items
|
||||
) => {
|
||||
const events = {
|
||||
ArrowUp: createAction(moveSelectionUp, items),
|
||||
'Control+KeyP': createAction(moveSelectionUp, items),
|
||||
ArrowDown: createAction(moveSelectionDown, items),
|
||||
'Control+KeyN': createAction(moveSelectionDown, items),
|
||||
ArrowUp: createAction(moveSelectionUp),
|
||||
'Control+KeyP': createAction(moveSelectionUp),
|
||||
ArrowDown: createAction(moveSelectionDown),
|
||||
'Control+KeyN': createAction(moveSelectionDown),
|
||||
};
|
||||
|
||||
// Adds an event handler for the Enter key if the onSelect function is provided.
|
||||
if (typeof onSelect === 'function') {
|
||||
events.Enter = createAction(onSelect, items);
|
||||
events.Enter = createAction(() => items.value?.length > 0 && onSelect());
|
||||
}
|
||||
|
||||
return events;
|
||||
|
||||
@@ -15,11 +15,6 @@ export const useMacros = () => {
|
||||
const teams = computed(() => getters['teams/getTeams'].value);
|
||||
const agents = computed(() => getters['agents/getVerifiedAgents'].value);
|
||||
|
||||
const withNoneOption = options => [
|
||||
{ id: 'nil', name: t('AUTOMATION.NONE_OPTION') },
|
||||
...(options || []),
|
||||
];
|
||||
|
||||
/**
|
||||
* Get dropdown values based on the specified type
|
||||
* @param {string} type - The type of dropdown values to retrieve
|
||||
@@ -28,15 +23,10 @@ export const useMacros = () => {
|
||||
const getMacroDropdownValues = type => {
|
||||
switch (type) {
|
||||
case 'assign_team':
|
||||
return withNoneOption(teams.value);
|
||||
case 'send_email_to_team':
|
||||
return teams.value;
|
||||
case 'assign_agent':
|
||||
return [
|
||||
...withNoneOption(),
|
||||
{ id: 'self', name: 'Self' },
|
||||
...agents.value,
|
||||
];
|
||||
return [{ id: 'self', name: 'Self' }, ...agents.value];
|
||||
case 'add_label':
|
||||
case 'remove_label':
|
||||
return labels.value.map(i => ({
|
||||
|
||||
@@ -172,7 +172,6 @@ export const FORMATTING = {
|
||||
export const ARTICLE_EDITOR_MENU_OPTIONS = [
|
||||
'strong',
|
||||
'em',
|
||||
'strike',
|
||||
'link',
|
||||
'undo',
|
||||
'redo',
|
||||
@@ -183,7 +182,6 @@ export const ARTICLE_EDITOR_MENU_OPTIONS = [
|
||||
'h3',
|
||||
'imageUpload',
|
||||
'code',
|
||||
'insertTable',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,11 +12,6 @@ export default {
|
||||
SNOOZED: 'snoozed',
|
||||
ALL: 'all',
|
||||
},
|
||||
CONVERSATION_TYPE: {
|
||||
MENTION: 'mention',
|
||||
PARTICIPATING: 'participating',
|
||||
UNATTENDED: 'unattended',
|
||||
},
|
||||
SORT_BY_TYPE: {
|
||||
LAST_ACTIVITY_AT_ASC: 'last_activity_at_asc',
|
||||
LAST_ACTIVITY_AT_DESC: 'last_activity_at_desc',
|
||||
|
||||
@@ -51,7 +51,6 @@ export const conversationListPageURL = ({
|
||||
} else if (conversationType) {
|
||||
const urlMap = {
|
||||
mention: 'mentions/conversations',
|
||||
participating: 'participating/conversations',
|
||||
unattended: 'unattended/conversations',
|
||||
};
|
||||
url = `accounts/${accountId}/${urlMap[conversationType]}`;
|
||||
|
||||
@@ -40,15 +40,6 @@ describe('#URL Helpers', () => {
|
||||
'/app/accounts/1/custom_view/1'
|
||||
);
|
||||
});
|
||||
|
||||
it('should return url to participating conversations', () => {
|
||||
expect(
|
||||
conversationListPageURL({
|
||||
accountId: 1,
|
||||
conversationType: 'participating',
|
||||
})
|
||||
).toBe('/app/accounts/1/participating/conversations');
|
||||
});
|
||||
});
|
||||
describe('conversationUrl', () => {
|
||||
it('should return direct conversation URL if activeInbox is nil', () => {
|
||||
|
||||
@@ -45,10 +45,6 @@ describe('#resolveTeamIds', () => {
|
||||
const resolvedTeams = '⚙️ sales team, 🤷♂️ fayaz';
|
||||
expect(resolveTeamIds(teams, [1, 2])).toEqual(resolvedTeams);
|
||||
});
|
||||
|
||||
it('resolves nil as None', () => {
|
||||
expect(resolveTeamIds(teams, ['nil'])).toEqual('None');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resolveLabels', () => {
|
||||
@@ -63,10 +59,6 @@ describe('#resolveAgents', () => {
|
||||
const resolvedAgents = 'John Doe';
|
||||
expect(resolveAgents(agents, [1])).toEqual(resolvedAgents);
|
||||
});
|
||||
|
||||
it('resolves nil and self values', () => {
|
||||
expect(resolveAgents(agents, ['nil', 'self'])).toEqual('None, Self');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getFileName', () => {
|
||||
|
||||
@@ -125,7 +125,6 @@ const validateSingleAction = action => {
|
||||
'mute_conversation',
|
||||
'snooze_conversation',
|
||||
'resolve_conversation',
|
||||
'remove_assigned_agent',
|
||||
'remove_assigned_team',
|
||||
'open_conversation',
|
||||
'pending_conversation',
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
||||
}
|
||||
},
|
||||
"SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "ምስጢሩን ወደ ክሊፕቦርድ ቅዳ",
|
||||
"COPY_SUCCESS": "ምስጢሩ ወደ ክሊፕቦርድ ተቀድሷል",
|
||||
"TOGGLE": "የምስጢሩን ማየት አሳይ/ደብቅ",
|
||||
"CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
|
||||
"DONE": "ተጠናቀቀ",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "Access Token",
|
||||
"DESCRIPTION": "Copy the access token and save it securely",
|
||||
|
||||
@@ -140,8 +140,6 @@
|
||||
"ACTIONS": {
|
||||
"ASSIGN_AGENT": "Assign to Agent",
|
||||
"ASSIGN_TEAM": "Assign a Team",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
|
||||
@@ -171,7 +169,6 @@
|
||||
},
|
||||
"ATTRIBUTES": {
|
||||
"MESSAGE_TYPE": "Message Type",
|
||||
"PRIVATE_NOTE": "የግል ማስታወሻ",
|
||||
"MESSAGE_CONTAINS": "Message Contains",
|
||||
"EMAIL": "Email",
|
||||
"INBOX": "Inbox",
|
||||
|
||||
@@ -52,17 +52,5 @@
|
||||
},
|
||||
"CHANNEL_SELECTOR": {
|
||||
"COMING_SOON": "Coming Soon!"
|
||||
},
|
||||
"SLASH_COMMANDS": {
|
||||
"HEADING_1": "Heading 1",
|
||||
"HEADING_2": "Heading 2",
|
||||
"HEADING_3": "Heading 3",
|
||||
"BOLD": "Bold",
|
||||
"ITALIC": "Italic",
|
||||
"STRIKETHROUGH": "Strikethrough",
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"CALL": "ደውል",
|
||||
"CALL_INITIATED": "ለእውነተኛው እውቀት መደወል እየተከናወነ ነው…",
|
||||
"CALL_FAILED": "ጥሪውን መጀመር አልቻልንም። እባክዎ ደግመው ይሞክሩ።.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "የድምፅ ኢንቦክስ ይምረጡ"
|
||||
},
|
||||
|
||||
@@ -86,14 +86,6 @@
|
||||
"PLACEHOLDER": "እባክዎ የWebhook URLዎን ያስገቡ",
|
||||
"ERROR": "እባክዎ ትክክለኛ አድራሻ ያስገቡ"
|
||||
},
|
||||
"CHANNEL_WEBHOOK_SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "ምስጢሩን ወደ ክሊፕቦርድ ቅዳ",
|
||||
"COPY_SUCCESS": "ምስጢሩ ወደ ክሊፕቦርድ ተቀድሷል",
|
||||
"TOGGLE": "የምስጢሩን ማየት አሳይ/ደብቅ",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"CHANNEL_DOMAIN": {
|
||||
"LABEL": "የድር ጣቢያ ዶሜን",
|
||||
"PLACEHOLDER": "የድር ጣቢያዎን ዶሜን ያስገቡ (ለምሳሌ፡ acme.com)"
|
||||
|
||||
@@ -837,18 +837,6 @@
|
||||
"SUCCESS_MESSAGE": "ብልጽግና ተጠቃሚ መሣሪያ ተሰርዟል",
|
||||
"ERROR_MESSAGE": "ብልጽግና መሣሪያውን ማስወገድ አልተሳካም"
|
||||
},
|
||||
"PAYWALL": {
|
||||
"TITLE": "Upgrade to use tools with Captain",
|
||||
"AVAILABLE_ON": "Captain Tools are only available in Business and Enterprise plans. Please upgrade to Business plan to use the feature.",
|
||||
"UPGRADE_PROMPT": "",
|
||||
"UPGRADE_NOW": "Open billing",
|
||||
"CANCEL_ANYTIME": ""
|
||||
},
|
||||
"ENTERPRISE_PAYWALL": {
|
||||
"AVAILABLE_ON": "Captain Tools are only available in the paid plans.",
|
||||
"UPGRADE_PROMPT": "Please upgrade to a paid plan to use this feature.",
|
||||
"ASK_ADMIN": "Please reach out to your administrator for the upgrade."
|
||||
},
|
||||
"TEST": {
|
||||
"BUTTON": "Test connection",
|
||||
"SUCCESS": "Endpoint returned HTTP {status}",
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
"ASSIGN_AGENT": "Assign an Agent",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
|
||||
"MUTE_CONVERSATION": "Mute Conversation",
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
"API_SUCCESS": "ፊርማው በተሳካ ሁኔታ ተቀምጧል",
|
||||
"IMAGE_UPLOAD_ERROR": "ምስሉን ማስገባት አልተቻለም! እባክዎ እንደገና ይሞክሩ",
|
||||
"IMAGE_UPLOAD_SUCCESS": "ምስሉ በተሳካ ሁኔታ ታክሏል። እባክዎ ማስቀመጫ ለማስቀመጥ ላይ ይጫኑ",
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "የምስል መጠን ከ{size}MB በታች መሆን አለበት",
|
||||
"INLINE_IMAGE_WARNING": "Pasted inline images were removed. Please use the image upload button to add images to your signature."
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "የምስል መጠን ከ{size}MB በታች መሆን አለበት"
|
||||
},
|
||||
"MESSAGE_SIGNATURE": {
|
||||
"LABEL": "የመልእክት ፊርማ",
|
||||
|
||||
@@ -45,13 +45,6 @@
|
||||
"ERROR_MESSAGE": "Could not connect to Woot server. Please try again."
|
||||
},
|
||||
"SUBMIT": "Create account",
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?",
|
||||
"VERIFY_EMAIL": {
|
||||
"TITLE": "Check your inbox",
|
||||
"DESCRIPTION": "We sent a verification link to {email}. Click the link to verify your email and get started.",
|
||||
"RESEND": "የማረጋገጫ ኢሜል እንደገና ላክ",
|
||||
"RESEND_SUCCESS": "Verification email sent. Please check your inbox.",
|
||||
"RESEND_ERROR": "Could not send verification email. Please try again."
|
||||
}
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"ERROR_MESSAGE": "تعذر تحديث الروبوت. يرجى المحاولة مرة أخرى."
|
||||
}
|
||||
},
|
||||
"SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
|
||||
"DONE": "Done",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "رمز المصادقة",
|
||||
"DESCRIPTION": "Copy the access token and save it securely",
|
||||
|
||||
@@ -140,8 +140,6 @@
|
||||
"ACTIONS": {
|
||||
"ASSIGN_AGENT": "Assign to Agent",
|
||||
"ASSIGN_TEAM": "Assign a Team",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
|
||||
@@ -171,7 +169,6 @@
|
||||
},
|
||||
"ATTRIBUTES": {
|
||||
"MESSAGE_TYPE": "Message Type",
|
||||
"PRIVATE_NOTE": "إضافة ملاحظة خاصة",
|
||||
"MESSAGE_CONTAINS": "Message Contains",
|
||||
"EMAIL": "البريد الإلكتروني",
|
||||
"INBOX": "صندوق الوارد",
|
||||
|
||||
@@ -52,17 +52,5 @@
|
||||
},
|
||||
"CHANNEL_SELECTOR": {
|
||||
"COMING_SOON": "Coming Soon!"
|
||||
},
|
||||
"SLASH_COMMANDS": {
|
||||
"HEADING_1": "Heading 1",
|
||||
"HEADING_2": "Heading 2",
|
||||
"HEADING_3": "Heading 3",
|
||||
"BOLD": "Bold",
|
||||
"ITALIC": "Italic",
|
||||
"STRIKETHROUGH": "Strikethrough",
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "جار الاتصال بجهة الاتصال…",
|
||||
"CALL_FAILED": "تعذر بدء المكالمة. الرجاء المحاولة مرة أخرى.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
},
|
||||
|
||||
@@ -86,14 +86,6 @@
|
||||
"PLACEHOLDER": "Please enter your Webhook URL",
|
||||
"ERROR": "الرجاء إدخال عنوان URL صالح"
|
||||
},
|
||||
"CHANNEL_WEBHOOK_SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"CHANNEL_DOMAIN": {
|
||||
"LABEL": "نطاق الموقع",
|
||||
"PLACEHOLDER": "أدخل نطاق موقعك الإلكتروني (مثال: acme.com)"
|
||||
|
||||
@@ -837,18 +837,6 @@
|
||||
"SUCCESS_MESSAGE": "Custom tool deleted successfully",
|
||||
"ERROR_MESSAGE": "Failed to delete custom tool"
|
||||
},
|
||||
"PAYWALL": {
|
||||
"TITLE": "Upgrade to use tools with Captain",
|
||||
"AVAILABLE_ON": "Captain Tools are only available in Business and Enterprise plans. Please upgrade to Business plan to use the feature.",
|
||||
"UPGRADE_PROMPT": "",
|
||||
"UPGRADE_NOW": "فتح الفواتير",
|
||||
"CANCEL_ANYTIME": ""
|
||||
},
|
||||
"ENTERPRISE_PAYWALL": {
|
||||
"AVAILABLE_ON": "Captain Tools are only available in the paid plans.",
|
||||
"UPGRADE_PROMPT": "Please upgrade to a paid plan to use this feature.",
|
||||
"ASK_ADMIN": "Please reach out to your administrator for the upgrade."
|
||||
},
|
||||
"TEST": {
|
||||
"BUTTON": "Test connection",
|
||||
"SUCCESS": "Endpoint returned HTTP {status}",
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
"ASSIGN_AGENT": "Assign an Agent",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
|
||||
"MUTE_CONVERSATION": "كتم المحادثة",
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
"API_SUCCESS": "تم حفظ التوقيع بنجاح",
|
||||
"IMAGE_UPLOAD_ERROR": "تعذر رفع الصورة! حاول مرة أخرى",
|
||||
"IMAGE_UPLOAD_SUCCESS": "تم إضافة الصورة بنجاح. الرجاء النقر على الحفظ لحفظ التوقيع",
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "حجم الصورة يجب أن يكون أقل من {size}MB",
|
||||
"INLINE_IMAGE_WARNING": "Pasted inline images were removed. Please use the image upload button to add images to your signature."
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "حجم الصورة يجب أن يكون أقل من {size}MB"
|
||||
},
|
||||
"MESSAGE_SIGNATURE": {
|
||||
"LABEL": "توقيع الرسالة",
|
||||
|
||||
@@ -45,13 +45,6 @@
|
||||
"ERROR_MESSAGE": "تعذر الاتصال بالخادم، الرجاء المحاولة مرة أخرى لاحقاً"
|
||||
},
|
||||
"SUBMIT": "إرسال",
|
||||
"HAVE_AN_ACCOUNT": "هل لديك حساب مسبق؟",
|
||||
"VERIFY_EMAIL": {
|
||||
"TITLE": "Check your inbox",
|
||||
"DESCRIPTION": "We sent a verification link to {email}. Click the link to verify your email and get started.",
|
||||
"RESEND": "إعادة إرسال رسالة التحقق",
|
||||
"RESEND_SUCCESS": "Verification email sent. Please check your inbox.",
|
||||
"RESEND_ERROR": "Could not send verification email. Please try again."
|
||||
}
|
||||
"HAVE_AN_ACCOUNT": "هل لديك حساب مسبق؟"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
||||
}
|
||||
},
|
||||
"SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
|
||||
"DONE": "Hazır",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "Access Token",
|
||||
"DESCRIPTION": "Copy the access token and save it securely",
|
||||
|
||||
@@ -140,8 +140,6 @@
|
||||
"ACTIONS": {
|
||||
"ASSIGN_AGENT": "Assign to Agent",
|
||||
"ASSIGN_TEAM": "Assign a Team",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
|
||||
@@ -171,7 +169,6 @@
|
||||
},
|
||||
"ATTRIBUTES": {
|
||||
"MESSAGE_TYPE": "Message Type",
|
||||
"PRIVATE_NOTE": "Şəxsi Qeyd",
|
||||
"MESSAGE_CONTAINS": "Message Contains",
|
||||
"EMAIL": "Email",
|
||||
"INBOX": "Inbox",
|
||||
|
||||
@@ -52,17 +52,5 @@
|
||||
},
|
||||
"CHANNEL_SELECTOR": {
|
||||
"COMING_SOON": "Coming Soon!"
|
||||
},
|
||||
"SLASH_COMMANDS": {
|
||||
"HEADING_1": "Heading 1",
|
||||
"HEADING_2": "Heading 2",
|
||||
"HEADING_3": "Heading 3",
|
||||
"BOLD": "Bold",
|
||||
"ITALIC": "Italic",
|
||||
"STRIKETHROUGH": "Strikethrough",
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"CALL": "Zəng et",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
},
|
||||
|
||||
@@ -698,7 +698,7 @@
|
||||
"EDIT_ARTICLE": {
|
||||
"MORE_PROPERTIES": "More properties",
|
||||
"UNCATEGORIZED": "Uncategorized",
|
||||
"EDITOR_PLACEHOLDER": "Write your content here. Type '/' for formatting options."
|
||||
"EDITOR_PLACEHOLDER": "Write something..."
|
||||
},
|
||||
"ARTICLE_PROPERTIES": {
|
||||
"ARTICLE_PROPERTIES": "Article properties",
|
||||
|
||||
@@ -86,14 +86,6 @@
|
||||
"PLACEHOLDER": "Zəhmət olmasa Webhook URL-ni daxil edin",
|
||||
"ERROR": "Zəhmət olmasa düzgün URL daxil edin"
|
||||
},
|
||||
"CHANNEL_WEBHOOK_SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"CHANNEL_DOMAIN": {
|
||||
"LABEL": "Veb sayt Domeni",
|
||||
"PLACEHOLDER": "Veb sayt domeninizi daxil edin (məsələn: acme.com)"
|
||||
|
||||
@@ -837,18 +837,6 @@
|
||||
"SUCCESS_MESSAGE": "Xüsusi alət uğurla silindi",
|
||||
"ERROR_MESSAGE": "Xüsusi alət silinmədi"
|
||||
},
|
||||
"PAYWALL": {
|
||||
"TITLE": "Upgrade to use tools with Captain",
|
||||
"AVAILABLE_ON": "Captain Tools are only available in Business and Enterprise plans. Please upgrade to Business plan to use the feature.",
|
||||
"UPGRADE_PROMPT": "",
|
||||
"UPGRADE_NOW": "Open billing",
|
||||
"CANCEL_ANYTIME": ""
|
||||
},
|
||||
"ENTERPRISE_PAYWALL": {
|
||||
"AVAILABLE_ON": "Captain Tools are only available in the paid plans.",
|
||||
"UPGRADE_PROMPT": "Please upgrade to a paid plan to use this feature.",
|
||||
"ASK_ADMIN": "Please reach out to your administrator for the upgrade."
|
||||
},
|
||||
"TEST": {
|
||||
"BUTTON": "Bağlantını yoxla",
|
||||
"SUCCESS": "Endpoint HTTP {status} qaytardı",
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
"ASSIGN_AGENT": "Assign an Agent",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
|
||||
"MUTE_CONVERSATION": "Mute Conversation",
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
"API_SUCCESS": "İmza uğurla yadda saxlanıldı",
|
||||
"IMAGE_UPLOAD_ERROR": "Şəkil yüklənmədi! Yenidən cəhd edin",
|
||||
"IMAGE_UPLOAD_SUCCESS": "Şəkil uğurla əlavə edildi. İmzanı saxlamaq üçün zəhmət olmasa Saxla düyməsini basın",
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "Şəkil ölçüsü {size}MB-dan az olmalıdır",
|
||||
"INLINE_IMAGE_WARNING": "Pasted inline images were removed. Please use the image upload button to add images to your signature."
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "Şəkil ölçüsü {size}MB-dan az olmalıdır"
|
||||
},
|
||||
"MESSAGE_SIGNATURE": {
|
||||
"LABEL": "Mesaj imzası",
|
||||
|
||||
@@ -45,13 +45,6 @@
|
||||
"ERROR_MESSAGE": "Could not connect to Woot server. Please try again."
|
||||
},
|
||||
"SUBMIT": "Create account",
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?",
|
||||
"VERIFY_EMAIL": {
|
||||
"TITLE": "Check your inbox",
|
||||
"DESCRIPTION": "We sent a verification link to {email}. Click the link to verify your email and get started.",
|
||||
"RESEND": "Təsdiq e-poçtunu yenidən göndər",
|
||||
"RESEND_SUCCESS": "Verification email sent. Please check your inbox.",
|
||||
"RESEND_ERROR": "Could not send verification email. Please try again."
|
||||
}
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
||||
}
|
||||
},
|
||||
"SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
|
||||
"DONE": "Done",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "Access Token",
|
||||
"DESCRIPTION": "Copy the access token and save it securely",
|
||||
|
||||
@@ -140,8 +140,6 @@
|
||||
"ACTIONS": {
|
||||
"ASSIGN_AGENT": "Assign to Agent",
|
||||
"ASSIGN_TEAM": "Assign a Team",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
|
||||
@@ -171,7 +169,6 @@
|
||||
},
|
||||
"ATTRIBUTES": {
|
||||
"MESSAGE_TYPE": "Message Type",
|
||||
"PRIVATE_NOTE": "Private Note",
|
||||
"MESSAGE_CONTAINS": "Message Contains",
|
||||
"EMAIL": "Email",
|
||||
"INBOX": "Входяща кутия",
|
||||
|
||||
@@ -52,17 +52,5 @@
|
||||
},
|
||||
"CHANNEL_SELECTOR": {
|
||||
"COMING_SOON": "Coming Soon!"
|
||||
},
|
||||
"SLASH_COMMANDS": {
|
||||
"HEADING_1": "Heading 1",
|
||||
"HEADING_2": "Heading 2",
|
||||
"HEADING_3": "Heading 3",
|
||||
"BOLD": "Bold",
|
||||
"ITALIC": "Italic",
|
||||
"STRIKETHROUGH": "Strikethrough",
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
},
|
||||
|
||||
@@ -86,14 +86,6 @@
|
||||
"PLACEHOLDER": "Please enter your Webhook URL",
|
||||
"ERROR": "Please enter a valid URL"
|
||||
},
|
||||
"CHANNEL_WEBHOOK_SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"CHANNEL_DOMAIN": {
|
||||
"LABEL": "Website Domain",
|
||||
"PLACEHOLDER": "Enter your website domain (eg: acme.com)"
|
||||
|
||||
@@ -837,18 +837,6 @@
|
||||
"SUCCESS_MESSAGE": "Custom tool deleted successfully",
|
||||
"ERROR_MESSAGE": "Failed to delete custom tool"
|
||||
},
|
||||
"PAYWALL": {
|
||||
"TITLE": "Upgrade to use tools with Captain",
|
||||
"AVAILABLE_ON": "Captain Tools are only available in Business and Enterprise plans. Please upgrade to Business plan to use the feature.",
|
||||
"UPGRADE_PROMPT": "",
|
||||
"UPGRADE_NOW": "Open billing",
|
||||
"CANCEL_ANYTIME": ""
|
||||
},
|
||||
"ENTERPRISE_PAYWALL": {
|
||||
"AVAILABLE_ON": "Captain Tools are only available in the paid plans.",
|
||||
"UPGRADE_PROMPT": "Please upgrade to a paid plan to use this feature.",
|
||||
"ASK_ADMIN": "Please reach out to your administrator for the upgrade."
|
||||
},
|
||||
"TEST": {
|
||||
"BUTTON": "Test connection",
|
||||
"SUCCESS": "Endpoint returned HTTP {status}",
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
"ASSIGN_AGENT": "Assign an Agent",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
|
||||
"MUTE_CONVERSATION": "Заглушаване на разговора",
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
"API_SUCCESS": "Signature saved successfully",
|
||||
"IMAGE_UPLOAD_ERROR": "Couldn't upload image! Try again",
|
||||
"IMAGE_UPLOAD_SUCCESS": "Image added successfully. Please click on save to save the signature",
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "Image size should be less than {size}MB",
|
||||
"INLINE_IMAGE_WARNING": "Pasted inline images were removed. Please use the image upload button to add images to your signature."
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "Image size should be less than {size}MB"
|
||||
},
|
||||
"MESSAGE_SIGNATURE": {
|
||||
"LABEL": "Message Signature",
|
||||
|
||||
@@ -45,13 +45,6 @@
|
||||
"ERROR_MESSAGE": "Не можа да се свърже с Woot сървър. Моля, опитайте отново по-късно"
|
||||
},
|
||||
"SUBMIT": "Create account",
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?",
|
||||
"VERIFY_EMAIL": {
|
||||
"TITLE": "Check your inbox",
|
||||
"DESCRIPTION": "We sent a verification link to {email}. Click the link to verify your email and get started.",
|
||||
"RESEND": "Resend verification email",
|
||||
"RESEND_SUCCESS": "Verification email sent. Please check your inbox.",
|
||||
"RESEND_ERROR": "Could not send verification email. Please try again."
|
||||
}
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
||||
}
|
||||
},
|
||||
"SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "গোপন কোড ক্লিপবোর্ডে কপি করুন",
|
||||
"COPY_SUCCESS": "গোপন কোড ক্লিপবোর্ডে কপি হয়েছে",
|
||||
"TOGGLE": "গোপন কোডের দৃশ্যমানতা টগল করুন",
|
||||
"CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
|
||||
"DONE": "সম্পন্ন",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "Access Token",
|
||||
"DESCRIPTION": "Copy the access token and save it securely",
|
||||
|
||||
@@ -140,8 +140,6 @@
|
||||
"ACTIONS": {
|
||||
"ASSIGN_AGENT": "Assign to Agent",
|
||||
"ASSIGN_TEAM": "Assign a Team",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
|
||||
@@ -171,7 +169,6 @@
|
||||
},
|
||||
"ATTRIBUTES": {
|
||||
"MESSAGE_TYPE": "Message Type",
|
||||
"PRIVATE_NOTE": "ব্যক্তিগত নোট",
|
||||
"MESSAGE_CONTAINS": "Message Contains",
|
||||
"EMAIL": "Email",
|
||||
"INBOX": "Inbox",
|
||||
|
||||
@@ -52,17 +52,5 @@
|
||||
},
|
||||
"CHANNEL_SELECTOR": {
|
||||
"COMING_SOON": "Coming Soon!"
|
||||
},
|
||||
"SLASH_COMMANDS": {
|
||||
"HEADING_1": "Heading 1",
|
||||
"HEADING_2": "Heading 2",
|
||||
"HEADING_3": "Heading 3",
|
||||
"BOLD": "Bold",
|
||||
"ITALIC": "Italic",
|
||||
"STRIKETHROUGH": "Strikethrough",
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
},
|
||||
|
||||
@@ -86,14 +86,6 @@
|
||||
"PLACEHOLDER": "অনুগ্রহ করে আপনার Webhook URL লিখুন",
|
||||
"ERROR": "অনুগ্রহ করে একটি বৈধ URL দিন"
|
||||
},
|
||||
"CHANNEL_WEBHOOK_SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "গোপন কোড ক্লিপবোর্ডে কপি করুন",
|
||||
"COPY_SUCCESS": "গোপন কোড ক্লিপবোর্ডে কপি হয়েছে",
|
||||
"TOGGLE": "গোপন কোডের দৃশ্যমানতা টগল করুন",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"CHANNEL_DOMAIN": {
|
||||
"LABEL": "ওয়েবসাইট ডোমেইন",
|
||||
"PLACEHOLDER": "আপনার ওয়েবসাইট ডোমেইন লিখুন (যেমন: acme.com)"
|
||||
|
||||
@@ -837,18 +837,6 @@
|
||||
"SUCCESS_MESSAGE": "কাস্টম টুল সফলভাবে মুছে ফেলা হয়েছে",
|
||||
"ERROR_MESSAGE": "কাস্টম টুল মুছে ফেলা যায়নি"
|
||||
},
|
||||
"PAYWALL": {
|
||||
"TITLE": "Upgrade to use tools with Captain",
|
||||
"AVAILABLE_ON": "Captain Tools are only available in Business and Enterprise plans. Please upgrade to Business plan to use the feature.",
|
||||
"UPGRADE_PROMPT": "",
|
||||
"UPGRADE_NOW": "Open billing",
|
||||
"CANCEL_ANYTIME": ""
|
||||
},
|
||||
"ENTERPRISE_PAYWALL": {
|
||||
"AVAILABLE_ON": "Captain Tools are only available in the paid plans.",
|
||||
"UPGRADE_PROMPT": "Please upgrade to a paid plan to use this feature.",
|
||||
"ASK_ADMIN": "Please reach out to your administrator for the upgrade."
|
||||
},
|
||||
"TEST": {
|
||||
"BUTTON": "Test connection",
|
||||
"SUCCESS": "Endpoint returned HTTP {status}",
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
"ASSIGN_AGENT": "Assign an Agent",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
|
||||
"MUTE_CONVERSATION": "Mute Conversation",
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
"API_SUCCESS": "স্বাক্ষর সফলভাবে সংরক্ষণ হয়েছে",
|
||||
"IMAGE_UPLOAD_ERROR": "ছবি আপলোড করা সম্ভব হয়নি! আবার চেষ্টা করুন",
|
||||
"IMAGE_UPLOAD_SUCCESS": "ছবি সফলভাবে যোগ হয়েছে। স্বাক্ষর সংরক্ষণ করতে অনুগ্রহ করে সেভ-এ ক্লিক করুন।",
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "ছবির আকার {size}MB-এর কম হতে হবে",
|
||||
"INLINE_IMAGE_WARNING": "Pasted inline images were removed. Please use the image upload button to add images to your signature."
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "ছবির আকার {size}MB-এর কম হতে হবে"
|
||||
},
|
||||
"MESSAGE_SIGNATURE": {
|
||||
"LABEL": "বার্তার স্বাক্ষর",
|
||||
|
||||
@@ -45,13 +45,6 @@
|
||||
"ERROR_MESSAGE": "Could not connect to Woot server. Please try again."
|
||||
},
|
||||
"SUBMIT": "Create account",
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?",
|
||||
"VERIFY_EMAIL": {
|
||||
"TITLE": "Check your inbox",
|
||||
"DESCRIPTION": "We sent a verification link to {email}. Click the link to verify your email and get started.",
|
||||
"RESEND": "যাচাইকরণ ইমেল পুনরায় পাঠান",
|
||||
"RESEND_SUCCESS": "Verification email sent. Please check your inbox.",
|
||||
"RESEND_ERROR": "Could not send verification email. Please try again."
|
||||
}
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"ERROR_MESSAGE": "No s'ha pogut actualitzar el bot. Torneu-ho a provar."
|
||||
}
|
||||
},
|
||||
"SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
|
||||
"DONE": "Done",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "Token d'accés",
|
||||
"DESCRIPTION": "Copy the access token and save it securely",
|
||||
|
||||
@@ -140,8 +140,6 @@
|
||||
"ACTIONS": {
|
||||
"ASSIGN_AGENT": "Assign to Agent",
|
||||
"ASSIGN_TEAM": "Assign a Team",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
|
||||
@@ -171,7 +169,6 @@
|
||||
},
|
||||
"ATTRIBUTES": {
|
||||
"MESSAGE_TYPE": "Message Type",
|
||||
"PRIVATE_NOTE": "Nota privada",
|
||||
"MESSAGE_CONTAINS": "Message Contains",
|
||||
"EMAIL": "Correu electrònic",
|
||||
"INBOX": "Safata d'entrada",
|
||||
|
||||
@@ -52,17 +52,5 @@
|
||||
},
|
||||
"CHANNEL_SELECTOR": {
|
||||
"COMING_SOON": "Coming Soon!"
|
||||
},
|
||||
"SLASH_COMMANDS": {
|
||||
"HEADING_1": "Heading 1",
|
||||
"HEADING_2": "Heading 2",
|
||||
"HEADING_3": "Heading 3",
|
||||
"BOLD": "Bold",
|
||||
"ITALIC": "Italic",
|
||||
"STRIKETHROUGH": "Strikethrough",
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
},
|
||||
|
||||
@@ -86,14 +86,6 @@
|
||||
"PLACEHOLDER": "Introdueix l'URL del teu webhook",
|
||||
"ERROR": "Introduïu una URL vàlid"
|
||||
},
|
||||
"CHANNEL_WEBHOOK_SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"CHANNEL_DOMAIN": {
|
||||
"LABEL": "Domini del lloc web",
|
||||
"PLACEHOLDER": "Introduïu el vostre domini de lloc web (pe: acme.com)"
|
||||
|
||||
@@ -837,18 +837,6 @@
|
||||
"SUCCESS_MESSAGE": "Custom tool deleted successfully",
|
||||
"ERROR_MESSAGE": "Failed to delete custom tool"
|
||||
},
|
||||
"PAYWALL": {
|
||||
"TITLE": "Upgrade to use tools with Captain",
|
||||
"AVAILABLE_ON": "Captain Tools are only available in Business and Enterprise plans. Please upgrade to Business plan to use the feature.",
|
||||
"UPGRADE_PROMPT": "",
|
||||
"UPGRADE_NOW": "Obrir facturació",
|
||||
"CANCEL_ANYTIME": ""
|
||||
},
|
||||
"ENTERPRISE_PAYWALL": {
|
||||
"AVAILABLE_ON": "Captain Tools are only available in the paid plans.",
|
||||
"UPGRADE_PROMPT": "Please upgrade to a paid plan to use this feature.",
|
||||
"ASK_ADMIN": "Posa't en contacte amb el vostre administrador per obtenir l'actualització."
|
||||
},
|
||||
"TEST": {
|
||||
"BUTTON": "Test connection",
|
||||
"SUCCESS": "Endpoint returned HTTP {status}",
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
"ASSIGN_AGENT": "Assign an Agent",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
|
||||
"MUTE_CONVERSATION": "Silencia la conversa",
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
"API_SUCCESS": "La signatura s'ha desat correctament",
|
||||
"IMAGE_UPLOAD_ERROR": "No s'ha pogut carregar la imatge! Torna-ho a provar",
|
||||
"IMAGE_UPLOAD_SUCCESS": "La imatge s'ha afegit correctament. Fes clic a desa per desar la signatura",
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "La mida de la imatge ha de ser inferior a {size}MB",
|
||||
"INLINE_IMAGE_WARNING": "Pasted inline images were removed. Please use the image upload button to add images to your signature."
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "La mida de la imatge ha de ser inferior a {size}MB"
|
||||
},
|
||||
"MESSAGE_SIGNATURE": {
|
||||
"LABEL": "Signatura del missatge",
|
||||
|
||||
@@ -45,13 +45,6 @@
|
||||
"ERROR_MESSAGE": "No s'ha pogut connectar amb el servidor Woot. Torna-ho a provar més endavant"
|
||||
},
|
||||
"SUBMIT": "Crear un compte",
|
||||
"HAVE_AN_ACCOUNT": "Ja tens un compte?",
|
||||
"VERIFY_EMAIL": {
|
||||
"TITLE": "Check your inbox",
|
||||
"DESCRIPTION": "We sent a verification link to {email}. Click the link to verify your email and get started.",
|
||||
"RESEND": "Reenviar correu electrònic de verificació",
|
||||
"RESEND_SUCCESS": "Verification email sent. Please check your inbox.",
|
||||
"RESEND_ERROR": "Could not send verification email. Please try again."
|
||||
}
|
||||
"HAVE_AN_ACCOUNT": "Ja tens un compte?"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
||||
}
|
||||
},
|
||||
"SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
|
||||
"DONE": "Done",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "Přístupový token",
|
||||
"DESCRIPTION": "Copy the access token and save it securely",
|
||||
|
||||
@@ -140,8 +140,6 @@
|
||||
"ACTIONS": {
|
||||
"ASSIGN_AGENT": "Přiřadit agentovi",
|
||||
"ASSIGN_TEAM": "Přiřadit tým",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"ADD_LABEL": "Přidat štítek",
|
||||
"REMOVE_LABEL": "Odebrat štítek",
|
||||
"SEND_EMAIL_TO_TEAM": "Poslat e-mail týmu",
|
||||
@@ -171,7 +169,6 @@
|
||||
},
|
||||
"ATTRIBUTES": {
|
||||
"MESSAGE_TYPE": "Typ zprávy",
|
||||
"PRIVATE_NOTE": "Soukromá poznámka",
|
||||
"MESSAGE_CONTAINS": "Zpráva obsahuje",
|
||||
"EMAIL": "E-mailová adresa",
|
||||
"INBOX": "Inbox",
|
||||
|
||||
@@ -52,17 +52,5 @@
|
||||
},
|
||||
"CHANNEL_SELECTOR": {
|
||||
"COMING_SOON": "Coming Soon!"
|
||||
},
|
||||
"SLASH_COMMANDS": {
|
||||
"HEADING_1": "Heading 1",
|
||||
"HEADING_2": "Heading 2",
|
||||
"HEADING_3": "Heading 3",
|
||||
"BOLD": "Bold",
|
||||
"ITALIC": "Italic",
|
||||
"STRIKETHROUGH": "Strikethrough",
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
},
|
||||
|
||||
@@ -86,14 +86,6 @@
|
||||
"PLACEHOLDER": "Please enter your Webhook URL",
|
||||
"ERROR": "Zadejte prosím platnou URL"
|
||||
},
|
||||
"CHANNEL_WEBHOOK_SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"CHANNEL_DOMAIN": {
|
||||
"LABEL": "Doména webových stránek",
|
||||
"PLACEHOLDER": "Zadejte doménu webu (např. acme.com)"
|
||||
|
||||
@@ -837,18 +837,6 @@
|
||||
"SUCCESS_MESSAGE": "Custom tool deleted successfully",
|
||||
"ERROR_MESSAGE": "Failed to delete custom tool"
|
||||
},
|
||||
"PAYWALL": {
|
||||
"TITLE": "Upgrade to use tools with Captain",
|
||||
"AVAILABLE_ON": "Captain Tools are only available in Business and Enterprise plans. Please upgrade to Business plan to use the feature.",
|
||||
"UPGRADE_PROMPT": "",
|
||||
"UPGRADE_NOW": "Open billing",
|
||||
"CANCEL_ANYTIME": ""
|
||||
},
|
||||
"ENTERPRISE_PAYWALL": {
|
||||
"AVAILABLE_ON": "Captain Tools are only available in the paid plans.",
|
||||
"UPGRADE_PROMPT": "Please upgrade to a paid plan to use this feature.",
|
||||
"ASK_ADMIN": "Please reach out to your administrator for the upgrade."
|
||||
},
|
||||
"TEST": {
|
||||
"BUTTON": "Test connection",
|
||||
"SUCCESS": "Endpoint returned HTTP {status}",
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
"ASSIGN_AGENT": "Přiřadit agenta",
|
||||
"ADD_LABEL": "Přidat štítek",
|
||||
"REMOVE_LABEL": "Odebrat štítek",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
|
||||
"MUTE_CONVERSATION": "Ztlumit konverzaci",
|
||||
|
||||
@@ -68,8 +68,7 @@
|
||||
"API_SUCCESS": "Signature saved successfully",
|
||||
"IMAGE_UPLOAD_ERROR": "Couldn't upload image! Try again",
|
||||
"IMAGE_UPLOAD_SUCCESS": "Image added successfully. Please click on save to save the signature",
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "Image size should be less than {size}MB",
|
||||
"INLINE_IMAGE_WARNING": "Pasted inline images were removed. Please use the image upload button to add images to your signature."
|
||||
"IMAGE_UPLOAD_SIZE_ERROR": "Image size should be less than {size}MB"
|
||||
},
|
||||
"MESSAGE_SIGNATURE": {
|
||||
"LABEL": "Message Signature",
|
||||
|
||||
@@ -45,13 +45,6 @@
|
||||
"ERROR_MESSAGE": "Nelze se připojit k Woot serveru, opakujte akci později"
|
||||
},
|
||||
"SUBMIT": "Create account",
|
||||
"HAVE_AN_ACCOUNT": "Máte již účet?",
|
||||
"VERIFY_EMAIL": {
|
||||
"TITLE": "Check your inbox",
|
||||
"DESCRIPTION": "We sent a verification link to {email}. Click the link to verify your email and get started.",
|
||||
"RESEND": "Resend verification email",
|
||||
"RESEND_SUCCESS": "Verification email sent. Please check your inbox.",
|
||||
"RESEND_ERROR": "Could not send verification email. Please try again."
|
||||
}
|
||||
"HAVE_AN_ACCOUNT": "Máte již účet?"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"ERROR_MESSAGE": "Could not update bot. Please try again."
|
||||
}
|
||||
},
|
||||
"SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
|
||||
"DONE": "Done",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "Adgangs Token",
|
||||
"DESCRIPTION": "Copy the access token and save it securely",
|
||||
|
||||
@@ -140,8 +140,6 @@
|
||||
"ACTIONS": {
|
||||
"ASSIGN_AGENT": "Assign to Agent",
|
||||
"ASSIGN_TEAM": "Assign a Team",
|
||||
"REMOVE_ASSIGNED_AGENT": "Remove Assigned Agent",
|
||||
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
|
||||
"ADD_LABEL": "Add a Label",
|
||||
"REMOVE_LABEL": "Remove a Label",
|
||||
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
|
||||
@@ -171,7 +169,6 @@
|
||||
},
|
||||
"ATTRIBUTES": {
|
||||
"MESSAGE_TYPE": "Message Type",
|
||||
"PRIVATE_NOTE": "Privat Note",
|
||||
"MESSAGE_CONTAINS": "Message Contains",
|
||||
"EMAIL": "E-mail",
|
||||
"INBOX": "Indbakke",
|
||||
|
||||
@@ -52,17 +52,5 @@
|
||||
},
|
||||
"CHANNEL_SELECTOR": {
|
||||
"COMING_SOON": "Coming Soon!"
|
||||
},
|
||||
"SLASH_COMMANDS": {
|
||||
"HEADING_1": "Heading 1",
|
||||
"HEADING_2": "Heading 2",
|
||||
"HEADING_3": "Heading 3",
|
||||
"BOLD": "Bold",
|
||||
"ITALIC": "Italic",
|
||||
"STRIKETHROUGH": "Strikethrough",
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
},
|
||||
|
||||
@@ -86,14 +86,6 @@
|
||||
"PLACEHOLDER": "Please enter your Webhook URL",
|
||||
"ERROR": "Angiv en gyldig URL"
|
||||
},
|
||||
"CHANNEL_WEBHOOK_SECRET": {
|
||||
"LABEL": "Webhook Secret",
|
||||
"COPY": "Copy secret to clipboard",
|
||||
"COPY_SUCCESS": "Secret copied to clipboard",
|
||||
"TOGGLE": "Toggle secret visibility",
|
||||
"RESET_SUCCESS": "Webhook secret regenerated successfully",
|
||||
"RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
|
||||
},
|
||||
"CHANNEL_DOMAIN": {
|
||||
"LABEL": "Hjemmeside Domæne",
|
||||
"PLACEHOLDER": "Indtast dit website domæne (fx: ditfirma.dk)"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user