Compare commits

..
Author SHA1 Message Date
Pranav 320e7ea246 Update paywall restrictions 2025-03-05 18:43:57 -08:00
121 changed files with 519 additions and 1085 deletions
+1 -1
View File
@@ -561,7 +561,7 @@ GEM
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.8.1)
rack (2.2.12)
rack (2.2.11)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-contrib (2.5.0)
@@ -6,8 +6,6 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
before_action :conversation, except: [:index, :meta, :search, :create, :filter]
before_action :inbox, :contact, :contact_inbox, only: [:create]
ATTACHMENT_RESULTS_PER_PAGE = 100
def index
result = conversation_finder.perform
@conversations = result[:conversations]
@@ -26,12 +24,7 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
end
def attachments
@attachments_count = @conversation.attachments.count
@attachments = @conversation.attachments
.includes(:message)
.order(created_at: :desc)
.page(attachment_params[:page])
.per(ATTACHMENT_RESULTS_PER_PAGE)
end
def show; end
@@ -131,10 +124,6 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
params.permit(:priority)
end
def attachment_params
params.permit(:page)
end
def update_last_seen_on_conversation(last_seen_at, update_assignee)
# rubocop:disable Rails/SkipsModelValidations
@conversation.update_column(:agent_last_seen_at, last_seen_at)
+14 -19
View File
@@ -5,11 +5,10 @@ module SwitchLocale
def switch_locale(&)
# priority is for locale set in query string (mostly for widget/from js sdk)
locale ||= params[:locale]
locale ||= locale_from_params
locale ||= locale_from_custom_domain
# if locale is not set in account, let's use DEFAULT_LOCALE env variable
locale ||= ENV.fetch('DEFAULT_LOCALE', nil)
locale ||= locale_from_env_variable
set_locale(locale, &)
end
@@ -33,30 +32,26 @@ module SwitchLocale
end
def set_locale(locale, &)
safe_locale = validate_and_get_locale(locale)
# if locale is empty, use default_locale
locale ||= I18n.default_locale
# Ensure locale won't bleed into other requests
# https://guides.rubyonrails.org/i18n.html#managing-the-locale-across-requests
I18n.with_locale(safe_locale, &)
I18n.with_locale(locale, &)
end
def validate_and_get_locale(locale)
return I18n.default_locale.to_s if locale.blank?
available_locales = I18n.available_locales.map(&:to_s)
locale_without_variant = locale.split('_')[0]
if available_locales.include?(locale)
locale
elsif available_locales.include?(locale_without_variant)
locale_without_variant
else
I18n.default_locale.to_s
end
def locale_from_params
I18n.available_locales.map(&:to_s).include?(params[:locale]) ? params[:locale] : nil
end
def locale_from_account(account)
return unless account
account.locale
I18n.available_locales.map(&:to_s).include?(account.locale) ? account.locale : nil
end
def locale_from_env_variable
return unless ENV.fetch('DEFAULT_LOCALE', nil)
I18n.available_locales.map(&:to_s).include?(ENV.fetch('DEFAULT_LOCALE')) ? ENV.fetch('DEFAULT_LOCALE') : nil
end
end
@@ -1,6 +1,4 @@
class Public::Api::V1::Portals::BaseController < PublicController
include SwitchLocale
before_action :show_plain_layout
before_action :set_color_scheme
before_action :set_global_config
@@ -29,7 +27,14 @@ class Public::Api::V1::Portals::BaseController < PublicController
end
def switch_locale_with_portal(&)
@locale = validate_and_get_locale(params[:locale])
locale_without_variant = params[:locale].split('_')[0]
is_locale_available = I18n.available_locales.map(&:to_s).include?(params[:locale])
is_locale_variant_available = I18n.available_locales.map(&:to_s).include?(locale_without_variant)
if is_locale_available
@locale = params[:locale]
elsif is_locale_variant_available
@locale = locale_without_variant
end
I18n.with_locale(@locale, &)
end
@@ -39,12 +44,12 @@ class Public::Api::V1::Portals::BaseController < PublicController
Rails.logger.info "Article: not found for slug: #{params[:article_slug]}"
render_404 && return if article.blank?
article_locale = if article.category.present?
article.category.locale
else
article.portal.default_locale
end
@locale = validate_and_get_locale(article_locale)
@locale = if article.category.present?
article.category.locale
else
article.portal.default_locale
end
I18n.with_locale(@locale, &)
end
-3
View File
@@ -4,7 +4,6 @@ import AddAccountModal from '../dashboard/components/layout/sidebarComponents/Ad
import LoadingState from './components/widgets/LoadingState.vue';
import NetworkNotification from './components/NetworkNotification.vue';
import UpdateBanner from './components/app/UpdateBanner.vue';
import UpgradeBanner from './components/app/UpgradeBanner.vue';
import PaymentPendingBanner from './components/app/PaymentPendingBanner.vue';
import PendingEmailVerificationBanner from './components/app/PendingEmailVerificationBanner.vue';
import vueActionCable from './helper/actionCable';
@@ -31,7 +30,6 @@ export default {
UpdateBanner,
PaymentPendingBanner,
WootSnackbarBox,
UpgradeBanner,
PendingEmailVerificationBanner,
},
setup() {
@@ -146,7 +144,6 @@ export default {
<template v-if="currentAccountId">
<PendingEmailVerificationBanner v-if="hideOnOnboardingView" />
<PaymentPendingBanner v-if="hideOnOnboardingView" />
<UpgradeBanner />
</template>
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
@@ -146,19 +146,17 @@
--solid-2: 255 255 255;
--solid-3: 255 255 255;
--solid-active: 255 255 255;
--solid-amber: 255 228 181;
--solid-blue: 182 216 255;
--solid-amber: 252 232 193;
--solid-blue: 218 236 255;
--solid-iris: 230 231 255;
--solid-purple: 202 202 255;
--solid-red: 255 212 219;
--alpha-1: 67, 67, 67, 0.06;
--alpha-2: 196, 197, 198, 0.22;
--alpha-2: 201, 202, 207, 0.15;
--alpha-3: 255, 255, 255, 0.96;
--black-alpha-1: 0, 0, 0, 0.08;
--black-alpha-1: 0, 0, 0, 0.12;
--black-alpha-2: 0, 0, 0, 0.04;
--border-blue: 39, 129, 246, 0.5;
--white-alpha: 255, 255, 255, 0.84;
--white-alpha: 255, 255, 255, 0.8;
}
.dark {
@@ -242,18 +240,16 @@
--gray-12: 238 238 238;
--background-color: 18 18 19;
--text-blue: 126 182 255;
--border-strong: 52 52 52;
--border-weak: 38 38 42;
--solid-1: 23 23 26;
--solid-2: 29 30 36;
--solid-3: 44 45 54;
--solid-active: 53 57 66;
--solid-amber: 56 50 41;
--solid-blue: 4 51 101;
--solid-amber: 42 37 30;
--solid-blue: 16 49 91;
--solid-iris: 38 42 101;
--solid-purple: 51 51 107;
--solid-red: 78 46 55;
--text-blue: 126 182 255;
--alpha-1: 36, 36, 36, 0.8;
--alpha-2: 139, 147, 182, 0.15;
@@ -50,16 +50,16 @@ const AVATAR_COLORS = {
dark: [
['#4B143D', '#FF8DCC'],
['#3F220D', '#FFA366'],
['#023B37', '#0BD8B6'],
['#2A2A2A', '#ADB1B8'],
['#023B37', '#0BD8B6'],
['#27264D', '#A19EFF'],
['#1D2E62', '#9EB1FF'],
],
light: [
['#FBDCEF', '#C2298A'],
['#FFE0BB', '#99543A'],
['#CCF3EA', '#008573'],
['#E8E8E8', '#60646C'],
['#CCF3EA', '#008573'],
['#EBEBFE', '#4747C2'],
['#E1E9FF', '#3A5BC7'],
],
@@ -117,7 +117,7 @@ const props = defineProps({
},
conversationId: { type: Number, required: true },
createdAt: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties
currentUserId: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties
currentUserId: { type: Number, required: true },
groupWithNext: { type: Boolean, default: false },
inboxId: { type: Number, default: null }, // eslint-disable-line vue/no-unused-properties
inboxSupportsReplyTo: { type: Object, default: () => ({}) },
@@ -173,11 +173,7 @@ const variant = computed(() => {
return variants[props.messageType] || MESSAGE_VARIANTS.USER;
});
const isBotOrAgentMessage = computed(() => {
if (props.messageType === MESSAGE_TYPES.ACTIVITY) {
return false;
}
const isMyMessage = computed(() => {
// if an outgoing message is still processing, then it's definitely a
// message sent by the current user
if (
@@ -186,15 +182,17 @@ const isBotOrAgentMessage = computed(() => {
) {
return true;
}
const senderId = props.senderId ?? props.sender?.id;
const senderType = props.senderType ?? props.sender?.type;
if (!senderType || !senderId) {
return true;
return false;
}
return senderType.toLowerCase() === SENDER_TYPES.USER.toLowerCase();
return (
senderType.toLowerCase() === SENDER_TYPES.USER.toLowerCase() &&
props.currentUserId === senderId
);
});
/**
@@ -202,7 +200,7 @@ const isBotOrAgentMessage = computed(() => {
* @returns {import('vue').ComputedRef<'left'|'right'|'center'>} The computed orientation
*/
const orientation = computed(() => {
if (isBotOrAgentMessage.value) {
if (isMyMessage.value) {
return ORIENTATION.RIGHT;
}
@@ -223,8 +221,8 @@ const flexOrientationClass = computed(() => {
const gridClass = computed(() => {
const map = {
[ORIENTATION.LEFT]: 'grid grid-cols-1fr',
[ORIENTATION.RIGHT]: 'grid grid-cols-[1fr_24px]',
[ORIENTATION.LEFT]: 'grid grid-cols-[24px_1fr]',
[ORIENTATION.RIGHT]: 'grid grid-cols-1fr',
};
return map[orientation.value];
@@ -233,12 +231,12 @@ const gridClass = computed(() => {
const gridTemplate = computed(() => {
const map = {
[ORIENTATION.LEFT]: `
"bubble"
"meta"
"avatar bubble"
"spacer meta"
`,
[ORIENTATION.RIGHT]: `
"bubble avatar"
"meta spacer"
"bubble"
"meta"
`,
};
@@ -253,7 +251,7 @@ const shouldGroupWithNext = computed(() => {
const shouldShowAvatar = computed(() => {
if (props.messageType === MESSAGE_TYPES.ACTIVITY) return false;
if (orientation.value === ORIENTATION.LEFT) return false;
if (orientation.value === ORIENTATION.RIGHT) return false;
return true;
});
@@ -440,7 +438,7 @@ provideMessageContext({
isPrivate: computed(() => props.private),
variant,
orientation,
isBotOrAgentMessage,
isMyMessage,
shouldGroupWithNext,
});
</script>
@@ -450,7 +448,7 @@ provideMessageContext({
<div
v-if="shouldRenderMessage"
:id="`message${props.id}`"
class="flex w-full message-bubble-container"
class="flex w-full message-bubble-container mb-2"
:data-message-id="props.id"
:class="[
flexOrientationClass,
@@ -472,28 +470,22 @@ provideMessageContext({
'w-full': variant === MESSAGE_VARIANTS.EMAIL,
},
]"
class="gap-x-2"
class="gap-x-3"
:style="{
gridTemplateAreas: gridTemplate,
}"
>
<div
v-if="!shouldGroupWithNext && shouldShowAvatar"
v-tooltip.left-end="avatarTooltip"
v-tooltip.right-end="avatarTooltip"
class="[grid-area:avatar] flex items-end"
>
<Avatar v-bind="avatarInfo" :size="20" />
<Avatar v-bind="avatarInfo" :size="24" />
</div>
<div
class="[grid-area:bubble] flex"
:class="{
'justify-end': orientation === ORIENTATION.RIGHT,
'ltr:pl-12 rtl:pr-12 2xl:ltr:pl-0 2xl:rtl:pr-0':
orientation === ORIENTATION.RIGHT &&
variant !== MESSAGE_VARIANTS.EMAIL,
'ltr:pr-12 rtl:pl-12 2xl:ltr:pr-0 2xl:rtl:pl-0':
orientation === ORIENTATION.LEFT &&
variant !== MESSAGE_VARIANTS.EMAIL,
'ltr:pl-9 rtl:pl-0 justify-end': orientation === ORIENTATION.RIGHT,
'min-w-0': variant === MESSAGE_VARIANTS.EMAIL,
}"
@contextmenu="openContextMenu($event)"
@@ -26,7 +26,7 @@ const { t } = useI18n();
/>
</div>
<div
class="absolute bg-n-alpha-3 px-4 py-3 border rounded-xl border-n-strong text-n-slate-12 bottom-6 w-52 text-xs backdrop-blur-[100px] shadow-[0px_0px_24px_0px_rgba(0,0,0,0.12)] opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all break-all"
class="absolute bg-n-alpha-3 px-4 py-3 border rounded-xl border-n-strong text-n-slate-12 bottom-6 w-52 text-xs backdrop-blur-[100px] shadow-[0px_0px_24px_0px_rgba(0,0,0,0.12)] opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all"
:class="{
'ltr:left-0 rtl:right-0': orientation === ORIENTATION.LEFT,
'ltr:right-0 rtl:left-0': orientation === ORIENTATION.RIGHT,
@@ -66,11 +66,7 @@ const shouldGroupWithNext = (index, searchList) => {
nextMessageType === MESSAGE_TYPES.TEMPLATE &&
currentMessageType === MESSAGE_TYPES.TEMPLATE;
const areBothActivity =
nextMessageType === MESSAGE_TYPES.ACTIVITY &&
currentMessageType === MESSAGE_TYPES.ACTIVITY;
if (!hasSameSender || areBothTemplates || areBothActivity) return false;
if (!hasSameSender || areBothTemplates) return false;
if (currentMessageType !== nextMessageType) return false;
@@ -99,17 +95,6 @@ const getInReplyToMessage = parentMessage => {
return replyMessage ? useCamelCase(replyMessage) : null;
};
const getMessageSpacingClass = (message, messages, index) => {
// For non-activity messages, use groupWithNext logic
if (message.messageType !== MESSAGE_TYPES.ACTIVITY) {
return shouldGroupWithNext(index, messages) ? 'mb-1' : 'mb-6';
}
// For activity messages, check next message exists and is also an activity
const nextMessage = messages[index + 1];
return nextMessage?.messageType === MESSAGE_TYPES.ACTIVITY ? 'mb-2' : 'mb-6';
};
</script>
<template>
@@ -127,7 +112,6 @@ const getMessageSpacingClass = (message, messages, index) => {
:group-with-next="shouldGroupWithNext(index, allMessages)"
:inbox-supports-reply-to="inboxSupportsReplyTo"
:current-user-id="currentUserId"
:class="getMessageSpacingClass(message, allMessages, index)"
data-clarity-mask="True"
/>
</template>
@@ -102,7 +102,6 @@ const statusToShow = computed(() => {
if (isRead.value) return MESSAGE_STATUS.READ;
if (isDelivered.value) return MESSAGE_STATUS.DELIVERED;
if (isSent.value) return MESSAGE_STATUS.SENT;
if (status.value === MESSAGE_STATUS.FAILED) return MESSAGE_STATUS.FAILED;
return MESSAGE_STATUS.PROGRESS;
});
@@ -14,7 +14,7 @@ const readableTime = computed(() =>
<template>
<BaseBubble
v-tooltip.top="readableTime"
class="px-3 py-1 !rounded-full flex min-w-0 items-center gap-2"
class="px-2 py-0.5 !rounded-full flex min-w-0 items-center gap-2"
data-bubble-name="activity"
>
<span v-dompurify-html="content" :title="content" />
@@ -20,9 +20,9 @@ const varaintBaseMap = {
'bg-n-solid-amber text-n-amber-12 [&_.prosemirror-mention-node]:font-semibold',
[MESSAGE_VARIANTS.USER]: 'bg-n-slate-4 text-n-slate-12',
[MESSAGE_VARIANTS.ACTIVITY]: 'bg-n-alpha-1 text-n-slate-11 text-sm',
[MESSAGE_VARIANTS.BOT]: 'bg-n-solid-purple text-n-slate-12',
[MESSAGE_VARIANTS.TEMPLATE]: 'bg-n-solid-purple text-n-slate-12',
[MESSAGE_VARIANTS.ERROR]: 'bg-n-solid-red text-n-ruby-12',
[MESSAGE_VARIANTS.BOT]: 'bg-n-solid-iris text-n-slate-12',
[MESSAGE_VARIANTS.TEMPLATE]: 'bg-n-solid-iris text-n-slate-12',
[MESSAGE_VARIANTS.ERROR]: 'bg-n-ruby-4 text-n-ruby-12',
[MESSAGE_VARIANTS.EMAIL]: 'w-full',
[MESSAGE_VARIANTS.UNSUPPORTED]:
'bg-n-solid-amber/70 border border-dashed border-n-amber-12 text-n-amber-12',
@@ -64,13 +64,13 @@ const senderName = computed(() => {
:href="action.href"
rel="noreferrer noopener nofollow"
target="_blank"
class="w-full block bg-n-alpha-white px-4 py-2 rounded-lg text-sm text-center border border-n-container"
class="w-full block bg-n-solid-3 px-4 py-2 rounded-lg text-sm text-center border border-n-container"
>
{{ action.label }}
</a>
<button
v-else
class="w-full bg-n-alpha-white px-4 py-2 rounded-lg text-sm text-center border border-n-container"
class="w-full bg-n-solid-3 px-4 py-2 rounded-lg text-sm text-center border border-n-container"
@click="action.onClick"
>
{{ action.label }}
@@ -62,7 +62,7 @@ const hasQuotedMessage = computed(() => {
<EmailMeta
class="p-3"
:class="{
'border-b border-n-slate-6': isIncoming,
'border-b border-n-strong': isIncoming,
'border-b border-n-slate-8/20': isOutgoing,
}"
/>
@@ -75,7 +75,7 @@ const hasQuotedMessage = computed(() => {
>
<div
v-if="isExpandable && !isExpanded"
class="absolute left-0 right-0 bottom-0 h-40 px-8 flex items-end bg-gradient-to-t from-n-slate-4 via-n-strong/50 to-transparent dark:from-n-slate-5 dark:via-n-slate-4/50"
class="absolute left-0 right-0 bottom-0 h-40 px-8 flex items-end bg-gradient-to-t from-n-gray-3 via-n-gray-3 via-20% to-transparent"
>
<button
class="text-n-slate-12 py-2 px-8 mx-auto text-center flex items-center gap-2"
@@ -139,10 +139,7 @@ const hasQuotedMessage = computed(() => {
v-if="Array.isArray(attachments) && attachments.length"
class="px-4 pb-4 space-y-2"
>
<AttachmentChips
:attachments="attachments"
class="flex flex-col gap-2.5"
/>
<AttachmentChips :attachments="attachments" class="gap-1" />
</section>
</BaseBubble>
</template>
@@ -20,15 +20,12 @@ const isEmpty = computed(() => {
<template>
<BaseBubble class="px-4 py-3" data-bubble-name="text">
<div class="gap-4 flex flex-col">
<div class="gap-3 flex flex-col">
<span v-if="isEmpty" class="text-n-slate-11">
{{ $t('CONVERSATION.NO_CONTENT') }}
</span>
<FormattedContent v-if="content" :content="content" />
<AttachmentChips
:attachments="attachments"
class="flex flex-col gap-2.5"
/>
<AttachmentChips :attachments="attachments" class="gap-2" />
<template v-if="isTemplate">
<div
v-if="contentAttributes.submittedEmail"
@@ -1,7 +1,8 @@
<script setup>
import { computed, defineOptions, useAttrs } from 'vue';
import ImageVideoChip from 'next/message/chips/AttachmentGrid.vue'; // Image and Video Grids are the same component
import ImageChip from 'next/message/chips/Image.vue';
import VideoChip from 'next/message/chips/Video.vue';
import AudioChip from 'next/message/chips/Audio.vue';
import FileChip from 'next/message/chips/File.vue';
import { useMessageContext } from '../provider.js';
@@ -36,7 +37,7 @@ const attrs = useAttrs();
const { orientation } = useMessageContext();
const classToApply = computed(() => {
const baseClasses = [attrs.class, 'flex', 'flex-wrap', 'gap-2'];
const baseClasses = [attrs.class, 'flex', 'flex-wrap'];
if (orientation.value === 'right') {
baseClasses.push('justify-end');
@@ -76,25 +77,30 @@ const files = computed(() => {
<template>
<div v-if="mediaAttachments.length" :class="classToApply">
<ImageVideoChip :attachments="mediaAttachments" />
<template v-for="attachment in mediaAttachments" :key="attachment.id">
<ImageChip
v-if="attachment.fileType === ATTACHMENT_TYPES.IMAGE"
:attachment="attachment"
/>
<VideoChip
v-else-if="attachment.fileType === ATTACHMENT_TYPES.VIDEO"
:attachment="attachment"
/>
</template>
</div>
<div v-if="recordings.length" :class="classToApply">
<div v-for="attachment in recordings" :key="attachment.id" class="w-full">
<div v-for="attachment in recordings" :key="attachment.id">
<AudioChip
class="bg-n-alpha-3 dark:bg-n-alpha-2 text-n-slate-12"
:attachment="attachment"
/>
</div>
</div>
<div v-if="files.length" :class="classToApply">
<div className="grid grid-cols-1 2xl:grid-cols-2 gap-3">
<FileChip
v-for="attachment in files"
:key="attachment.id"
:attachment="attachment"
/>
</div>
<FileChip
v-for="attachment in files"
:key="attachment.id"
:attachment="attachment"
/>
</div>
</template>
@@ -1,82 +0,0 @@
<script setup>
import { computed } from 'vue';
import ImageChip from './Image.vue';
import VideoChip from './Video.vue';
import { ATTACHMENT_TYPES } from '../constants';
const props = defineProps({
attachments: {
type: Array,
required: true,
validator: value => Array.isArray(value) && value.length > 0,
},
});
const MAX_DISPLAYED = 5;
const visibleAttachments = computed(() =>
props.attachments.slice(0, MAX_DISPLAYED)
);
const remainingCount = computed(() =>
Math.max(0, props.attachments.length - MAX_DISPLAYED)
);
const gridClass = computed(() => {
const count = props.attachments.length;
const base = 'grid gap-2 w-full';
if (count === 1) return `${base} grid-cols-1`;
const classes = {
2: `${base} grid-cols-2 max-h-[500px]`,
3: `${base} grid-cols-2 max-h-[500px]`,
4: `${base} grid-cols-2 max-h-[500px]`,
5: `${base} grid-cols-3 max-h-[500px]`,
};
return classes[count] || classes[5];
});
const itemClass = computed(() => index => {
const count = props.attachments.length;
const base = 'relative overflow-hidden rounded-lg';
if (count === 1) return `${base} w-full h-auto`;
if (count === 3 && index === 0) return `${base} row-span-2 h-[492px]`;
if (count >= 5 && index === 0) return `${base} col-span-2 h-[242px]`;
return count === 2 ? `${base} h-[500px]` : `${base} h-[242px]`;
});
const shouldShowOverlay = computed(
() => index => remainingCount.value > 0 && index === MAX_DISPLAYED - 1
);
const componentMap = {
[ATTACHMENT_TYPES.IMAGE]: ImageChip,
[ATTACHMENT_TYPES.VIDEO]: VideoChip,
};
const getComponent = fileType =>
componentMap[fileType?.toLowerCase()] || componentMap[ATTACHMENT_TYPES.IMAGE];
</script>
<template>
<div :class="gridClass">
<div
v-for="(attachment, index) in visibleAttachments"
:key="attachment.id"
:class="itemClass(index)"
>
<component
:is="getComponent(attachment.fileType)"
:attachment="attachment"
:remaining-count="remainingCount"
:should-show-overlay="shouldShowOverlay(index)"
/>
</div>
</div>
</template>
@@ -11,16 +11,7 @@ defineProps({
type: Object,
required: true,
},
remainingCount: {
type: Number,
required: true,
},
shouldShowOverlay: {
type: Boolean,
required: true,
},
});
const hasError = ref(false);
const showGallery = ref(false);
@@ -29,20 +20,12 @@ const { filteredCurrentChatAttachments } = useMessageContext();
const handleError = () => {
hasError.value = true;
};
const handleGalleryClick = () => {
showGallery.value = true;
};
const handleGalleryClose = () => {
showGallery.value = false;
};
</script>
<template>
<div
class="rounded-lg overflow-hidden contain-content cursor-pointer size-full"
@click="handleGalleryClick"
class="size-[72px] overflow-hidden contain-content rounded-xl cursor-pointer"
@click="showGallery = true"
>
<div
v-if="hasError"
@@ -51,7 +34,6 @@ const handleGalleryClose = () => {
<Icon icon="i-lucide-circle-off" class="text-n-slate-11" />
{{ $t('COMPONENTS.MEDIA.LOADING_FAILED') }}
</div>
<img
v-else
class="object-cover w-full h-full"
@@ -59,23 +41,12 @@ const handleGalleryClose = () => {
@error="handleError"
/>
</div>
<div
v-if="shouldShowOverlay"
class="absolute inset-0 flex items-center cursor-pointer justify-center bg-n-black/25 dark:bg-n-alpha-1 rounded-lg"
@click="handleGalleryClick"
>
<span class="text-white text-2xl font-semibold">
+{{ remainingCount }}
</span>
</div>
<GalleryView
v-if="showGallery"
v-model:show="showGallery"
:attachment="useSnakeCase(attachment)"
:all-attachments="filteredCurrentChatAttachments"
@error="handleError"
@close="handleGalleryClose"
@close="() => (showGallery = false)"
/>
</template>
@@ -10,33 +10,17 @@ defineProps({
type: Object,
required: true,
},
remainingCount: {
type: Number,
required: true,
},
shouldShowOverlay: {
type: Boolean,
required: true,
},
});
const showGallery = ref(false);
const { filteredCurrentChatAttachments } = useMessageContext();
const handleGalleryClick = () => {
showGallery.value = true;
};
const handleGalleryClose = () => {
showGallery.value = false;
};
</script>
<template>
<div
class="rounded-lg overflow-hidden contain-content cursor-pointer size-full"
@click="handleGalleryClick"
class="size-[72px] overflow-hidden contain-content rounded-xl cursor-pointer relative group"
@click="showGallery = true"
>
<video
:src="attachment.dataUrl"
@@ -45,7 +29,6 @@ const handleGalleryClose = () => {
playsInline
/>
<div
v-if="!shouldShowOverlay"
class="absolute w-full h-full inset-0 p-1 flex items-center justify-center"
>
<div
@@ -58,21 +41,12 @@ const handleGalleryClose = () => {
</div>
</div>
</div>
<div
v-if="shouldShowOverlay"
class="absolute inset-0 flex items-center cursor-pointer justify-center bg-n-black/25 dark:bg-n-alpha-1 rounded-lg"
@click="handleGalleryClick"
>
<span class="text-white text-2xl font-semibold">
+{{ remainingCount }}
</span>
</div>
<GalleryView
v-if="showGallery"
v-model:show="showGallery"
:attachment="useSnakeCase(attachment)"
:all-attachments="filteredCurrentChatAttachments"
@close="handleGalleryClose"
@error="onError"
@close="() => (showGallery = false)"
/>
</template>
@@ -98,7 +98,7 @@ const MessageControl = Symbol('MessageControl');
* @property {import('vue').Ref<Sender|null>} [sender=null] - The sender information
* @property {import('vue').ComputedRef<MessageOrientation>} orientation - The visual variant of the message
* @property {import('vue').ComputedRef<MessageVariant>} variant - The visual variant of the message
* @property {import('vue').ComputedRef<boolean>} isBotOrAgentMessage - Does the message belong to the bot or agents
* @property {import('vue').ComputedRef<boolean>} isMyMessage - Does the message belong to the current user
* @property {import('vue').ComputedRef<boolean>} isPrivate - Proxy computed value for private
* @property {import('vue').ComputedRef<boolean>} shouldGroupWithNext - Should group with the next message or not, it is differnt from groupWithNext, this has a bypass for a failed message
*/
@@ -0,0 +1,79 @@
<script setup>
import { computed, onMounted } from 'vue';
import { useStore } from 'vuex';
import { useAccount } from 'dashboard/composables/useAccount';
import { differenceInDays } from 'date-fns';
import { useRoute } from 'vue-router';
const ALWAYS_ON_ROUTES = [
'agent_list',
'settings_inbox_list',
'billing_settings_index',
];
const { accountId } = useAccount();
const store = useStore();
const route = useRoute();
const routeName = computed(() => {
return route.name || '';
});
const isOnChatwootCloud = computed(
() => store.getters['globalConfig/isOnChatwootCloud']
);
const account = computed(() =>
store.getters['accounts/getAccount'](accountId.value)
);
const isTrialAccount = computed(() => {
if (!account.value) return false;
const createdAt = new Date(account.value.created_at);
const diffDays = differenceInDays(new Date(), createdAt);
return diffDays <= 15;
});
const testLimit = ({ allowed, consumed }) => {
return consumed > allowed;
};
const isLimitExceeded = computed(() => {
if (ALWAYS_ON_ROUTES.includes(routeName.value)) {
return false;
}
if (!account.value) return false;
const { limits } = account.value;
if (!limits) return false;
const { conversation, non_web_inboxes: nonWebInboxes } = limits;
return testLimit(conversation) || testLimit(nonWebInboxes);
});
const shouldShowBanner = computed(() => {
if (!isOnChatwootCloud.value) {
return false;
}
if (isTrialAccount.value) {
return false;
}
return isLimitExceeded.value;
});
const fetchLimits = () => store.dispatch('accounts/limits');
onMounted(() => {
if (isOnChatwootCloud.value) {
fetchLimits();
}
});
</script>
<template>
<div v-if="shouldShowBanner">Limits exceeded</div>
<slot v-else />
</template>
@@ -560,7 +560,7 @@
"ERROR_MESSAGE": "Wir konnten die Suche nicht abschließen. Bitte versuch es erneut."
},
"FORM": {
"GO_TO_CONVERSATION": "Anzeigen",
"GO_TO_CONVERSATION": "Aussicht",
"SUCCESS_MESSAGE": "Die Nachricht wurde erfolgreich versendet!",
"ERROR_MESSAGE": "Beim Erstellen der Unterhaltung ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.",
"NO_INBOX_ALERT": "Es sind keine Posteingänge vorhanden, um eine Unterhaltung mit diesem Kontakt zu starten.",
@@ -751,7 +751,7 @@
"WHATSAPP": "WhatsApp",
"SMS": "SMS",
"EMAIL": "E-Mail",
"TELEGRAM": "Telegramm",
"TELEGRAM": "Telegram",
"LINE": "Line",
"API": "API-Kanal"
}
@@ -12,7 +12,7 @@
},
"WEBHOOK": {
"SUBSCRIBED_EVENTS": "Abonnierte Events",
"LEARN_MORE": "Mehr über Webhooks erfahren",
"LEARN_MORE": "Learn more about webhooks",
"FORM": {
"CANCEL": "Stornieren",
"DESC": "Webhook-Ereignisse bieten Ihnen Echtzeitinformationen darüber, was in Ihrem Chatwoot-Konto passiert. Bitte geben Sie eine gültige URL ein, um einen Rückruf zu konfigurieren.",
@@ -31,7 +31,7 @@
},
"END_POINT": {
"LABEL": "Webhook-URL",
"PLACEHOLDER": "Beispiel: {webhookExampleURL}",
"PLACEHOLDER": "Example: {webhookExampleURL}",
"ERROR": "Bitte geben Sie eine gültige URL ein"
},
"EDIT_SUBMIT": "Webhook aktualisieren",
@@ -114,7 +114,7 @@
},
"OPEN_AI": {
"AI_ASSIST": "AI-Assistent",
"WITH_AI": " {option} mit KI ",
"WITH_AI": " {option} with AI ",
"OPTIONS": {
"REPLY_SUGGESTION": "Antwortvorschlag",
"SUMMARIZE": "Zusammenfassen",
@@ -235,7 +235,7 @@
"ERROR": "Beim Abrufen der linearen Probleme ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut",
"LINK_SUCCESS": "Problem erfolgreich verknüpft",
"LINK_ERROR": "Beim Verknüpfen des Problems ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut",
"LINK_TITLE": "Unterhaltung (#{conversationId}) mit {name}"
"LINK_TITLE": "Conversation (#{conversationId}) with {name}"
},
"ADD_OR_LINK": {
"TITLE": "Lineares Problem erstellen/verknüpfen",
@@ -294,7 +294,7 @@
"PRIORITY": "Priorität",
"ASSIGNEE": "Zugewiesener",
"LABELS": "Labels",
"CREATED_AT": "Erstellt am {createdAt}"
"CREATED_AT": "Created at {createdAt}"
},
"UNLINK": {
"TITLE": "Verknüpfung aufheben",
@@ -302,8 +302,8 @@
"ERROR": "Beim Aufheben der Verknüpfung des Problems ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut"
},
"DELETE": {
"TITLE": "Sind Sie sicher, dass Sie die Integration löschen möchten?",
"MESSAGE": "Sind Sie sicher, dass Sie die Integration löschen möchten?",
"TITLE": "Are you sure you want to delete the integration?",
"MESSAGE": "Are you sure you want to delete the integration?",
"CONFIRM": "Ja, löschen",
"CANCEL": "Stornieren"
}
@@ -313,27 +313,27 @@
"NAME": "Kapitän",
"COPILOT": {
"SEND_MESSAGE": "Nachricht senden...",
"LOADER": "Captain denkt nach",
"LOADER": "Captain is thinking",
"YOU": "Sie",
"USE": "Verwenden",
"RESET": "Zurücksetzen",
"SELECT_ASSISTANT": "Assistent auswählen"
"USE": "Use this",
"RESET": "Reset",
"SELECT_ASSISTANT": "Select Assistant"
},
"PAYWALL": {
"TITLE": "Upgrade auf Captain AI",
"TITLE": "Upgrade to use Captain AI",
"AVAILABLE_ON": "Captain is not available on the free plan.",
"UPGRADE_PROMPT": "Tarif upgraden, um Zugang zu unseren Assistenten, Copilot und mehr zu erhalten.",
"UPGRADE_NOW": "Jetzt upgraden",
"CANCEL_ANYTIME": "Sie können Ihr Paket jederzeit ändern oder kündigen"
"UPGRADE_PROMPT": "Upgrade your plan to get access to our assistants, copilot and more.",
"UPGRADE_NOW": "Upgrade now",
"CANCEL_ANYTIME": "You can change or cancel your plan anytime"
},
"ENTERPRISE_PAYWALL": {
"AVAILABLE_ON": "Captain AI Funktion ist nur mit einem kostenpflichtigen Tarif verfügbar.",
"UPGRADE_PROMPT": "Tarif upgraden, um Zugang zu unseren Assistenten, Copilot und mehr zu erhalten.",
"ASK_ADMIN": "Bitte kontaktieren Sie Ihren Administrator für das Upgrade."
"AVAILABLE_ON": "Captain AI feature is only available in a paid plan.",
"UPGRADE_PROMPT": "Upgrade your plan to get access to our assistants, copilot and more.",
"ASK_ADMIN": "Please reach out to your administrator for the upgrade."
},
"BANNER": {
"RESPONSES": "You've used over 80% of your response limit. To continue using Captain AI, please upgrade.",
"DOCUMENTS": "Dokumentenlimit erreicht. Upgraden um Cpatain AI weiter zu verwenden."
"DOCUMENTS": "Document limit reached. Upgrade to continue using Captain AI."
},
"FORM": {
"CANCEL": "Stornieren",
@@ -100,7 +100,7 @@
"NO_RESULTS": "Nenhum resultado encontrado."
},
"MULTI_SELECTOR": {
"PLACEHOLDER": "Nenhum",
"PLACEHOLDER": "Nenhuma",
"TITLE": {
"AGENT": "Selecionar agente",
"TEAM": "Selecionar time"
@@ -1,16 +1,16 @@
{
"AUDIT_LOGS": {
"HEADER": "Auditoria",
"HEADER_BTN_TXT": "Adicionar Logs de Auditoria",
"HEADER": "Registros de Auditoria",
"HEADER_BTN_TXT": "Adicionar Registros de Auditoria",
"LOADING": "Buscando Logs de Auditoria",
"DESCRIPTION": "Logs de Auditoria mantêm um registro de atividades em sua conta, permitindo que você acompanhe e auditoria de sua conta, equipe ou serviços.",
"LEARN_MORE": "Saiba mais sobre os logs de auditoria",
"SEARCH_404": "Não existem itens correspondentes a esta consulta",
"SIDEBAR_TXT": "<p><b>Logs de Auditoria</b> </p><p> Os Logs de Auditoria são rastros para eventos e ações em um Sistema Chatwoot. </p>",
"SIDEBAR_TXT": "<p><b>Registros de Auditoria</b> </p><p> Registros de Auditoria são trilhas para eventos e ações em um Sistema Chatwoot. </p>",
"LIST": {
"404": "Não há Logs de Auditoria disponíveis nesta conta.",
"TITLE": "Gerenciar Logs de Auditoria",
"DESC": "Logs de auditoria são rastros para eventos e ações em um Sistema de Chatwoot.",
"404": "Não há Registros de Auditoria disponíveis nesta conta.",
"TITLE": "Gerenciar Registros de Auditoria",
"DESC": "Registros de Auditoria são trilhas para eventos e ações em um Sistema Chatwoot.",
"TABLE_HEADER": {
"ACTIVITY": "Usuário",
"TIME": "Ação",
@@ -27,12 +27,12 @@
"ERROR": "Falha ao atualizar etiquetas"
},
"CONVERSATION": {
"TITLE": "Etiquetas da conversa",
"ADD_BUTTON": "Adicionar etiquetas"
"TITLE": "Marcador da conversa",
"ADD_BUTTON": "Adicionar marcador"
},
"LABEL_SELECT": {
"TITLE": "Adicionar etiquetas",
"PLACEHOLDER": "Pesquisar etiquetas",
"TITLE": "Adicionar marcador",
"PLACEHOLDER": "Pesquisar marcador ",
"NO_RESULT": "Nenhuma etiqueta encontrada",
"CREATE_LABEL": "Criar etiqueta"
}
@@ -65,7 +65,7 @@
"HEADER": {
"RESOLVE_ACTION": "Resolver",
"REOPEN_ACTION": "Reabrir",
"OPEN_ACTION": "Abrir",
"OPEN_ACTION": "Abertas",
"OPEN": "Mais",
"CLOSE": "Fechar",
"DETAILS": "detalhes",
@@ -148,7 +148,7 @@
"MESSAGE_SIGN_TOOLTIP": "Assinatura de mensagem",
"ENABLE_SIGN_TOOLTIP": "Ativar assinatura",
"DISABLE_SIGN_TOOLTIP": "Desativar assinatura",
"MSG_INPUT": "Shift + enter para nova linha. Digite '/' para selecionar uma Resposta Pronta.",
"MSG_INPUT": "Shift + enter para nova linha. Digite '/' para atalhos.",
"PRIVATE_MSG_INPUT": "A mensagem será visível apenas para agentes",
"MESSAGE_SIGNATURE_NOT_CONFIGURED": "A assinatura da mensagem não está configurada. Por favor, configure-a nas configurações do perfil.",
"CLICK_HERE": "Clique aqui para atualizar",
@@ -15,7 +15,7 @@
},
"ENTERPRISE_PAYWALL": {
"AVAILABLE_ON": "O recurso de função personalizada está disponível apenas nos planos pagos.",
"UPGRADE_PROMPT": "Atualize para um plano pago para acessar recursos avançados como logs de auditoria, capacidade do agente e muito mais.",
"UPGRADE_PROMPT": "Atualize para um plano pago para acessar recursos avançados como registros de auditoria, capacidade do agente e muito mais.",
"ASK_ADMIN": "Entre em contato com seu administrador para fazer a atualização."
},
"LIST": {
@@ -1,6 +1,6 @@
{
"GENERAL_SETTINGS": {
"TITLE": "Conta",
"TITLE": "Configurações da conta",
"SUBMIT": "Atualizar configurações",
"BACK": "Anterior",
"DISMISS": "Recusar",
@@ -131,10 +131,10 @@
"GO_TO_INBOX_REPORTS": "Ir para Relatórios da Caixa de Entrada",
"GO_TO_TEAM_REPORTS": "Ir para Relatórios da Equipe",
"GO_TO_SETTINGS_AGENTS": "Ir para Configurações de Agente",
"GO_TO_SETTINGS_TEAMS": "Ir para as Configurações de Equipe",
"GO_TO_SETTINGS_INBOXES": "Ir para as Configurações da Caixa de Entrada",
"GO_TO_SETTINGS_TEAMS": "Ir para as configurações de equipe",
"GO_TO_SETTINGS_INBOXES": "Ir para as configurações da Caixa de Entrada",
"GO_TO_SETTINGS_LABELS": "Ir para as Configurações de Etiqueta",
"GO_TO_SETTINGS_CANNED_RESPONSES": "Ir para as Configurações de Respostas Prontas",
"GO_TO_SETTINGS_CANNED_RESPONSES": "Ir para as configurações de respostas prontas",
"GO_TO_SETTINGS_APPLICATIONS": "Vá para Configurações do Aplicativo",
"GO_TO_SETTINGS_ACCOUNT": "Ir para as Configurações da Conta",
"GO_TO_SETTINGS_PROFILE": "Ir para as Configurações do Perfil",
@@ -500,7 +500,7 @@
"ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT": "As conversas continuarão sobre o e-mail se o endereço de e-mail de contato estiver disponível.",
"LOCK_TO_SINGLE_CONVERSATION": "Bloquear para conversa única",
"LOCK_TO_SINGLE_CONVERSATION_SUB_TEXT": "Ativar ou desativar várias conversas para o mesmo contato nesta caixa de entrada",
"INBOX_UPDATE_TITLE": "Configurações da Caixa de Entrada",
"INBOX_UPDATE_TITLE": "Configurações da Caixa de entrada",
"INBOX_UPDATE_SUB_TEXT": "Atualize suas configurações de caixa de entrada",
"AUTO_ASSIGNMENT_SUB_TEXT": "Ativar ou desativar a atribuição automática de novas conversas aos agentes adicionados a essa caixa de entrada.",
"HMAC_VERIFICATION": "Validação de Identidade do Usuário",
@@ -695,7 +695,7 @@
"PLACE_HOLDER": "Fale conosco no chat"
},
"UPDATE": {
"BUTTON_TEXT": "Atualizar Configurações do Widget",
"BUTTON_TEXT": "Atualizar configurações do widget",
"API": {
"SUCCESS_MESSAGE": "Configurações do widget atualizadas com sucesso",
"ERROR_MESSAGE": "Não é possível atualizar as configurações do widget"
@@ -302,8 +302,8 @@
"ERROR": "Houve um erro ao desvincular o atributo, por favor, tente novamente"
},
"DELETE": {
"TITLE": "Tem certeza que deseja excluir esta integração?",
"MESSAGE": "Tem certeza que deseja excluir esta integração?",
"TITLE": "Are you sure you want to delete the integration?",
"MESSAGE": "Are you sure you want to delete the integration?",
"CONFIRM": "Sim, excluir",
"CANCEL": "Cancelar"
}
@@ -317,7 +317,7 @@
"YOU": "Você",
"USE": "Use isto",
"RESET": "Reiniciar",
"SELECT_ASSISTANT": "Selecione o Assistente"
"SELECT_ASSISTANT": "Select Assistant"
},
"PAYWALL": {
"TITLE": "Atualize para usar o Capitão IA",
@@ -440,19 +440,19 @@
"DOCUMENTABLE": {
"CONVERSATION": "Conversação #{id}"
},
"SELECTED": "{count} selecionado",
"BULK_APPROVE_BUTTON": "Aprovar",
"SELECTED": "{count} selected",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Excluir",
"BULK_APPROVE": {
"SUCCESS_MESSAGE": "Perguntas Frequentes aprovadas com sucesso",
"ERROR_MESSAGE": "Ocorreu um erro ao aprovar as Perguntas Frequentes. Tente novamente."
"SUCCESS_MESSAGE": "FAQs approved successfully",
"ERROR_MESSAGE": "There was an error approving the FAQs, please try again."
},
"BULK_DELETE": {
"TITLE": "Excluir as Perguntas Frequentes?",
"DESCRIPTION": "Tem certeza que deseja excluir as Perguntas Frequentes selecionadas? Esta ação não pode ser desfeita.",
"CONFIRM": "Sim, excluir todas",
"SUCCESS_MESSAGE": "Perguntas Frequentes excluídas com sucesso/",
"ERROR_MESSAGE": "Ocorreu um erro ao excluir as Perguntas Frequentes, por favor tente novamente."
"TITLE": "Delete FAQs?",
"DESCRIPTION": "Are you sure you want to delete the selected FAQs? This action cannot be undone.",
"CONFIRM": "Yes, delete all",
"SUCCESS_MESSAGE": "FAQs deleted successfully",
"ERROR_MESSAGE": "There was an error deleting the FAQs, please try again."
},
"DELETE": {
"TITLE": "Tem certeza que deseja excluir o FAQ?",
@@ -1,7 +1,7 @@
{
"LABEL_MGMT": {
"HEADER": "Etiquetas",
"HEADER_BTN_TXT": "Adicionar etiqueta",
"HEADER_BTN_TXT": "Adicionar marcador",
"LOADING": "Buscando etiquetas",
"DESCRIPTION": "As etiquetas ajudam você a categorizar e priorizar conversas e leads. Você pode atribuir uma etiqueta a uma conversa ou contato usando o painel lateral.",
"LEARN_MORE": "Aprenda mais sobre etiquetas",
@@ -18,21 +18,21 @@
},
"FORM": {
"NAME": {
"LABEL": "Nome da Etiqueta",
"PLACEHOLDER": "Nome da etiqueta",
"REQUIRED_ERROR": "O nome da etiqueta é obrigatório",
"LABEL": "Nome do marcador",
"PLACEHOLDER": "Nome do marcador",
"REQUIRED_ERROR": "O nome do marcador é obrigatório",
"MINIMUM_LENGTH_ERROR": "Tamanho mínimo 2 é necessário",
"VALID_ERROR": "Somente Letras, Números, Hífen e Sublinhado são permitidos"
},
"DESCRIPTION": {
"LABEL": "Descrição",
"PLACEHOLDER": "Descrição da etiqueta"
"PLACEHOLDER": "Descrição do marcador"
},
"COLOR": {
"LABEL": "Cor"
},
"SHOW_ON_SIDEBAR": {
"LABEL": "Exibir etiqueta na barra lateral"
"LABEL": "Exibir marcador na barra lateral"
},
"EDIT": "Alterar",
"CREATE": "Criar",
@@ -54,10 +54,10 @@
"SUGGESTED_LABELS": "Etiquetas sugeridos"
},
"ADD": {
"TITLE": "Adicionar etiqueta",
"TITLE": "Adicionar marcador",
"DESC": "Etiquetas permitem agrupar as conversas.",
"API": {
"SUCCESS_MESSAGE": "Etiqueta adicionada com sucesso",
"SUCCESS_MESSAGE": "Marcador adicionado com sucesso",
"ERROR_MESSAGE": "Ocorreu um erro, por favor tente novamente"
}
},
@@ -71,7 +71,7 @@
"DELETE": {
"BUTTON_TEXT": "Excluir",
"API": {
"SUCCESS_MESSAGE": "Etiqueta excluída com sucesso",
"SUCCESS_MESSAGE": "Marcador excluído com sucesso",
"ERROR_MESSAGE": "Ocorreu um erro, por favor tente novamente"
},
"CONFIRM": {
@@ -471,7 +471,7 @@
"NO_AGENTS": "Não há conversas por agentes",
"TABLE_HEADER": {
"AGENT": "Agente",
"OPEN": "Abrir",
"OPEN": "Abertas",
"UNATTENDED": "Não atendidas",
"STATUS": "Situação"
}
@@ -509,7 +509,7 @@
"SLA": "Nome do SLA",
"AGENTS": "Nome do Agente",
"INBOXES": "Nome da Caixa de Entrada",
"LABELS": "Nome da etiqueta",
"LABELS": "Nome do marcador",
"TEAMS": "Nome da equipe"
},
"SLA": "Política SLA",
@@ -26,7 +26,7 @@
"UPDATE_SUCCESS": "Suas configurações foram atualizadas com sucesso",
"CARD": {
"ENTER_KEY": {
"HEADING": "Enter (↵)",
"HEADING": "Inserir (↵)",
"CONTENT": "Enviar mensagens pressionando a tecla Enter em vez de clicar no botão de enviar."
},
"CMD_ENTER_KEY": {
@@ -37,19 +37,19 @@
},
"INTERFACE_SECTION": {
"TITLE": "Interface",
"NOTE": "Personalize a aparência do seu painel do Chatwoot.",
"NOTE": "Customize the look and feel of your Chatwoot dashboard.",
"FONT_SIZE": {
"TITLE": "Tamanho da fonte",
"NOTE": "Ajuste o tamanho do texto do painel com base na sua preferência.",
"UPDATE_SUCCESS": "As configurações de sua fonte foram atualizadas com sucesso",
"UPDATE_ERROR": "Ocorreu um erro ao atualizar as configurações de fonte, por favor, tente novamente",
"TITLE": "Font size",
"NOTE": "Adjust the text size across the dashboard based on your preference.",
"UPDATE_SUCCESS": "Your font settings have been updated successfully",
"UPDATE_ERROR": "There is an error while updating the font settings, please try again",
"OPTIONS": {
"SMALLER": "Menor",
"SMALL": "Pequeno",
"SMALLER": "Smaller",
"SMALL": "Small",
"DEFAULT": "Padrão",
"LARGE": "Grande",
"LARGER": "Maior",
"EXTRA_LARGE": "Muito Grande"
"LARGE": "Large",
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
}
},
@@ -287,13 +287,13 @@
"HOME": "Principal",
"AGENTS": "Agentes",
"AGENT_BOTS": "Robôs",
"AUDIT_LOGS": "Auditoria",
"AUDIT_LOGS": "Registros de Auditoria",
"INBOXES": "Caixas de Entrada",
"NOTIFICATIONS": "Notificações",
"CANNED_RESPONSES": "Respostas Prontas",
"INTEGRATIONS": "Integrações",
"PROFILE_SETTINGS": "Configurações do Perfil",
"ACCOUNT_SETTINGS": "Conta",
"ACCOUNT_SETTINGS": "Configurações da conta",
"APPLICATIONS": "Aplicações",
"LABELS": "Etiquetas",
"CUSTOM_ATTRIBUTES": "Atributos Personalizados",
@@ -322,7 +322,7 @@
"REPORTS_INBOX": "Caixa de Entrada",
"REPORTS_TEAM": "Equipe",
"SET_AVAILABILITY_TITLE": "Defina como",
"SET_YOUR_AVAILABILITY": "Disponibilidade",
"SET_YOUR_AVAILABILITY": "Definir a sua disponibilidade",
"SLA": "SLA",
"CUSTOM_ROLES": "Regras Personalizadas",
"BETA": "Beta",
@@ -1,9 +1,9 @@
{
"SLA": {
"HEADER": "Acordo de Nível de Serviço",
"HEADER": "Termos do Serviço",
"ADD_ACTION": "Adicionar SLA",
"ADD_ACTION_LONG": "Criar uma nova Política de SLA",
"DESCRIPTION": "Acordo de Nível de Serviço (SLAs em inglês) são acordos que definem expectativas claras entre sua equipe e os clientes. Estabelecem normas para tempos de resposta e de resolução, criando um quadro de responsabilização e garantindo uma experiência coerente e de qualidade.",
"DESCRIPTION": "Contratos de Nível de Serviço (SLAs em inglês) são contratos que definem expectativas claras entre sua equipe e os clientes. Estabelecem normas para tempos de resposta e de resolução, criando um quadro de responsabilização e garantindo uma experiência coerente e de qualidade.",
"LEARN_MORE": "Saiba mais sobre SLA",
"LOADING": "Buscando SLAs",
"PAYWALL": {
@@ -15,7 +15,7 @@
},
"ENTERPRISE_PAYWALL": {
"AVAILABLE_ON": "O recurso SLA está disponível apenas nos planos pagos.",
"UPGRADE_PROMPT": "Atualize para um plano pago para acessar recursos avançados como logs de auditoria, capacidade do agente e muito mais.",
"UPGRADE_PROMPT": "Atualize para um plano pago para acessar recursos avançados como registros de auditoria, capacidade do agente e muito mais.",
"ASK_ADMIN": "Entre em contato com seu administrador para fazer a atualização."
},
"LIST": {
@@ -266,7 +266,7 @@
"ATTRIBUTE_WARNING": "Контактная информация <strong>{primaryContactName}</strong> будет скопирована в <strong>{parentContactName}</strong>."
},
"SEARCH": {
"ERROR_MESSAGE": "Что-то пошло не так. Пожалуйста, попробуйте позже."
"ERROR_MESSAGE": "Something went wrong. Please try again later."
},
"FORM": {
"SUBMIT": " Объединить контакты",
@@ -563,7 +563,7 @@
"GO_TO_CONVERSATION": "Просмотр",
"SUCCESS_MESSAGE": "Сообщение успешно отправлено!",
"ERROR_MESSAGE": "Произошла ошибка при создании диалога. Пожалуйста, повторите попытку позже.",
"NO_INBOX_ALERT": "Нет доступных входящих для начала разговора с этим контактом.",
"NO_INBOX_ALERT": "Нет доступных \"входящих\" для начала разговора с этим контактом.",
"CONTACT_SELECTOR": {
"LABEL": "Кому:",
"TAG_INPUT_PLACEHOLDER": "Поиск контакта с именем, email или номером телефона",
@@ -31,7 +31,7 @@
},
"PERMISSIONS": {
"CONVERSATION_MANAGE": "Управление всеми диалогами",
"CONVERSATION_UNASSIGNED_MANAGE": "Управление неназначенными беседами и теми, которые им назначены",
"CONVERSATION_UNASSIGNED_MANAGE": "Управление неназначенными беседами и теми, которые назначены им",
"CONVERSATION_PARTICIPATING_MANAGE": "Управление текущими беседами и теми, которые им назначены",
"CONTACT_MANAGE": "Управление контактами",
"REPORT_MANAGE": "Управление отчетами",
@@ -114,7 +114,7 @@
"CHANGE_PRIORITY": "Изменить приоритет",
"CHANGE_TEAM": "Изменить команду",
"SNOOZE_CONVERSATION": "Включить звук диалога",
"ADD_LABEL": "Добавить метку в диалог",
"ADD_LABEL": "Добавить метку в разговор",
"REMOVE_LABEL": "Удалить метку из диалога",
"SETTINGS": "Настройки",
"AI_ASSIST": "Помощь ИИ",
@@ -128,11 +128,11 @@
"GO_TO_CONVERSATION_REPORTS": "Перейти к отчетам по диалогам",
"GO_TO_AGENT_REPORTS": "Перейти к отчетам по сотрудникам",
"GO_TO_LABEL_REPORTS": "Перейти к отчетам по меткам",
"GO_TO_INBOX_REPORTS": "Перейти к отчётам по «Входящим»",
"GO_TO_INBOX_REPORTS": "Перейти к отчётам по \"Входящим\"",
"GO_TO_TEAM_REPORTS": "Перейти к отчетам по командам",
"GO_TO_SETTINGS_AGENTS": "Перейти к настройкам сотрудников",
"GO_TO_SETTINGS_TEAMS": "Перейти к настройкам команд",
"GO_TO_SETTINGS_INBOXES": "Перейти к настройкам «Входящих»",
"GO_TO_SETTINGS_INBOXES": "Перейти к настройкам \"Входящих\"",
"GO_TO_SETTINGS_LABELS": "Перейти к настройкам меток",
"GO_TO_SETTINGS_CANNED_RESPONSES": "Перейти к настройкам шаблонных ответов",
"GO_TO_SETTINGS_APPLICATIONS": "Перейти в настройки приложения",
@@ -2,7 +2,7 @@
"INBOX_MGMT": {
"HEADER": "Источники",
"DESCRIPTION": "Канал — это способ коммуникации, который ваш клиент выбирает для связи с вами. Входящие — это место, где вы управляете диалогами для определенного канала. Он может включать диалоги из различных источников, таких как электронная почта, онлайн чат или социальные сети.",
"LEARN_MORE": "Узнать больше о «Входящих»",
"LEARN_MORE": "Узнать больше о \"Входящих\"",
"RECONNECTION_REQUIRED": "Входящие сообщения отключены. Вы не будете получать новые сообщения пока не пройдете авторизацию повторно.",
"CLICK_TO_RECONNECT": "Нажмите здесь для повторного подключения.",
"LIST": {
@@ -208,8 +208,8 @@
}
},
"WHATSAPP": {
"TITLE": "Канал WhatsApp",
"DESC": "Начните поддерживать своих клиентов через WhatsApp.",
"TITLE": "WhatsApp канал",
"DESC": "Начните поддерживать ваших клиентов через WhatsApp.",
"PROVIDERS": {
"LABEL": "Поставщик API",
"TWILIO": "Twilio",
@@ -302,8 +302,8 @@
"ERROR": "Произошла ошибка при отвязке задачи, пожалуйста, попробуйте ещё раз"
},
"DELETE": {
"TITLE": "Вы уверены, что хотите удалить интеграцию?",
"MESSAGE": "Вы уверены, что хотите удалить интеграцию?",
"TITLE": "Are you sure you want to delete the integration?",
"MESSAGE": "Are you sure you want to delete the integration?",
"CONFIRM": "Да, удалить",
"CANCEL": "Отменить"
}
@@ -317,18 +317,18 @@
"YOU": "Вы",
"USE": "Использовать это",
"RESET": "Сброс",
"SELECT_ASSISTANT": "Выбрать ассистента"
"SELECT_ASSISTANT": "Select Assistant"
},
"PAYWALL": {
"TITLE": "Обновите тарифный план, чтобы использовать Captain AI",
"AVAILABLE_ON": "Капитан недоступен на бесплатном тарифном плане.",
"UPGRADE_PROMPT": "Обновите тарифный план, чтобы получить доступ к нашим ассистентам, copilot и другим функциям.",
"UPGRADE_PROMPT": "Обновите тарифный план, чтобы получить доступ к помощникам, Captain AI и другому.",
"UPGRADE_NOW": "Обновить сейчас",
"CANCEL_ANYTIME": "Вы можете изменить или отменить ваш тарифный план в любое время"
},
"ENTERPRISE_PAYWALL": {
"AVAILABLE_ON": "Функция Captain AI доступна только в платном плане.",
"UPGRADE_PROMPT": "Обновите тарифный план, чтобы получить доступ к нашим ассистентам, copilot и другим функциям.",
"UPGRADE_PROMPT": "Обновите тарифный план, чтобы получить доступ к помощникам, Captain AI и другому.",
"ASK_ADMIN": "Пожалуйста, обратитесь к вашему администратору для обновления."
},
"BANNER": {
@@ -341,35 +341,35 @@
"EDIT": "Обновить"
},
"ASSISTANTS": {
"HEADER": "Ассистенты",
"ADD_NEW": "Создать нового ассистента",
"HEADER": "Помощники",
"ADD_NEW": "Создать нового помощника",
"DELETE": {
"TITLE": "Вы уверены, что хотите удалить ассистента?",
"DESCRIPTION": "Это действие необратимо. Удаление этого ассистента удалит его из всех подключенных источников и навсегда удалит все сгенерированные знания.",
"TITLE": "Вы уверены, что хотите удалить помощника?",
"DESCRIPTION": "Это действие необратимо. Удаление этого помощника удалит его из всех подключенных источников и навсегда удалит все сгенерированные знания.",
"CONFIRM": "Да, удалить",
"SUCCESS_MESSAGE": "Ассистент успешно удален",
"ERROR_MESSAGE": "При удалении ассистента произошла ошибка. Пожалуйста, попробуйте еще раз."
"SUCCESS_MESSAGE": "Помощник успешно удален",
"ERROR_MESSAGE": "При удалении помощника произошла ошибка. Пожалуйста, попробуйте еще раз."
},
"FORM_DESCRIPTION": "Заполните приведенные ниже сведения, чтобы назвать своего ассистента, описать его цель и указать поддерживаемый продукт.",
"FORM_DESCRIPTION": "Заполните приведенные ниже сведения, чтобы назвать своего помощника, описать его цель и указать поддерживаемый продукт.",
"CREATE": {
"TITLE": "Создать ассистента",
"SUCCESS_MESSAGE": "Ассистент успешно создан",
"ERROR_MESSAGE": "При создании ассистента произошла ошибка, пожалуйста, попробуйте еще раз."
"TITLE": "Создать помощника",
"SUCCESS_MESSAGE": "Помощник успешно создан",
"ERROR_MESSAGE": "При создании помощника произошла ошибка, пожалуйста, попробуйте еще раз."
},
"FORM": {
"NAME": {
"LABEL": "Имя ассистента",
"PLACEHOLDER": "Введите имя ассистента",
"ERROR": "Пожалуйста, укажите имя ассистента"
"LABEL": "Имя помощника",
"PLACEHOLDER": "Введите имя помощника",
"ERROR": "Пожалуйста, укажите имя помощника"
},
"DESCRIPTION": {
"LABEL": "Описание ассистента",
"PLACEHOLDER": "Опишите, как и где будет использоваться этот ассистент",
"LABEL": "Описание помощника",
"PLACEHOLDER": "Опишите, как и где будет использоваться этот помощник",
"ERROR": "Необходимо описание"
},
"PRODUCT_NAME": {
"LABEL": "Название продукта",
"PLACEHOLDER": "Введите название продукта, для которого предназначен этот ассистент",
"PLACEHOLDER": "Введите название продукта, для которого предназначен этот помощник",
"ERROR": "Требуется название продукта"
},
"FEATURES": {
@@ -379,18 +379,18 @@
}
},
"EDIT": {
"TITLE": "Обновить ассистента",
"SUCCESS_MESSAGE": "Ассистент успешно обновлен",
"ERROR_MESSAGE": "При обновлении ассистента произошла ошибка, пожалуйста, попробуйте еще раз."
"TITLE": "Обновить помощника",
"SUCCESS_MESSAGE": "Помощник успешно обновлен",
"ERROR_MESSAGE": "При обновлении помощника произошла ошибка, пожалуйста, попробуйте еще раз."
},
"OPTIONS": {
"EDIT_ASSISTANT": "Редактировать ассистента",
"DELETE_ASSISTANT": "Удалить ассистента",
"EDIT_ASSISTANT": "Редактировать помощника",
"DELETE_ASSISTANT": "Удалить помощника",
"VIEW_CONNECTED_INBOXES": "Просмотр подключенных источников"
},
"EMPTY_STATE": {
"TITLE": "Нет доступных ассистентов",
"SUBTITLE": "Создайте ассистента, чтобы дать быстрые и точные ответы пользователям. Он может учиться на ваших статьях из центра поддержки и прошлых диалогах."
"TITLE": "Нет доступных помощников",
"SUBTITLE": "Создайте помощника, чтобы дать быстрые и точные ответы пользователям. Он может учиться на ваших статьях из центра поддержки и прошлых диалогах."
}
},
"DOCUMENTS": {
@@ -413,9 +413,9 @@
"ERROR": "Пожалуйста, укажите корректный URL для документа"
},
"ASSISTANT": {
"LABEL": "Ассистент",
"PLACEHOLDER": "Выберите ассистента",
"ERROR": "Необходимо заполнить поле ассистента"
"LABEL": "Помощник",
"PLACEHOLDER": "Выберите помощника",
"ERROR": "Необходимо заполнить поле помощника"
}
},
"DELETE": {
@@ -440,19 +440,19 @@
"DOCUMENTABLE": {
"CONVERSATION": "Диалог #{id}"
},
"SELECTED": "Выбрано {count}",
"BULK_APPROVE_BUTTON": "Одобрить",
"SELECTED": "{count} selected",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Удалить",
"BULK_APPROVE": {
"SUCCESS_MESSAGE": "FAQ успешно одобрены",
"ERROR_MESSAGE": "Произошла ошибка при одобрении FAQ, пожалуйста, попробуйте ещё раз."
"SUCCESS_MESSAGE": "FAQs approved successfully",
"ERROR_MESSAGE": "There was an error approving the FAQs, please try again."
},
"BULK_DELETE": {
"TITLE": "Удалить FAQ?",
"DESCRIPTION": "Вы уверены, что хотите удалить выбранные FAQ? Это действие невозможно отменить.",
"CONFIRM": "Да, удалить всё",
"SUCCESS_MESSAGE": "FAQ успешно удалены",
"ERROR_MESSAGE": "Произошла ошибка при удалении FAQ, пожалуйста, попробуйте ещё раз."
"TITLE": "Delete FAQs?",
"DESCRIPTION": "Are you sure you want to delete the selected FAQs? This action cannot be undone.",
"CONFIRM": "Yes, delete all",
"SUCCESS_MESSAGE": "FAQs deleted successfully",
"ERROR_MESSAGE": "There was an error deleting the FAQs, please try again."
},
"DELETE": {
"TITLE": "Вы действительно хотите удалить FAQ?",
@@ -462,7 +462,7 @@
"ERROR_MESSAGE": "Произошла ошибка при удалении FAQ, попробуйте еще раз."
},
"FILTER": {
"ASSISTANT": "Ассистент: {selected}",
"ASSISTANT": "Помощник: {selected}",
"STATUS": "Статус: {selected}",
"ALL_ASSISTANTS": "Все"
},
@@ -472,7 +472,7 @@
"APPROVED": "Одобрено",
"ALL": "Все"
},
"FORM_DESCRIPTION": "Добавьте вопрос и соответствующий ему ответ в базу знаний и выберите ассистента, с которым он должен связаться.",
"FORM_DESCRIPTION": "Добавьте вопрос и соответствующий ему ответ в базу знаний и выберите помощника, с которым он должен связаться.",
"CREATE": {
"TITLE": "Добавить FAQ",
"SUCCESS_MESSAGE": "Ответ успешно добавлен.",
@@ -487,59 +487,59 @@
"ANSWER": {
"LABEL": "Ответ",
"PLACEHOLDER": "Введите ответ",
"ERROR": "Пожалуйста, введите корректный ответ."
"ERROR": "Please provide a valid answer."
},
"ASSISTANT": {
"LABEL": "Ассистент",
"PLACEHOLDER": "Выбрать ассистента",
"ERROR": "Выбрать ассистента"
"LABEL": "Помощник",
"PLACEHOLDER": "Select an assistant",
"ERROR": "Please select an assistant."
}
},
"EDIT": {
"TITLE": "Обновить FAQ",
"SUCCESS_MESSAGE": "FAQ успешно обновлён",
"ERROR_MESSAGE": "Произошла ошибка при обновлении FAQ, пожалуйста, попробуйте ещё раз",
"APPROVE_SUCCESS_MESSAGE": "FAQ был отмечен как одобренный"
"TITLE": "Update the FAQ",
"SUCCESS_MESSAGE": "The FAQ has been successfully updated",
"ERROR_MESSAGE": "There was an error updating the FAQ, please try again",
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
},
"OPTIONS": {
"APPROVE": "Отметить как одобренный",
"EDIT_RESPONSE": "FAQ отмечен как одобренный",
"DELETE_RESPONSE": "Удалить FAQ"
"APPROVE": "Mark as approved",
"EDIT_RESPONSE": "Edit FAQ",
"DELETE_RESPONSE": "Delete FAQ"
},
"EMPTY_STATE": {
"TITLE": "FAQ не найдены",
"SUBTITLE": "FAQ помогают вашему ассистенту быстро и точно отвечать на вопросы клиентов. Их можно генерировать автоматически из вашего контента или добавлять вручную."
"TITLE": "No FAQs Found",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually."
}
},
"INBOXES": {
"HEADER": "Подключённые источники входящих",
"ADD_NEW": "Подключить новый источник входящих",
"HEADER": "Connected Inboxes",
"ADD_NEW": "Connect a new inbox",
"OPTIONS": {
"DISCONNECT": "Отключиться"
},
"DELETE": {
"TITLE": "Вы уверены, что хотите отключить этот источник входящих?",
"TITLE": "Are you sure to disconnect the inbox?",
"DESCRIPTION": "",
"CONFIRM": "Да, удалить",
"SUCCESS_MESSAGE": "Источник входящих успешно отключён.",
"ERROR_MESSAGE": "Произошла ошибка при отключении источника входящих, пожалуйста, попробуйте ещё раз."
"SUCCESS_MESSAGE": "The inbox was successfully disconnected.",
"ERROR_MESSAGE": "There was an error disconnecting the inbox, please try again."
},
"FORM_DESCRIPTION": "Выберите источник входящих для подключения к ассистенту.",
"FORM_DESCRIPTION": "Choose an inbox to connect with the assistant.",
"CREATE": {
"TITLE": "Подключить источник входящих",
"SUCCESS_MESSAGE": "Источник входящих успешно подключён.",
"ERROR_MESSAGE": "Произошла ошибка при подключении источника входящих. Пожалуйста, попробуйте ещё раз."
"TITLE": "Connect an Inbox",
"SUCCESS_MESSAGE": "The inbox was successfully connected.",
"ERROR_MESSAGE": "An error occurred while connecting the inbox. Please try again."
},
"FORM": {
"INBOX": {
"LABEL": "Электронная почта",
"PLACEHOLDER": "Выберите источник входящих для развертывания ассистента.",
"ERROR": "Необходимо выбрать источник входящих."
"PLACEHOLDER": "Choose the inbox to deploy the assistant.",
"ERROR": "An inbox selection is required."
}
},
"EMPTY_STATE": {
"TITLE": "Нет подключённых источников входящих",
"SUBTITLE": "Подключение источника входящих позволяет ассистенту обрабатывать первые вопросы ваших клиентов до их передачи вам."
"TITLE": "No Connected Inboxes",
"SUBTITLE": "Connecting an inbox allows the assistant to handle initial questions from your customers before transferring them to you."
}
}
}
@@ -124,7 +124,7 @@
},
"AGENT_REPORTS": {
"HEADER": "Обзор агентов",
"DESCRIPTION": "Легко отслеживайте эффективность работы агентов с помощью ключевых метрик, таких как количество бесед, время ответа, время решения проблем и количество решённых случаев. Нажмите на имя агента, чтобы узнать подробности.",
"DESCRIPTION": "Easily track agent performance with key metrics such as conversations, response times, resolution times, and resolved cases. Click an agents name to learn more.",
"LOADING_CHART": "Загрузка данных графика...",
"NO_ENOUGH_DATA": "Недостаточно данных для создания отчета, пожалуйста, повторите попытку позже.",
"DOWNLOAD_AGENT_REPORTS": "Сказать отчёт по агентам",
@@ -259,7 +259,7 @@
},
"INBOX_REPORTS": {
"HEADER": "Обзор входящих",
"DESCRIPTION": "Быстро просматривайте эффективность источника входящих с основными метриками — количеством бесед, временем ответа, временем решения проблем и количеством решённых случаев — всё в одном месте. Нажмите на имя источника для получения подробной информации.",
"DESCRIPTION": "Quickly view your inbox performance with key metrics like conversations, response times, resolution times, and resolved cases—all in one place. Click an inbox name for more details.",
"LOADING_CHART": "Загрузка данных графика...",
"NO_ENOUGH_DATA": "Недостаточно данных для создания отчета, пожалуйста, повторите попытку позже.",
"DOWNLOAD_INBOX_REPORTS": "Скачать отчет по входящим",
@@ -327,7 +327,7 @@
},
"TEAM_REPORTS": {
"HEADER": "Обзор команды",
"DESCRIPTION": "Получите обзор работы вашей команды с основными метриками, такими как количество бесед, время ответа, время решения проблем и количество решённых случаев. Нажмите на имя команды для подробностей.",
"DESCRIPTION": "Get a snapshot of your teams performance with essential metrics, including conversations, response times, resolution times, and resolved cases. Click a team name for more details.",
"LOADING_CHART": "Загрузка данных графика...",
"NO_ENOUGH_DATA": "Недостаточно данных для создания отчета, пожалуйста, повторите попытку позже.",
"DOWNLOAD_TEAM_REPORTS": "Скачать отчет по команде",
@@ -546,9 +546,9 @@
"INBOX": "Электронная почта",
"AGENT": "Оператор",
"TEAM": "Команда",
"AVG_RESOLUTION_TIME": "Среднее время решения",
"AVG_FIRST_RESPONSE_TIME": "Среднее время первого ответа",
"AVG_REPLY_TIME": "Среднее время ожидания клиента",
"AVG_RESOLUTION_TIME": "Avg. Resolution Time",
"AVG_FIRST_RESPONSE_TIME": "Avg. First Response Time",
"AVG_REPLY_TIME": "Avg. Customer Waiting Time",
"RESOLUTION_COUNT": "Количество завершенных",
"CONVERSATIONS": "Количество диалогов"
}
@@ -11,8 +11,8 @@
"CONVERSATIONS": "Диалоги",
"MESSAGES": "Сообщения"
},
"VIEW_MORE": "Посмотреть больше",
"LOAD_MORE": "Загрузить ещё",
"VIEW_MORE": "View more",
"LOAD_MORE": "Load more",
"SEARCHING_DATA": "Идёт поиск",
"LOADING_DATA": "Загрузка",
"EMPTY_STATE": "Не найдено {item} для запроса '{query}'",
@@ -36,20 +36,20 @@
}
},
"INTERFACE_SECTION": {
"TITLE": "Интерфейс",
"NOTE": "Настройте внешний вид и оформление вашей панели Chatwoot.",
"TITLE": "Interface",
"NOTE": "Customize the look and feel of your Chatwoot dashboard.",
"FONT_SIZE": {
"TITLE": "Размер шрифта",
"NOTE": "Измените размер текста на всей панели в соответствии с вашими предпочтениями.",
"UPDATE_SUCCESS": "Настройки шрифта успешно обновлены",
"UPDATE_ERROR": "Произошла ошибка при обновлении настроек шрифта, пожалуйста, попробуйте ещё раз",
"TITLE": "Font size",
"NOTE": "Adjust the text size across the dashboard based on your preference.",
"UPDATE_SUCCESS": "Your font settings have been updated successfully",
"UPDATE_ERROR": "There is an error while updating the font settings, please try again",
"OPTIONS": {
"SMALLER": "Меньше",
"SMALL": "Маленький",
"SMALLER": "Smaller",
"SMALL": "Small",
"DEFAULT": "По умолчанию",
"LARGE": "Большой",
"LARGER": "Крупнее",
"EXTRA_LARGE": "Очень большой"
"LARGE": "Large",
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
}
},
@@ -162,7 +162,7 @@
"REQUEST_PUSH": "Включить push-уведомления",
"SLA_MISSED_FIRST_RESPONSE": "Отправить push-уведомление, если диалог не соответствует SLA первого ответа",
"SLA_MISSED_NEXT_RESPONSE": "Отправить push-уведомление, если диалог не успел получить следующий ответ SLA",
"SLA_MISSED_RESOLUTION": "Отправить push-уведомление, если диалог не соответствует SLA"
"SLA_MISSED_RESOLUTION": "Отправить push-уведомление, если диалог не соответствует уровню разрешения SLA"
},
"PROFILE_IMAGE": {
"LABEL": "Изображение профиля"
@@ -281,9 +281,9 @@
"SETTINGS": "Настройки",
"CONTACTS": "Контакты",
"CAPTAIN": "Капитан",
"CAPTAIN_ASSISTANTS": "Ассистенты",
"CAPTAIN_ASSISTANTS": "Помощники",
"CAPTAIN_DOCUMENTS": "Документы",
"CAPTAIN_RESPONSES": "FAQ",
"CAPTAIN_RESPONSES": "FAQs",
"HOME": "Главная",
"AGENTS": "Операторы",
"AGENT_BOTS": "Боты",
@@ -345,14 +345,14 @@
},
"BILLING_SETTINGS": {
"TITLE": "Платёж",
"DESCRIPTION": "Управляйте подпиской здесь, обновите тарифный план и получите больше возможностей для вашей команды.",
"DESCRIPTION": "Manage your subscription here, upgrade your plan and get more for your team.",
"CURRENT_PLAN": {
"TITLE": "Текущий план",
"PLAN_NOTE": "На данный момент вы подписаны на ** план{plan}** с **{quantity}** лицензиями",
"SEAT_COUNT": "Количество мест",
"RENEWS_ON": "Продление"
"SEAT_COUNT": "Number of seats",
"RENEWS_ON": "Renews on"
},
"VIEW_PRICING": "Посмотреть цены",
"VIEW_PRICING": "View Pricing",
"MANAGE_SUBSCRIPTION": {
"TITLE": "Управление подпиской",
"DESCRIPTION": "Просматривайте ваши предыдущие счета, редактируйте платежные реквизиты или отмените подписку.",
@@ -360,11 +360,11 @@
},
"CAPTAIN": {
"TITLE": "Капитан",
"DESCRIPTION": "Управляйте использованием и кредитами для Captain AI.",
"BUTTON_TXT": "Купить дополнительные кредиты",
"DESCRIPTION": "Manage usage and credits for Captain AI.",
"BUTTON_TXT": "Buy more credits",
"DOCUMENTS": "Документы",
"RESPONSES": "Ответы",
"UPGRADE": "Captain недоступен в бесплатном тарифном плане, обновите его, чтобы получить доступ к ассистентам, copilot и другим функциям."
"RESPONSES": "Responses",
"UPGRADE": "Captain is not available on the free plan, upgrade now to get access to assistants, copilot and more."
},
"CHAT_WITH_US": {
"TITLE": "Нужна помощь?",
@@ -3,12 +3,13 @@ import { mapGetters } from 'vuex';
import { defineAsyncComponent } from 'vue';
import NextSidebar from 'next/sidebar/Sidebar.vue';
import Sidebar from '../../components/layout/Sidebar.vue';
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal.vue';
import AddAccountModal from 'dashboard/components/layout/sidebarComponents/AddAccountModal.vue';
import AccountSelector from 'dashboard/components/layout/sidebarComponents/AccountSelector.vue';
import AddLabelModal from 'dashboard/routes/dashboard/settings/labels/AddLabel.vue';
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
import PaymentPaywall from 'dashboard/components/app/PaymentPaywall.vue';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { useAccount } from 'dashboard/composables/useAccount';
@@ -19,10 +20,6 @@ import { FEATURE_FLAGS } from 'dashboard/featureFlags';
const CommandBar = defineAsyncComponent(
() => import('./commands/commandbar.vue')
);
const Sidebar = defineAsyncComponent(
() => import('../../components/layout/Sidebar.vue')
);
import { emitter } from 'shared/helpers/mitt';
export default {
@@ -32,6 +29,7 @@ export default {
CommandBar,
WootKeyShortcutModal,
AddAccountModal,
PaymentPaywall,
AccountSelector,
AddLabelModal,
NotificationPanel,
@@ -197,7 +195,9 @@ export default {
@show-add-label-popup="showAddLabelPopup"
/>
<main class="flex flex-1 h-full min-h-0 px-0 overflow-hidden">
<router-view />
<PaymentPaywall>
<router-view />
</PaymentPaywall>
<CommandBar />
<AccountSelector
:show-account-modal="showAccountModal"
@@ -121,6 +121,8 @@ export default {
this.isALineChannel ||
this.isAPIInbox ||
(this.isAnEmailChannel && !this.inbox.provider) ||
this.isAMicrosoftInbox ||
this.isAGoogleInbox ||
this.isAWhatsAppChannel ||
this.isAWebWidgetInbox
) {
@@ -39,14 +39,13 @@ export default {
},
mixins: [globalConfigMixin],
setup() {
const { isEditorHotKeyEnabled, updateUISettings } = useUISettings();
const { isEditorHotKeyEnabled } = useUISettings();
const { currentFontSize, updateFontSize } = useFontSize();
return {
currentFontSize,
updateFontSize,
isEditorHotKeyEnabled,
updateUISettings,
};
},
data() {
@@ -23,6 +23,7 @@ const state = {
export const getters = {
getAccount: $state => id => {
console.log('account', id);
return findRecordById($state, id);
},
getUIFlags($state) {
@@ -2,15 +2,8 @@ class Inboxes::FetchImapEmailInboxesJob < ApplicationJob
queue_as :scheduled_jobs
def perform
email_inboxes = Inbox.where(channel_type: 'Channel::Email')
email_inboxes.find_each(batch_size: 100) do |inbox|
::Inboxes::FetchImapEmailsJob.perform_later(inbox.channel) if should_fetch_emails?(inbox)
Inbox.where(channel_type: 'Channel::Email').all.find_each(batch_size: 100) do |inbox|
::Inboxes::FetchImapEmailsJob.perform_later(inbox.channel) if inbox.channel.imap_enabled
end
end
private
def should_fetch_emails?(inbox)
inbox.channel.imap_enabled && !inbox.account.suspended?
end
end
+3 -18
View File
@@ -37,7 +37,7 @@ class Channel::Sms < ApplicationRecord
body = message_body(contact_number, message.content)
body['media'] = message.attachments.map(&:download_url) if message.attachments.present?
send_to_bandwidth(body, message)
send_to_bandwidth(body)
end
def send_text_message(contact_number, message_content)
@@ -56,7 +56,7 @@ class Channel::Sms < ApplicationRecord
}
end
def send_to_bandwidth(body, message = nil)
def send_to_bandwidth(body)
response = HTTParty.post(
"#{api_base_path}/users/#{provider_config['account_id']}/messages",
basic_auth: bandwidth_auth,
@@ -64,22 +64,7 @@ class Channel::Sms < ApplicationRecord
body: body.to_json
)
if response.success?
response.parsed_response['id']
else
handle_error(response, message)
nil
end
end
def handle_error(response, message)
Rails.logger.error("[#{account_id}] Error sending SMS: #{response.parsed_response['description']}")
return if message.blank?
# https://dev.bandwidth.com/apis/messaging-apis/messaging/#tag/Messages/operation/createMessage
message.external_error = response.parsed_response['description']
message.status = :failed
message.save!
response.success? ? response.parsed_response['id'] : nil
end
def bandwidth_auth
@@ -20,30 +20,15 @@ class Facebook::SendOnFacebookService < Base::SendOnChannelService
end
def send_message_to_facebook(delivery_params)
parsed_result = deliver_message(delivery_params)
return if parsed_result.nil?
result = Facebook::Messenger::Bot.deliver(delivery_params, page_id: channel.page_id)
parsed_result = JSON.parse(result)
if parsed_result['error'].present?
message.update!(status: :failed, external_error: external_error(parsed_result))
Rails.logger.info "Facebook::SendOnFacebookService: Error sending message to Facebook : Page - #{channel.page_id} : #{parsed_result}"
Rails.logger.info "Facebook::SendOnFacebookService: Error sending message to Facebook : Page - #{channel.page_id} : #{result}"
end
message.update!(source_id: parsed_result['message_id']) if parsed_result['message_id'].present?
end
def deliver_message(delivery_params)
result = Facebook::Messenger::Bot.deliver(delivery_params, page_id: channel.page_id)
JSON.parse(result)
rescue JSON::ParserError
message.update!(status: :failed, external_error: 'Facebook was unable to process this request')
Rails.logger.error "Facebook::SendOnFacebookService: Error parsing JSON response from Facebook : Page - #{channel.page_id} : #{result}"
nil
rescue Net::OpenTimeout
message.update!(status: :failed, external_error: 'Request timed out, please try again later')
Rails.logger.error "Facebook::SendOnFacebookService: Timeout error sending message to Facebook : Page - #{channel.page_id}"
nil
end
def fb_text_message_params
{
recipient: { id: contact.get_source_id(inbox.id) },
@@ -70,28 +70,22 @@ class Instagram::SendOnInstagramService < Base::SendOnChannelService
query: query
)
handle_response(response, message_content)
end
def handle_response(response, message_content)
parsed_response = response.parsed_response
if response.success? && parsed_response['error'].blank?
message.update!(source_id: parsed_response['message_id'])
parsed_response
else
external_error = external_error(parsed_response)
Rails.logger.error("Instagram response: #{external_error} : #{message_content}")
message.update!(status: :failed, external_error: external_error)
nil
if response[:error].present?
Rails.logger.error("Instagram response: #{response['error']} : #{message_content}")
message.status = :failed
message.external_error = external_error(response)
end
message.source_id = response['message_id'] if response['message_id'].present?
message.save!
response
end
def external_error(response)
# https://developers.facebook.com/docs/instagram-api/reference/error-codes/
error_message = response.dig('error', 'message')
error_code = response.dig('error', 'code')
error_message = response[:error][:message]
error_code = response[:error][:code]
"#{error_code} - #{error_message}"
end
@@ -27,33 +27,6 @@ class Whatsapp::Providers::BaseService
raise 'Overwrite this method in child class'
end
def error_message
raise 'Overwrite this method in child class'
end
def process_response(response)
parsed_response = response.parsed_response
if response.success? && parsed_response['error'].blank?
parsed_response['messages'].first['id']
else
handle_error(response)
nil
end
end
def handle_error(response)
Rails.logger.error response.body
return if @message.blank?
# https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes/#sample-response
error_message = error_message(response)
return if error_message.blank?
@message.external_error = error_message
@message.status = :failed
@message.save!
end
def create_buttons(items)
buttons = []
items.each do |item|
@@ -1,6 +1,5 @@
class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseService
def send_message(phone_number, message)
@message = message
if message.attachments.present?
send_attachment_message(phone_number, message)
elsif message.content_type == 'input_select'
@@ -79,7 +78,6 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
}
type_content['caption'] = message.content unless %w[audio sticker].include?(type)
type_content['filename'] = attachment.file.filename if type == 'document'
response = HTTParty.post(
"#{api_base_path}/messages",
headers: api_headers,
@@ -93,9 +91,13 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
process_response(response)
end
def error_message(response)
# {"meta": {"success": false, "http_code": 400, "developer_message": "errro-message", "360dialog_trace_id": "someid"}}
response.parsed_response.dig('meta', 'developer_message')
def process_response(response)
if response.success?
response['messages'].first['id']
else
Rails.logger.error response.body
nil
end
end
def template_body_parameters(template_info)
@@ -1,7 +1,5 @@
class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseService
def send_message(phone_number, message)
@message = message
if message.attachments.present?
send_attachment_message(phone_number, message)
elsif message.content_type == 'input_select'
@@ -113,9 +111,13 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
process_response(response)
end
def error_message(response)
# https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes/#sample-response
response.parsed_response&.dig('error', 'message')
def process_response(response)
if response.success?
response['messages'].first['id']
else
Rails.logger.error response.body
nil
end
end
def template_body_parameters(template_info)
@@ -1,7 +1,3 @@
json.meta do
json.total_count @attachments_count
end
json.payload @attachments do |attachment|
json.message_id attachment.push_event_data[:message_id]
json.thumb_url attachment.push_event_data[:thumb_url]
-2
View File
@@ -68,8 +68,6 @@ am:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ ar:
invalid_operator: مشغل غير صالح. المشغل المسموح به لـ %{attribute_name} هو [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: قيمة غير صالحة. القيم المقدمة ل %{attribute_name} غير صالحة
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: فترة التبليغ %{since} إلى %{until}
utc_warning: التقرير الذي تم إنشاؤه في التوقيت العالمي الموحّد
-2
View File
@@ -68,8 +68,6 @@ az:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ bg:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ ca:
invalid_operator: Operador no vàlid. Els operadors permesos per a %{attribute_name} son [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Valor no vàlid. Els valors proporcionats per a %{attribute_name} no són vàlids
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Període d'informes %{since} a %{until}
utc_warning: L'informe generat es troba a la zona horària UTC
-2
View File
@@ -68,8 +68,6 @@ cs:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ da:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Rapporteringsperiode %{since} til %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ de:
invalid_operator: Ungültiger Operator. Die erlaubten Operatoren für %{attribute_name} sind [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Ungültiger Wert. Die Werte für %{attribute_name} sind ungültig
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Berichtszeitraum von %{since} bis %{until}
utc_warning: Der generierte Bericht ist in UTC-Zeitzone
-2
View File
@@ -68,8 +68,6 @@ el:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Περίοδος αναφοράς %{since} έως %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ es:
invalid_operator: Operador no válido. Los operadores permitidos para %{attribute_name} son [%{allowed_keys}].
invalid_query_operator: El operador de consulta debe ser "Y" o "O".
invalid_value: Valor no válido. Los valores proporcionados para %{attribute_name} no son válidos
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reportando el periodo desde %{since} hasta %{until}
utc_warning: El informe generado está en zona horaria UTC
-2
View File
@@ -68,8 +68,6 @@ fa:
invalid_operator: این عملیات مجاز نیست. عملیات های مجاز برای %{attribute_name} شامل %{allowed_keys} می باشد.
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: مقدار معتبر نیست. مقادیر ارائه شده برای %{attribute_name} معتبر نیست
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: زمان گزارش از %{since} تا %{until}
utc_warning: گزارش تولید شده در منطقه زمانی UTC است
-2
View File
@@ -68,8 +68,6 @@ fi:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Raportointijakso %{since} %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ fr:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Période de rapport %{since} à %{until}
utc_warning: Le rapport généré est dans le fuseau horaire UTC
-2
View File
@@ -68,8 +68,6 @@ he:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ hi:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ hr:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ hu:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Jelentési időszak %{since}-tól %{until}-ig
utc_warning: A generált riport UTC időzónát használ
-2
View File
@@ -68,8 +68,6 @@ hy:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ id:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Periode pelaporan %{since} hingga %{until}
utc_warning: Laporan yang dihasilkan berada dalam zona waktu UTC
-2
View File
@@ -68,8 +68,6 @@ is:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ it:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Periodo di segnalazione da %{since} a %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ ja:
invalid_operator: 無効な演算子です。%{attribute_name} に許可されている演算子は [%{allowed_keys}] です。
invalid_query_operator: クエリ演算子は "AND" または "OR" でなければなりません。
invalid_value: 無効な値です。%{attribute_name} に提供された値は無効です。
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: レポート期間 %{since} から %{until} まで
utc_warning: 生成されたレポートはUTCタイムゾーンです
-2
View File
@@ -68,8 +68,6 @@ ka:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ ko:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: 보고 기간 %{since} - %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ lt:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Ataskaitinis laikotarpis nuo %{since} iki %{until}
utc_warning: Sugeneruota ataskaita yra UTC laiko juostoje
-2
View File
@@ -68,8 +68,6 @@ lv:
invalid_operator: Nederīgs operators. Atļautie operatori priekš %{attribute_name} ir [%{allowed_keys}].
invalid_query_operator: Vaicājuma operatoram ir jābūt "UN" vai "VAI".
invalid_value: Nederīga vērtība. Norādītās vērtības priekš %{attribute_name} nav derīgas
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Ziņošanas periods %{since} līdz %{until}
utc_warning: Izveidotais pārskats atbilst UTC laika joslai
-2
View File
@@ -68,8 +68,6 @@ ml:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ ms:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ ne:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ nl:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Rapportering van %{since} tot %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@
invalid_operator: Ugyldig operatør. De tillatte operatørene for %{attribute_name} er [%{allowed_keys}].
invalid_query_operator: Spørrings-operatør må være enten "AND" eller "OR".
invalid_value: Ugyldig verdi. Verdiene angitt for %{attribute_name} er ugyldige
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Rapporteringsperiode %{since} til %{until}
utc_warning: Rapporten generert er i UTC tidssone
-2
View File
@@ -68,8 +68,6 @@ pl:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Okres raportowania od %{since} do %{until}
utc_warning: Generowany raport jest w strefie czasowej UTC
-2
View File
@@ -68,8 +68,6 @@ pt:
invalid_operator: Operador inválido. Os operadores permitidos para %{attribute_name} são [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Valor inválido. Os valores fornecidos para %{attribute_name} são inválidos
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Período do relatório de %{since} a %{until}
utc_warning: O relatório gerado está no fuso horário UTC
+1 -3
View File
@@ -68,8 +68,6 @@ pt_BR:
invalid_operator: Operador inválido. Os operadores permitidos para %{attribute_name} são [%{allowed_keys}].
invalid_query_operator: Operador de consulta deve ser "E" ou "OU".
invalid_value: Valor inválido. Os valores fornecidos para %{attribute_name} são inválidos
custom_attribute_definition:
key_conflict: A chave fornecida não é permitida pois pode entrar em conflito com os atributos padrão.
reports:
period: Reportando o período %{since} a %{until}
utc_warning: O relatório gerado está em fuso horário UTC
@@ -135,7 +133,7 @@ pt_BR:
messages:
instagram_story_content: '%{story_sender} mencionou você na conversa: '
instagram_deleted_story_content: Este Story não está mais disponível.
deleted: Esta mensagem foi excluída
deleted: Esta mensagem foi apagada
delivery_status:
error_code: 'Código de erro: %{error_code}'
activity:
-2
View File
@@ -68,8 +68,6 @@ ro:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Perioada de raportare %{since}-%{until}
utc_warning: Raportul generat este în fusul orar UTC
+3 -5
View File
@@ -68,8 +68,6 @@ ru:
invalid_operator: Неверный оператор. Допустимыми операторами для %{attribute_name} являются [%{allowed_keys}].
invalid_query_operator: Оператор запроса должен быть "AND" или "OR".
invalid_value: Недопустимое значение. Значения, предоставленные для %{attribute_name} являются недопустимыми
custom_attribute_definition:
key_conflict: Предоставленный ключ не разрешён, так как он может конфликтовать со стандартными атрибутами.
reports:
period: Отчётный период с %{since} по %{until}
utc_warning: Отчёт создан в часовом поясе UTC
@@ -167,7 +165,7 @@ ru:
removed: '%{user_name} удалил политику SLA %{sla_name}'
muted: '%{user_name} заглушил(а) этот разговор'
unmuted: '%{user_name} включил(а) уведомления для разговора'
auto_resolution_message: 'Разговор закрывается, поскольку он был неактивен в течение длительного времени. Пожалуйста, начните новый разговор, если потребуется дополнительная помощь.'
auto_resolution_message: 'Resolving the conversation as it has been inactive for a while. Please start a new conversation if you need further assistance.'
templates:
greeting_message_body: '%{account_name} как правило отвечает в течении несколько часов.'
ways_to_reach_you_message_body: 'Оставьте ваш email для связи'
@@ -218,8 +216,8 @@ ru:
name: 'Linear'
description: 'Создавайте или прикрепляйте уже существующие задачи в Linear непосредственно из окна диалога для более упорядоченного и эффективного процесса отслеживания проблем.'
captain:
copilot_error: 'Пожалуйста, подключите ассистента к этому источнику входящих для использования Copilot'
copilot_limit: 'У вас закончились кредиты для Copilot. Вы можете купить дополнительные кредиты в разделе биллинга.'
copilot_error: 'Please connect an assistant to this inbox to use Copilot'
copilot_limit: 'You are out of Copilot credits. You can buy more credits from the billing section.'
public_portal:
search:
search_placeholder: Поиск статьи по названию или содержанию...
-2
View File
@@ -68,8 +68,6 @@ sh:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ sk:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ sl:
invalid_operator: Neveljaven operater. Dovoljeni operaterji za %{attribute_name} so [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Neveljavna vrednost. Podane vrednosti za %{attribute_name} so neveljavne
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Obdobje poročanja %{since} do %{until}
utc_warning: Ustvarjeno poročilo je v časovnem pasu UTC
-2
View File
@@ -68,8 +68,6 @@ sq:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ sr-Latn:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Period izveštaja %{since} do %{until}
utc_warning: Generisani izveštaj je u UTC vremenskoj zoni
-2
View File
@@ -68,8 +68,6 @@ sv:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Rapporteringsperiod %{since} till %{until}
utc_warning: The report generated is in UTC timezone
-2
View File
@@ -68,8 +68,6 @@ ta:
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
invalid_query_operator: Query operator must be either "AND" or "OR".
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone

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