Compare commits

...
Author SHA1 Message Date
Sivin VargheseandGitHub 4ed150fd82 Merge branch 'develop' into fix/next-message-bubbles 2025-03-10 21:52:34 +05:30
PranavandGitHub 66fbe1bde7 fix: Hide Configuration page for Microsoft & Gmail channels (#11045)
The Microsoft and Gmail channels don’t function with custom SMTP or IMAP
configurations or forwarding option. Displaying the configuration tab
creates confusion among users.

This PR would eliminate the option for these channels.
2025-03-07 16:02:40 -08:00
a543cc92df chore: Update translations (#11043)
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2025-03-07 14:07:28 -08:00
PranavandGitHub 22331fb626 fix: Paginate attachments API (#11044)
There are attachments with over 1000 attachments (unusual) in
production, and some of them timeout. This PR would limit the number of
attachments to 100 (which is sufficient for viewing the files in the
gallery, pagination on the UI can be added later).
2025-03-07 13:56:10 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
08f0ea2c98 chore(deps): Bump vue-i18n from 9.14.2 to 9.14.3 (#11041)
Bumps
[vue-i18n](https://github.com/intlify/vue-i18n/tree/HEAD/packages/vue-i18n)
from 9.14.2 to 9.14.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/intlify/vue-i18n/releases">vue-i18n's
releases</a>.</em></p>
<blockquote>
<h2>v9.14.3</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>🔒 Security Fixes</h3>
<ul>
<li>fix: prototype pollution in <code>handleFlatJson</code>, about
details see <a
href="https://github.com/intlify/vue-i18n/security/advisories/GHSA-p2ph-7g93-hw3m">https://github.com/intlify/vue-i18n/security/advisories/GHSA-p2ph-7g93-hw3m</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/intlify/vue-i18n/compare/v9.14.2...v9.14.3">https://github.com/intlify/vue-i18n/compare/v9.14.2...v9.14.3</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/intlify/vue-i18n/blob/v9.14.3/CHANGELOG.md">vue-i18n's
changelog</a>.</em></p>
<blockquote>
<h1>v9.14.3 (2025-03-07T03:21:38Z)</h1>
<p>This changelog is generated by <a
href="https://github.com/intlify/vue-i18n/releases/tag/v9.14.3">GitHub
Releases</a></p>
<!-- raw HTML omitted -->
<p><strong>Full Changelog</strong>: <a
href="https://github.com/intlify/vue-i18n/compare/v9.14.2...v9.14.3">https://github.com/intlify/vue-i18n/compare/v9.14.2...v9.14.3</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/intlify/vue-i18n/commit/2e255c59ded7caf7c35f67190a336ce4cec82816"><code>2e255c5</code></a>
release: v9.14.3</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/6695eec30737265032dca70690df00ad8d3aa713"><code>6695eec</code></a>
fix: update package name for npm provenance</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/2b5149d42afe9b00a514e6b1ee2021212e47edf5"><code>2b5149d</code></a>
Revert &quot;release: v9.14.3&quot;</li>
<li><a
href="https://github.com/intlify/vue-i18n/commit/26449f9d74165736cd1d5ff768ff3c7d12d8a8d5"><code>26449f9</code></a>
release: v9.14.3</li>
<li>See full diff in <a
href="https://github.com/intlify/vue-i18n/commits/v9.14.3/packages/vue-i18n">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=vue-i18n&package-manager=npm_and_yarn&previous-version=9.14.2&new-version=9.14.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/chatwoot/chatwoot/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-07 11:49:36 -08:00
PranavandGitHub 8fefdea4e8 chore: Dynamically load OldSidebar if needed (#11038)
Move old sidebar loading to async
2025-03-06 19:12:54 -08:00
PranavandGitHub a513f152ed fix: Extend the locale without variant check for article locales as well (#11021)
We allow users to select locale variants when creating the help center
(e.g., pt_BR or en_UK). However, the selected variant may not always be
available for translation in the app. In such cases, we need to fall
back to either the base language or the default locale.

While this fallback logic was implemented for the portal locale, it was
missing for article locales.

This PR fixes that issue.
2025-03-06 18:24:46 -08:00
a0cf25ccde chore: Rescue slack link unfurling errors. (#11033)
Fixes
https://linear.app/chatwoot/issue/CW-4122/slackwebapierrorsmissingscope-missing-scope

This PR adds the ability to handle errors when scopes are missing during
link unfurling. Since link unfurling is just a nice-to-have feature that
doesn't affect core functionality, we will silently ignore these errors.

---------

Co-authored-by: Sojan <sojan@pepalo.com>
2025-03-06 15:57:16 -08:00
8d85a02ca9 feat: handle Channel errors (#11015)
This PR adds missing error handlers for the following channels and cases

1. WhatsApp - Generic Handlers for both Cloud and 360Dialog (Deprecated)
2. Instagram - Handler for a case where there is an HTTP error instead
of an `:error` in the 200 response
3. Facebook - Errors from the two sentry issues
([Net::OpenTimeout](https://chatwoot-p3.sentry.io/issues/6164805227) &
[JSON::ParserError](https://chatwoot-p3.sentry.io/issues/5903200786))
4. SMS: Generic handlers for Bandwidth SMS

#### Checklist

- [x] Bandwidth SMS
- [x] Whatsapp Cloud + 360 Dialog
- [x] Twilio SMS
- [x] Line
- [x] Telegram
- [x] Instagram
- [x] Facebook
- [x] GMail
- [x] 365 Mail
- [x] SMTP Mail

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
2025-03-06 20:09:47 +05:30
Sivin VargheseandGitHub 7e1458fd32 fix: Issue when saving hotkeys (#11026)
# Pull Request Template

## Description

This PR fixes an issue when saving send message button hotkeys, where a
TypeError occurs: `this.updateUISettings is not a function`.

This issue arises after merging this PR
https://github.com/chatwoot/chatwoot/pull/10974

Fixes
https://chatwoot-p3.sentry.io/issues/6339976939/events/ca9946f92cb74428a72f1f74976a56a3/

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
2025-03-06 10:53:02 +05:30
PranavandGitHub d017156f32 fix: Disable syncing IMAP if the account is suspended (#11031)
This PR disables the IMAP syncing if the account is suspended.
2025-03-05 17:10:24 -08:00
Sivin VargheseandGitHub 019e2de124 Merge branch 'develop' into fix/next-message-bubbles 2025-02-26 14:52:32 +05:30
iamsivin eaf7f8d4a7 chore: Minor fix 2025-02-21 12:10:22 +05:30
Sivin VargheseandGitHub 390f8bf9ac Merge branch 'develop' into fix/next-message-bubbles 2025-02-21 12:08:03 +05:30
Sivin VargheseandGitHub 89b8276946 Merge branch 'develop' into fix/next-message-bubbles 2025-02-20 23:36:23 +05:30
Pranav 27946f5af8 Adjust the size of the avatar 2025-02-19 18:53:37 -08:00
PranavandGitHub 898020e7c3 Merge branch 'develop' into fix/next-message-bubbles 2025-02-19 16:19:29 -08:00
Sivin VargheseandGitHub daf3ef2496 Merge branch 'develop' into fix/next-message-bubbles 2025-02-19 20:28:55 +05:30
PranavandGitHub 707ec5dfde Merge branch 'develop' into fix/next-message-bubbles 2025-02-12 16:43:00 -08:00
Sivin VargheseandGitHub fd115c8ab1 Merge branch 'develop' into fix/next-message-bubbles 2025-02-11 22:52:44 +05:30
Sivin VargheseandGitHub 45869c4bfa Merge branch 'develop' into fix/next-message-bubbles 2025-02-11 09:20:45 +05:30
iamsivin 30ebf7724e chore: Minor fix 2025-02-07 23:29:02 +05:30
Sivin VargheseandGitHub 8733c0c64a Merge branch 'develop' into fix/next-message-bubbles 2025-02-07 23:26:24 +05:30
Sivin VargheseandGitHub b378cb5f82 Merge branch 'develop' into fix/next-message-bubbles 2025-02-04 08:59:42 +05:30
Sivin VargheseandGitHub 5fffa86dc2 Merge branch 'develop' into fix/next-message-bubbles 2025-02-03 21:21:17 +05:30
Sivin VargheseandGitHub 05d29fb046 Merge branch 'develop' into fix/next-message-bubbles 2025-02-03 15:21:15 +05:30
Sivin VargheseandGitHub dff4a2208d Merge branch 'develop' into fix/next-message-bubbles 2025-02-03 12:55:01 +05:30
iamsivin 4f48d52eba chore: Minor fix 2025-01-31 21:56:36 +05:30
Sivin VargheseandGitHub b1ffce88e2 Merge branch 'develop' into fix/next-message-bubbles 2025-01-31 21:51:33 +05:30
Sivin VargheseandGitHub b5994e4b70 Merge branch 'develop' into fix/next-message-bubbles 2025-01-31 12:46:30 +05:30
Sivin VargheseandGitHub 1c2f2ecad1 Merge branch 'develop' into fix/next-message-bubbles 2025-01-30 15:27:25 +05:30
Sivin VargheseandGitHub d9ce71d21d Merge branch 'develop' into fix/next-message-bubbles 2025-01-30 09:38:16 +05:30
iamsivin e94a58d976 chore: Fix email overlay bg 2025-01-29 22:24:40 +05:30
Sivin VargheseandGitHub c41f7507bf Merge branch 'develop' into fix/next-message-bubbles 2025-01-29 19:36:26 +05:30
iamsivin ad9a34949b chore: Improvements 2025-01-29 16:29:49 +05:30
Sivin VargheseandGitHub 182cddd799 Merge branch 'develop' into fix/next-message-bubbles 2025-01-29 15:35:04 +05:30
iamsivin 5c55584038 chore: Fix bullets color 2025-01-29 15:24:02 +05:30
Shivam MishraandGitHub 0a6df5b67a Merge branch 'develop' into fix/next-message-bubbles 2025-01-29 13:59:33 +05:30
iamsivin d0ca703da2 chore: Review fix 2025-01-28 23:31:13 +05:30
Sivin VargheseandGitHub 0cac9e1a6d Merge branch 'develop' into fix/next-message-bubbles 2025-01-28 19:12:05 +05:30
iamsivin 7bdc4d78b2 chore: Adds padding for activity message bubble 2025-01-28 11:34:45 +05:30
iamsivin 2500341278 chore: Update border-b for email bubble 2025-01-27 20:05:02 +05:30
iamsivin d09701db37 chore: Update colors 2025-01-27 19:59:46 +05:30
Sivin VargheseandGitHub 8ac679e042 Merge branch 'develop' into fix/next-message-bubbles 2025-01-27 19:34:42 +05:30
iamsivin cc4817b233 chore: Minor fix 2025-01-27 19:19:15 +05:30
iamsivin 3c227f8048 chore: Minor fix 2025-01-27 18:55:51 +05:30
Sivin VargheseandGitHub 79545ba2b8 Merge branch 'develop' into fix/next-message-bubbles 2025-01-27 13:20:06 +05:30
iamsivin 1dd9e867cd chore: Minor fix 2025-01-24 23:52:33 +05:30
iamsivin 123f0cb61f chore: Update file chip 2025-01-24 23:42:06 +05:30
iamsivin 78220565a9 chore: Minor fix 2025-01-24 23:15:19 +05:30
Sivin VargheseandGitHub cbcb32acf5 Merge branch 'develop' into fix/next-message-bubbles 2025-01-24 23:08:32 +05:30
iamsivin d451329a81 chore: Minor fix 2025-01-24 18:59:34 +05:30
iamsivin 5f083b5c17 chore: Minor fix 2025-01-24 18:56:55 +05:30
iamsivin a296847dd4 chore: Adds playback speed for audio bubble 2025-01-24 18:38:37 +05:30
iamsivin 0a0d1e07f4 chore: Fis eslint 2025-01-24 18:04:55 +05:30
iamsivin 077a8fd498 chore: Update attachment chip preview 2025-01-24 17:56:53 +05:30
iamsivin 11e77e9349 fix: Message grouping, orientation, spacing, and tooltip 2025-01-24 14:25:29 +05:30
118 changed files with 1080 additions and 434 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.11)
rack (2.2.12)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-contrib (2.5.0)
@@ -6,6 +6,8 @@ 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]
@@ -24,7 +26,12 @@ 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
@@ -124,6 +131,10 @@ 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)
+19 -14
View File
@@ -5,10 +5,11 @@ module SwitchLocale
def switch_locale(&)
# priority is for locale set in query string (mostly for widget/from js sdk)
locale ||= locale_from_params
locale ||= params[:locale]
locale ||= locale_from_custom_domain
# if locale is not set in account, let's use DEFAULT_LOCALE env variable
locale ||= locale_from_env_variable
locale ||= ENV.fetch('DEFAULT_LOCALE', nil)
set_locale(locale, &)
end
@@ -32,26 +33,30 @@ module SwitchLocale
end
def set_locale(locale, &)
# if locale is empty, use default_locale
locale ||= I18n.default_locale
safe_locale = validate_and_get_locale(locale)
# Ensure locale won't bleed into other requests
# https://guides.rubyonrails.org/i18n.html#managing-the-locale-across-requests
I18n.with_locale(locale, &)
I18n.with_locale(safe_locale, &)
end
def locale_from_params
I18n.available_locales.map(&:to_s).include?(params[:locale]) ? params[:locale] : nil
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
end
def locale_from_account(account)
return unless account
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
account.locale
end
end
@@ -1,4 +1,6 @@
class Public::Api::V1::Portals::BaseController < PublicController
include SwitchLocale
before_action :show_plain_layout
before_action :set_color_scheme
before_action :set_global_config
@@ -27,14 +29,7 @@ class Public::Api::V1::Portals::BaseController < PublicController
end
def switch_locale_with_portal(&)
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
@locale = validate_and_get_locale(params[:locale])
I18n.with_locale(@locale, &)
end
@@ -44,12 +39,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?
@locale = if article.category.present?
article.category.locale
else
article.portal.default_locale
end
article_locale = if article.category.present?
article.category.locale
else
article.portal.default_locale
end
@locale = validate_and_get_locale(article_locale)
I18n.with_locale(@locale, &)
end
@@ -146,17 +146,19 @@
--solid-2: 255 255 255;
--solid-3: 255 255 255;
--solid-active: 255 255 255;
--solid-amber: 252 232 193;
--solid-blue: 218 236 255;
--solid-amber: 255 228 181;
--solid-blue: 182 216 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: 201, 202, 207, 0.15;
--alpha-2: 196, 197, 198, 0.22;
--alpha-3: 255, 255, 255, 0.96;
--black-alpha-1: 0, 0, 0, 0.12;
--black-alpha-1: 0, 0, 0, 0.08;
--black-alpha-2: 0, 0, 0, 0.04;
--border-blue: 39, 129, 246, 0.5;
--white-alpha: 255, 255, 255, 0.8;
--white-alpha: 255, 255, 255, 0.84;
}
.dark {
@@ -240,16 +242,18 @@
--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: 42 37 30;
--solid-blue: 16 49 91;
--solid-amber: 56 50 41;
--solid-blue: 4 51 101;
--solid-iris: 38 42 101;
--text-blue: 126 182 255;
--solid-purple: 51 51 107;
--solid-red: 78 46 55;
--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'],
['#2A2A2A', '#ADB1B8'],
['#023B37', '#0BD8B6'],
['#2A2A2A', '#ADB1B8'],
['#27264D', '#A19EFF'],
['#1D2E62', '#9EB1FF'],
],
light: [
['#FBDCEF', '#C2298A'],
['#FFE0BB', '#99543A'],
['#E8E8E8', '#60646C'],
['#CCF3EA', '#008573'],
['#E8E8E8', '#60646C'],
['#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 },
currentUserId: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties
groupWithNext: { type: Boolean, default: false },
inboxId: { type: Number, default: null }, // eslint-disable-line vue/no-unused-properties
inboxSupportsReplyTo: { type: Object, default: () => ({}) },
@@ -173,7 +173,11 @@ const variant = computed(() => {
return variants[props.messageType] || MESSAGE_VARIANTS.USER;
});
const isMyMessage = computed(() => {
const isBotOrAgentMessage = computed(() => {
if (props.messageType === MESSAGE_TYPES.ACTIVITY) {
return false;
}
// if an outgoing message is still processing, then it's definitely a
// message sent by the current user
if (
@@ -182,17 +186,15 @@ const isMyMessage = computed(() => {
) {
return true;
}
const senderId = props.senderId ?? props.sender?.id;
const senderType = props.senderType ?? props.sender?.type;
if (!senderType || !senderId) {
return false;
return true;
}
return (
senderType.toLowerCase() === SENDER_TYPES.USER.toLowerCase() &&
props.currentUserId === senderId
);
return senderType.toLowerCase() === SENDER_TYPES.USER.toLowerCase();
});
/**
@@ -200,7 +202,7 @@ const isMyMessage = computed(() => {
* @returns {import('vue').ComputedRef<'left'|'right'|'center'>} The computed orientation
*/
const orientation = computed(() => {
if (isMyMessage.value) {
if (isBotOrAgentMessage.value) {
return ORIENTATION.RIGHT;
}
@@ -221,8 +223,8 @@ const flexOrientationClass = computed(() => {
const gridClass = computed(() => {
const map = {
[ORIENTATION.LEFT]: 'grid grid-cols-[24px_1fr]',
[ORIENTATION.RIGHT]: 'grid grid-cols-1fr',
[ORIENTATION.LEFT]: 'grid grid-cols-1fr',
[ORIENTATION.RIGHT]: 'grid grid-cols-[1fr_24px]',
};
return map[orientation.value];
@@ -231,13 +233,13 @@ const gridClass = computed(() => {
const gridTemplate = computed(() => {
const map = {
[ORIENTATION.LEFT]: `
"avatar bubble"
"spacer meta"
`,
[ORIENTATION.RIGHT]: `
"bubble"
"meta"
`,
[ORIENTATION.RIGHT]: `
"bubble avatar"
"meta spacer"
`,
};
return map[orientation.value];
@@ -251,7 +253,7 @@ const shouldGroupWithNext = computed(() => {
const shouldShowAvatar = computed(() => {
if (props.messageType === MESSAGE_TYPES.ACTIVITY) return false;
if (orientation.value === ORIENTATION.RIGHT) return false;
if (orientation.value === ORIENTATION.LEFT) return false;
return true;
});
@@ -438,7 +440,7 @@ provideMessageContext({
isPrivate: computed(() => props.private),
variant,
orientation,
isMyMessage,
isBotOrAgentMessage,
shouldGroupWithNext,
});
</script>
@@ -448,7 +450,7 @@ provideMessageContext({
<div
v-if="shouldRenderMessage"
:id="`message${props.id}`"
class="flex w-full message-bubble-container mb-2"
class="flex w-full message-bubble-container"
:data-message-id="props.id"
:class="[
flexOrientationClass,
@@ -470,22 +472,28 @@ provideMessageContext({
'w-full': variant === MESSAGE_VARIANTS.EMAIL,
},
]"
class="gap-x-3"
class="gap-x-2"
:style="{
gridTemplateAreas: gridTemplate,
}"
>
<div
v-if="!shouldGroupWithNext && shouldShowAvatar"
v-tooltip.right-end="avatarTooltip"
v-tooltip.left-end="avatarTooltip"
class="[grid-area:avatar] flex items-end"
>
<Avatar v-bind="avatarInfo" :size="24" />
<Avatar v-bind="avatarInfo" :size="20" />
</div>
<div
class="[grid-area:bubble] flex"
:class="{
'ltr:pl-9 rtl:pl-0 justify-end': orientation === ORIENTATION.RIGHT,
'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,
'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"
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="{
'ltr:left-0 rtl:right-0': orientation === ORIENTATION.LEFT,
'ltr:right-0 rtl:left-0': orientation === ORIENTATION.RIGHT,
@@ -66,7 +66,11 @@ const shouldGroupWithNext = (index, searchList) => {
nextMessageType === MESSAGE_TYPES.TEMPLATE &&
currentMessageType === MESSAGE_TYPES.TEMPLATE;
if (!hasSameSender || areBothTemplates) return false;
const areBothActivity =
nextMessageType === MESSAGE_TYPES.ACTIVITY &&
currentMessageType === MESSAGE_TYPES.ACTIVITY;
if (!hasSameSender || areBothTemplates || areBothActivity) return false;
if (currentMessageType !== nextMessageType) return false;
@@ -95,6 +99,17 @@ 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>
@@ -112,6 +127,7 @@ const getInReplyToMessage = parentMessage => {
: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,6 +102,7 @@ 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-2 py-0.5 !rounded-full flex min-w-0 items-center gap-2"
class="px-3 py-1 !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-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.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.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-solid-3 px-4 py-2 rounded-lg text-sm text-center border border-n-container"
class="w-full block bg-n-alpha-white 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-solid-3 px-4 py-2 rounded-lg text-sm text-center border border-n-container"
class="w-full bg-n-alpha-white 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-strong': isIncoming,
'border-b border-n-slate-6': 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-gray-3 via-n-gray-3 via-20% to-transparent"
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"
>
<button
class="text-n-slate-12 py-2 px-8 mx-auto text-center flex items-center gap-2"
@@ -139,7 +139,10 @@ const hasQuotedMessage = computed(() => {
v-if="Array.isArray(attachments) && attachments.length"
class="px-4 pb-4 space-y-2"
>
<AttachmentChips :attachments="attachments" class="gap-1" />
<AttachmentChips
:attachments="attachments"
class="flex flex-col gap-2.5"
/>
</section>
</BaseBubble>
</template>
@@ -20,12 +20,15 @@ const isEmpty = computed(() => {
<template>
<BaseBubble class="px-4 py-3" data-bubble-name="text">
<div class="gap-3 flex flex-col">
<div class="gap-4 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="gap-2" />
<AttachmentChips
:attachments="attachments"
class="flex flex-col gap-2.5"
/>
<template v-if="isTemplate">
<div
v-if="contentAttributes.submittedEmail"
@@ -1,8 +1,7 @@
<script setup>
import { computed, defineOptions, useAttrs } from 'vue';
import ImageChip from 'next/message/chips/Image.vue';
import VideoChip from 'next/message/chips/Video.vue';
import ImageVideoChip from 'next/message/chips/AttachmentGrid.vue'; // Image and Video Grids are the same component
import AudioChip from 'next/message/chips/Audio.vue';
import FileChip from 'next/message/chips/File.vue';
import { useMessageContext } from '../provider.js';
@@ -37,7 +36,7 @@ const attrs = useAttrs();
const { orientation } = useMessageContext();
const classToApply = computed(() => {
const baseClasses = [attrs.class, 'flex', 'flex-wrap'];
const baseClasses = [attrs.class, 'flex', 'flex-wrap', 'gap-2'];
if (orientation.value === 'right') {
baseClasses.push('justify-end');
@@ -77,30 +76,25 @@ const files = computed(() => {
<template>
<div v-if="mediaAttachments.length" :class="classToApply">
<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>
<ImageVideoChip :attachments="mediaAttachments" />
</div>
<div v-if="recordings.length" :class="classToApply">
<div v-for="attachment in recordings" :key="attachment.id">
<div v-for="attachment in recordings" :key="attachment.id" class="w-full">
<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">
<FileChip
v-for="attachment in files"
:key="attachment.id"
:attachment="attachment"
/>
<div className="grid grid-cols-1 2xl:grid-cols-2 gap-3">
<FileChip
v-for="attachment in files"
:key="attachment.id"
:attachment="attachment"
/>
</div>
</div>
</template>
@@ -0,0 +1,82 @@
<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,7 +11,16 @@ defineProps({
type: Object,
required: true,
},
remainingCount: {
type: Number,
required: true,
},
shouldShowOverlay: {
type: Boolean,
required: true,
},
});
const hasError = ref(false);
const showGallery = ref(false);
@@ -20,12 +29,20 @@ const { filteredCurrentChatAttachments } = useMessageContext();
const handleError = () => {
hasError.value = true;
};
const handleGalleryClick = () => {
showGallery.value = true;
};
const handleGalleryClose = () => {
showGallery.value = false;
};
</script>
<template>
<div
class="size-[72px] overflow-hidden contain-content rounded-xl cursor-pointer"
@click="showGallery = true"
class="rounded-lg overflow-hidden contain-content cursor-pointer size-full"
@click="handleGalleryClick"
>
<div
v-if="hasError"
@@ -34,6 +51,7 @@ const handleError = () => {
<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"
@@ -41,12 +59,23 @@ const handleError = () => {
@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="() => (showGallery = false)"
@close="handleGalleryClose"
/>
</template>
@@ -10,17 +10,33 @@ 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="size-[72px] overflow-hidden contain-content rounded-xl cursor-pointer relative group"
@click="showGallery = true"
class="rounded-lg overflow-hidden contain-content cursor-pointer size-full"
@click="handleGalleryClick"
>
<video
:src="attachment.dataUrl"
@@ -29,6 +45,7 @@ const { filteredCurrentChatAttachments } = useMessageContext();
playsInline
/>
<div
v-if="!shouldShowOverlay"
class="absolute w-full h-full inset-0 p-1 flex items-center justify-center"
>
<div
@@ -41,12 +58,21 @@ const { filteredCurrentChatAttachments } = useMessageContext();
</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"
@error="onError"
@close="() => (showGallery = false)"
@close="handleGalleryClose"
/>
</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>} isMyMessage - Does the message belong to the current user
* @property {import('vue').ComputedRef<boolean>} isBotOrAgentMessage - Does the message belong to the bot or agents
* @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
*/
@@ -560,7 +560,7 @@
"ERROR_MESSAGE": "Wir konnten die Suche nicht abschließen. Bitte versuch es erneut."
},
"FORM": {
"GO_TO_CONVERSATION": "Aussicht",
"GO_TO_CONVERSATION": "Anzeigen",
"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": "Telegram",
"TELEGRAM": "Telegramm",
"LINE": "Line",
"API": "API-Kanal"
}
@@ -12,7 +12,7 @@
},
"WEBHOOK": {
"SUBSCRIBED_EVENTS": "Abonnierte Events",
"LEARN_MORE": "Learn more about webhooks",
"LEARN_MORE": "Mehr über Webhooks erfahren",
"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": "Example: {webhookExampleURL}",
"PLACEHOLDER": "Beispiel: {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} with AI ",
"WITH_AI": " {option} mit KI ",
"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": "Conversation (#{conversationId}) with {name}"
"LINK_TITLE": "Unterhaltung (#{conversationId}) mit {name}"
},
"ADD_OR_LINK": {
"TITLE": "Lineares Problem erstellen/verknüpfen",
@@ -294,7 +294,7 @@
"PRIORITY": "Priorität",
"ASSIGNEE": "Zugewiesener",
"LABELS": "Labels",
"CREATED_AT": "Created at {createdAt}"
"CREATED_AT": "Erstellt am {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": "Are you sure you want to delete the integration?",
"MESSAGE": "Are you sure you want to delete the integration?",
"TITLE": "Sind Sie sicher, dass Sie die Integration löschen möchten?",
"MESSAGE": "Sind Sie sicher, dass Sie die Integration löschen möchten?",
"CONFIRM": "Ja, löschen",
"CANCEL": "Stornieren"
}
@@ -313,27 +313,27 @@
"NAME": "Kapitän",
"COPILOT": {
"SEND_MESSAGE": "Nachricht senden...",
"LOADER": "Captain is thinking",
"LOADER": "Captain denkt nach",
"YOU": "Sie",
"USE": "Use this",
"RESET": "Reset",
"SELECT_ASSISTANT": "Select Assistant"
"USE": "Verwenden",
"RESET": "Zurücksetzen",
"SELECT_ASSISTANT": "Assistent auswählen"
},
"PAYWALL": {
"TITLE": "Upgrade to use Captain AI",
"TITLE": "Upgrade auf Captain AI",
"AVAILABLE_ON": "Captain is not available on the free plan.",
"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"
"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"
},
"ENTERPRISE_PAYWALL": {
"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."
"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."
},
"BANNER": {
"RESPONSES": "You've used over 80% of your response limit. To continue using Captain AI, please upgrade.",
"DOCUMENTS": "Document limit reached. Upgrade to continue using Captain AI."
"DOCUMENTS": "Dokumentenlimit erreicht. Upgraden um Cpatain AI weiter zu verwenden."
},
"FORM": {
"CANCEL": "Stornieren",
@@ -100,7 +100,7 @@
"NO_RESULTS": "Nenhum resultado encontrado."
},
"MULTI_SELECTOR": {
"PLACEHOLDER": "Nenhuma",
"PLACEHOLDER": "Nenhum",
"TITLE": {
"AGENT": "Selecionar agente",
"TEAM": "Selecionar time"
@@ -1,16 +1,16 @@
{
"AUDIT_LOGS": {
"HEADER": "Registros de Auditoria",
"HEADER_BTN_TXT": "Adicionar Registros de Auditoria",
"HEADER": "Auditoria",
"HEADER_BTN_TXT": "Adicionar Logs 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>Registros de Auditoria</b> </p><p> Registros de Auditoria são trilhas para eventos e ações em um Sistema Chatwoot. </p>",
"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>",
"LIST": {
"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.",
"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.",
"TABLE_HEADER": {
"ACTIVITY": "Usuário",
"TIME": "Ação",
@@ -27,12 +27,12 @@
"ERROR": "Falha ao atualizar etiquetas"
},
"CONVERSATION": {
"TITLE": "Marcador da conversa",
"ADD_BUTTON": "Adicionar marcador"
"TITLE": "Etiquetas da conversa",
"ADD_BUTTON": "Adicionar etiquetas"
},
"LABEL_SELECT": {
"TITLE": "Adicionar marcador",
"PLACEHOLDER": "Pesquisar marcador ",
"TITLE": "Adicionar etiquetas",
"PLACEHOLDER": "Pesquisar etiquetas",
"NO_RESULT": "Nenhuma etiqueta encontrada",
"CREATE_LABEL": "Criar etiqueta"
}
@@ -65,7 +65,7 @@
"HEADER": {
"RESOLVE_ACTION": "Resolver",
"REOPEN_ACTION": "Reabrir",
"OPEN_ACTION": "Abertas",
"OPEN_ACTION": "Abrir",
"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 atalhos.",
"MSG_INPUT": "Shift + enter para nova linha. Digite '/' para selecionar uma Resposta Pronta.",
"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 registros de auditoria, capacidade do agente e muito mais.",
"UPGRADE_PROMPT": "Atualize para um plano pago para acessar recursos avançados como logs 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": "Configurações da conta",
"TITLE": "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": "Are you sure you want to delete the integration?",
"MESSAGE": "Are you sure you want to delete the integration?",
"TITLE": "Tem certeza que deseja excluir esta integração?",
"MESSAGE": "Tem certeza que deseja excluir esta integração?",
"CONFIRM": "Sim, excluir",
"CANCEL": "Cancelar"
}
@@ -317,7 +317,7 @@
"YOU": "Você",
"USE": "Use isto",
"RESET": "Reiniciar",
"SELECT_ASSISTANT": "Select Assistant"
"SELECT_ASSISTANT": "Selecione o Assistente"
},
"PAYWALL": {
"TITLE": "Atualize para usar o Capitão IA",
@@ -440,19 +440,19 @@
"DOCUMENTABLE": {
"CONVERSATION": "Conversação #{id}"
},
"SELECTED": "{count} selected",
"BULK_APPROVE_BUTTON": "Approve",
"SELECTED": "{count} selecionado",
"BULK_APPROVE_BUTTON": "Aprovar",
"BULK_DELETE_BUTTON": "Excluir",
"BULK_APPROVE": {
"SUCCESS_MESSAGE": "FAQs approved successfully",
"ERROR_MESSAGE": "There was an error approving the FAQs, please try again."
"SUCCESS_MESSAGE": "Perguntas Frequentes aprovadas com sucesso",
"ERROR_MESSAGE": "Ocorreu um erro ao aprovar as Perguntas Frequentes. Tente novamente."
},
"BULK_DELETE": {
"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."
"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."
},
"DELETE": {
"TITLE": "Tem certeza que deseja excluir o FAQ?",
@@ -1,7 +1,7 @@
{
"LABEL_MGMT": {
"HEADER": "Etiquetas",
"HEADER_BTN_TXT": "Adicionar marcador",
"HEADER_BTN_TXT": "Adicionar etiqueta",
"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 do marcador",
"PLACEHOLDER": "Nome do marcador",
"REQUIRED_ERROR": "O nome do marcador é obrigatório",
"LABEL": "Nome da Etiqueta",
"PLACEHOLDER": "Nome da etiqueta",
"REQUIRED_ERROR": "O nome da etiqueta é 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 do marcador"
"PLACEHOLDER": "Descrição da etiqueta"
},
"COLOR": {
"LABEL": "Cor"
},
"SHOW_ON_SIDEBAR": {
"LABEL": "Exibir marcador na barra lateral"
"LABEL": "Exibir etiqueta na barra lateral"
},
"EDIT": "Alterar",
"CREATE": "Criar",
@@ -54,10 +54,10 @@
"SUGGESTED_LABELS": "Etiquetas sugeridos"
},
"ADD": {
"TITLE": "Adicionar marcador",
"TITLE": "Adicionar etiqueta",
"DESC": "Etiquetas permitem agrupar as conversas.",
"API": {
"SUCCESS_MESSAGE": "Marcador adicionado com sucesso",
"SUCCESS_MESSAGE": "Etiqueta adicionada com sucesso",
"ERROR_MESSAGE": "Ocorreu um erro, por favor tente novamente"
}
},
@@ -71,7 +71,7 @@
"DELETE": {
"BUTTON_TEXT": "Excluir",
"API": {
"SUCCESS_MESSAGE": "Marcador excluído com sucesso",
"SUCCESS_MESSAGE": "Etiqueta excluída 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": "Abertas",
"OPEN": "Abrir",
"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 do marcador",
"LABELS": "Nome da etiqueta",
"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": "Inserir (↵)",
"HEADING": "Enter (↵)",
"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": "Customize the look and feel of your Chatwoot dashboard.",
"NOTE": "Personalize a aparência do seu painel do Chatwoot.",
"FONT_SIZE": {
"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",
"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",
"OPTIONS": {
"SMALLER": "Smaller",
"SMALL": "Small",
"SMALLER": "Menor",
"SMALL": "Pequeno",
"DEFAULT": "Padrão",
"LARGE": "Large",
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
"LARGE": "Grande",
"LARGER": "Maior",
"EXTRA_LARGE": "Muito Grande"
}
}
},
@@ -287,13 +287,13 @@
"HOME": "Principal",
"AGENTS": "Agentes",
"AGENT_BOTS": "Robôs",
"AUDIT_LOGS": "Registros de Auditoria",
"AUDIT_LOGS": "Auditoria",
"INBOXES": "Caixas de Entrada",
"NOTIFICATIONS": "Notificações",
"CANNED_RESPONSES": "Respostas Prontas",
"INTEGRATIONS": "Integrações",
"PROFILE_SETTINGS": "Configurações do Perfil",
"ACCOUNT_SETTINGS": "Configurações da conta",
"ACCOUNT_SETTINGS": "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": "Definir a sua disponibilidade",
"SET_YOUR_AVAILABILITY": "Disponibilidade",
"SLA": "SLA",
"CUSTOM_ROLES": "Regras Personalizadas",
"BETA": "Beta",
@@ -1,9 +1,9 @@
{
"SLA": {
"HEADER": "Termos do Serviço",
"HEADER": "Acordo de Nível de Serviço",
"ADD_ACTION": "Adicionar SLA",
"ADD_ACTION_LONG": "Criar uma nova Política de SLA",
"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.",
"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.",
"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 registros de auditoria, capacidade do agente e muito mais.",
"UPGRADE_PROMPT": "Atualize para um plano pago para acessar recursos avançados como logs 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": "Something went wrong. Please try again later."
"ERROR_MESSAGE": "Что-то пошло не так. Пожалуйста, попробуйте позже."
},
"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": "Are you sure you want to delete the integration?",
"MESSAGE": "Are you sure you want to delete the integration?",
"TITLE": "Вы уверены, что хотите удалить интеграцию?",
"MESSAGE": "Вы уверены, что хотите удалить интеграцию?",
"CONFIRM": "Да, удалить",
"CANCEL": "Отменить"
}
@@ -317,18 +317,18 @@
"YOU": "Вы",
"USE": "Использовать это",
"RESET": "Сброс",
"SELECT_ASSISTANT": "Select Assistant"
"SELECT_ASSISTANT": "Выбрать ассистента"
},
"PAYWALL": {
"TITLE": "Обновите тарифный план, чтобы использовать Captain AI",
"AVAILABLE_ON": "Капитан недоступен на бесплатном тарифном плане.",
"UPGRADE_PROMPT": "Обновите тарифный план, чтобы получить доступ к помощникам, Captain AI и другому.",
"UPGRADE_PROMPT": "Обновите тарифный план, чтобы получить доступ к нашим ассистентам, copilot и другим функциям.",
"UPGRADE_NOW": "Обновить сейчас",
"CANCEL_ANYTIME": "Вы можете изменить или отменить ваш тарифный план в любое время"
},
"ENTERPRISE_PAYWALL": {
"AVAILABLE_ON": "Функция Captain AI доступна только в платном плане.",
"UPGRADE_PROMPT": "Обновите тарифный план, чтобы получить доступ к помощникам, Captain AI и другому.",
"UPGRADE_PROMPT": "Обновите тарифный план, чтобы получить доступ к нашим ассистентам, copilot и другим функциям.",
"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} selected",
"BULK_APPROVE_BUTTON": "Approve",
"SELECTED": "Выбрано {count}",
"BULK_APPROVE_BUTTON": "Одобрить",
"BULK_DELETE_BUTTON": "Удалить",
"BULK_APPROVE": {
"SUCCESS_MESSAGE": "FAQs approved successfully",
"ERROR_MESSAGE": "There was an error approving the FAQs, please try again."
"SUCCESS_MESSAGE": "FAQ успешно одобрены",
"ERROR_MESSAGE": "Произошла ошибка при одобрении FAQ, пожалуйста, попробуйте ещё раз."
},
"BULK_DELETE": {
"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."
"TITLE": "Удалить FAQ?",
"DESCRIPTION": "Вы уверены, что хотите удалить выбранные FAQ? Это действие невозможно отменить.",
"CONFIRM": "Да, удалить всё",
"SUCCESS_MESSAGE": "FAQ успешно удалены",
"ERROR_MESSAGE": "Произошла ошибка при удалении FAQ, пожалуйста, попробуйте ещё раз."
},
"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": "Please provide a valid answer."
"ERROR": "Пожалуйста, введите корректный ответ."
},
"ASSISTANT": {
"LABEL": "Помощник",
"PLACEHOLDER": "Select an assistant",
"ERROR": "Please select an assistant."
"LABEL": "Ассистент",
"PLACEHOLDER": "Выбрать ассистента",
"ERROR": "Выбрать ассистента"
}
},
"EDIT": {
"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"
"TITLE": "Обновить FAQ",
"SUCCESS_MESSAGE": "FAQ успешно обновлён",
"ERROR_MESSAGE": "Произошла ошибка при обновлении FAQ, пожалуйста, попробуйте ещё раз",
"APPROVE_SUCCESS_MESSAGE": "FAQ был отмечен как одобренный"
},
"OPTIONS": {
"APPROVE": "Mark as approved",
"EDIT_RESPONSE": "Edit FAQ",
"DELETE_RESPONSE": "Delete FAQ"
"APPROVE": "Отметить как одобренный",
"EDIT_RESPONSE": "FAQ отмечен как одобренный",
"DELETE_RESPONSE": "Удалить FAQ"
},
"EMPTY_STATE": {
"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."
"TITLE": "FAQ не найдены",
"SUBTITLE": "FAQ помогают вашему ассистенту быстро и точно отвечать на вопросы клиентов. Их можно генерировать автоматически из вашего контента или добавлять вручную."
}
},
"INBOXES": {
"HEADER": "Connected Inboxes",
"ADD_NEW": "Connect a new inbox",
"HEADER": "Подключённые источники входящих",
"ADD_NEW": "Подключить новый источник входящих",
"OPTIONS": {
"DISCONNECT": "Отключиться"
},
"DELETE": {
"TITLE": "Are you sure to disconnect the inbox?",
"TITLE": "Вы уверены, что хотите отключить этот источник входящих?",
"DESCRIPTION": "",
"CONFIRM": "Да, удалить",
"SUCCESS_MESSAGE": "The inbox was successfully disconnected.",
"ERROR_MESSAGE": "There was an error disconnecting the inbox, please try again."
"SUCCESS_MESSAGE": "Источник входящих успешно отключён.",
"ERROR_MESSAGE": "Произошла ошибка при отключении источника входящих, пожалуйста, попробуйте ещё раз."
},
"FORM_DESCRIPTION": "Choose an inbox to connect with the assistant.",
"FORM_DESCRIPTION": "Выберите источник входящих для подключения к ассистенту.",
"CREATE": {
"TITLE": "Connect an Inbox",
"SUCCESS_MESSAGE": "The inbox was successfully connected.",
"ERROR_MESSAGE": "An error occurred while connecting the inbox. Please try again."
"TITLE": "Подключить источник входящих",
"SUCCESS_MESSAGE": "Источник входящих успешно подключён.",
"ERROR_MESSAGE": "Произошла ошибка при подключении источника входящих. Пожалуйста, попробуйте ещё раз."
},
"FORM": {
"INBOX": {
"LABEL": "Электронная почта",
"PLACEHOLDER": "Choose the inbox to deploy the assistant.",
"ERROR": "An inbox selection is required."
"PLACEHOLDER": "Выберите источник входящих для развертывания ассистента.",
"ERROR": "Необходимо выбрать источник входящих."
}
},
"EMPTY_STATE": {
"TITLE": "No Connected Inboxes",
"SUBTITLE": "Connecting an inbox allows the assistant to handle initial questions from your customers before transferring them to you."
"TITLE": "Нет подключённых источников входящих",
"SUBTITLE": "Подключение источника входящих позволяет ассистенту обрабатывать первые вопросы ваших клиентов до их передачи вам."
}
}
}
@@ -124,7 +124,7 @@
},
"AGENT_REPORTS": {
"HEADER": "Обзор агентов",
"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.",
"DESCRIPTION": "Легко отслеживайте эффективность работы агентов с помощью ключевых метрик, таких как количество бесед, время ответа, время решения проблем и количество решённых случаев. Нажмите на имя агента, чтобы узнать подробности.",
"LOADING_CHART": "Загрузка данных графика...",
"NO_ENOUGH_DATA": "Недостаточно данных для создания отчета, пожалуйста, повторите попытку позже.",
"DOWNLOAD_AGENT_REPORTS": "Сказать отчёт по агентам",
@@ -259,7 +259,7 @@
},
"INBOX_REPORTS": {
"HEADER": "Обзор входящих",
"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.",
"DESCRIPTION": "Быстро просматривайте эффективность источника входящих с основными метриками — количеством бесед, временем ответа, временем решения проблем и количеством решённых случаев — всё в одном месте. Нажмите на имя источника для получения подробной информации.",
"LOADING_CHART": "Загрузка данных графика...",
"NO_ENOUGH_DATA": "Недостаточно данных для создания отчета, пожалуйста, повторите попытку позже.",
"DOWNLOAD_INBOX_REPORTS": "Скачать отчет по входящим",
@@ -327,7 +327,7 @@
},
"TEAM_REPORTS": {
"HEADER": "Обзор команды",
"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.",
"DESCRIPTION": "Получите обзор работы вашей команды с основными метриками, такими как количество бесед, время ответа, время решения проблем и количество решённых случаев. Нажмите на имя команды для подробностей.",
"LOADING_CHART": "Загрузка данных графика...",
"NO_ENOUGH_DATA": "Недостаточно данных для создания отчета, пожалуйста, повторите попытку позже.",
"DOWNLOAD_TEAM_REPORTS": "Скачать отчет по команде",
@@ -546,9 +546,9 @@
"INBOX": "Электронная почта",
"AGENT": "Оператор",
"TEAM": "Команда",
"AVG_RESOLUTION_TIME": "Avg. Resolution Time",
"AVG_FIRST_RESPONSE_TIME": "Avg. First Response Time",
"AVG_REPLY_TIME": "Avg. Customer Waiting Time",
"AVG_RESOLUTION_TIME": "Среднее время решения",
"AVG_FIRST_RESPONSE_TIME": "Среднее время первого ответа",
"AVG_REPLY_TIME": "Среднее время ожидания клиента",
"RESOLUTION_COUNT": "Количество завершенных",
"CONVERSATIONS": "Количество диалогов"
}
@@ -11,8 +11,8 @@
"CONVERSATIONS": "Диалоги",
"MESSAGES": "Сообщения"
},
"VIEW_MORE": "View more",
"LOAD_MORE": "Load more",
"VIEW_MORE": "Посмотреть больше",
"LOAD_MORE": "Загрузить ещё",
"SEARCHING_DATA": "Идёт поиск",
"LOADING_DATA": "Загрузка",
"EMPTY_STATE": "Не найдено {item} для запроса '{query}'",
@@ -36,20 +36,20 @@
}
},
"INTERFACE_SECTION": {
"TITLE": "Interface",
"NOTE": "Customize the look and feel of your Chatwoot dashboard.",
"TITLE": "Интерфейс",
"NOTE": "Настройте внешний вид и оформление вашей панели Chatwoot.",
"FONT_SIZE": {
"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",
"TITLE": "Размер шрифта",
"NOTE": "Измените размер текста на всей панели в соответствии с вашими предпочтениями.",
"UPDATE_SUCCESS": "Настройки шрифта успешно обновлены",
"UPDATE_ERROR": "Произошла ошибка при обновлении настроек шрифта, пожалуйста, попробуйте ещё раз",
"OPTIONS": {
"SMALLER": "Smaller",
"SMALL": "Small",
"SMALLER": "Меньше",
"SMALL": "Маленький",
"DEFAULT": "По умолчанию",
"LARGE": "Large",
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
"LARGE": "Большой",
"LARGER": "Крупнее",
"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": "FAQs",
"CAPTAIN_RESPONSES": "FAQ",
"HOME": "Главная",
"AGENTS": "Операторы",
"AGENT_BOTS": "Боты",
@@ -345,14 +345,14 @@
},
"BILLING_SETTINGS": {
"TITLE": "Платёж",
"DESCRIPTION": "Manage your subscription here, upgrade your plan and get more for your team.",
"DESCRIPTION": "Управляйте подпиской здесь, обновите тарифный план и получите больше возможностей для вашей команды.",
"CURRENT_PLAN": {
"TITLE": "Текущий план",
"PLAN_NOTE": "На данный момент вы подписаны на ** план{plan}** с **{quantity}** лицензиями",
"SEAT_COUNT": "Number of seats",
"RENEWS_ON": "Renews on"
"SEAT_COUNT": "Количество мест",
"RENEWS_ON": "Продление"
},
"VIEW_PRICING": "View Pricing",
"VIEW_PRICING": "Посмотреть цены",
"MANAGE_SUBSCRIPTION": {
"TITLE": "Управление подпиской",
"DESCRIPTION": "Просматривайте ваши предыдущие счета, редактируйте платежные реквизиты или отмените подписку.",
@@ -360,11 +360,11 @@
},
"CAPTAIN": {
"TITLE": "Капитан",
"DESCRIPTION": "Manage usage and credits for Captain AI.",
"BUTTON_TXT": "Buy more credits",
"DESCRIPTION": "Управляйте использованием и кредитами для Captain AI.",
"BUTTON_TXT": "Купить дополнительные кредиты",
"DOCUMENTS": "Документы",
"RESPONSES": "Responses",
"UPGRADE": "Captain is not available on the free plan, upgrade now to get access to assistants, copilot and more."
"RESPONSES": "Ответы",
"UPGRADE": "Captain недоступен в бесплатном тарифном плане, обновите его, чтобы получить доступ к ассистентам, copilot и другим функциям."
},
"CHAT_WITH_US": {
"TITLE": "Нужна помощь?",
@@ -3,7 +3,6 @@ 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';
@@ -20,6 +19,10 @@ 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 {
@@ -121,8 +121,6 @@ export default {
this.isALineChannel ||
this.isAPIInbox ||
(this.isAnEmailChannel && !this.inbox.provider) ||
this.isAMicrosoftInbox ||
this.isAGoogleInbox ||
this.isAWhatsAppChannel ||
this.isAWebWidgetInbox
) {
@@ -39,13 +39,14 @@ export default {
},
mixins: [globalConfigMixin],
setup() {
const { isEditorHotKeyEnabled } = useUISettings();
const { isEditorHotKeyEnabled, updateUISettings } = useUISettings();
const { currentFontSize, updateFontSize } = useFontSize();
return {
currentFontSize,
updateFontSize,
isEditorHotKeyEnabled,
updateUISettings,
};
},
data() {
@@ -2,8 +2,15 @@ class Inboxes::FetchImapEmailInboxesJob < ApplicationJob
queue_as :scheduled_jobs
def perform
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
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)
end
end
private
def should_fetch_emails?(inbox)
inbox.channel.imap_enabled && !inbox.account.suspended?
end
end
+18 -3
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)
send_to_bandwidth(body, message)
end
def send_text_message(contact_number, message_content)
@@ -56,7 +56,7 @@ class Channel::Sms < ApplicationRecord
}
end
def send_to_bandwidth(body)
def send_to_bandwidth(body, message = nil)
response = HTTParty.post(
"#{api_base_path}/users/#{provider_config['account_id']}/messages",
basic_auth: bandwidth_auth,
@@ -64,7 +64,22 @@ class Channel::Sms < ApplicationRecord
body: body.to_json
)
response.success? ? response.parsed_response['id'] : nil
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!
end
def bandwidth_auth
@@ -20,15 +20,30 @@ class Facebook::SendOnFacebookService < Base::SendOnChannelService
end
def send_message_to_facebook(delivery_params)
result = Facebook::Messenger::Bot.deliver(delivery_params, page_id: channel.page_id)
parsed_result = JSON.parse(result)
parsed_result = deliver_message(delivery_params)
return if parsed_result.nil?
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} : #{result}"
Rails.logger.info "Facebook::SendOnFacebookService: Error sending message to Facebook : Page - #{channel.page_id} : #{parsed_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,22 +70,28 @@ class Instagram::SendOnInstagramService < Base::SendOnChannelService
query: query
)
if response[:error].present?
Rails.logger.error("Instagram response: #{response['error']} : #{message_content}")
message.status = :failed
message.external_error = external_error(response)
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
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[:error][:message]
error_code = response[:error][:code]
error_message = response.dig('error', 'message')
error_code = response.dig('error', 'code')
"#{error_code} - #{error_message}"
end
@@ -27,6 +27,33 @@ 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,5 +1,6 @@
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'
@@ -78,6 +79,7 @@ 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,
@@ -91,13 +93,9 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
process_response(response)
end
def process_response(response)
if response.success?
response['messages'].first['id']
else
Rails.logger.error response.body
nil
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')
end
def template_body_parameters(template_info)
@@ -1,5 +1,7 @@
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'
@@ -111,13 +113,9 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
process_response(response)
end
def process_response(response)
if response.success?
response['messages'].first['id']
else
Rails.logger.error response.body
nil
end
def error_message(response)
# https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes/#sample-response
response.parsed_response&.dig('error', 'message')
end
def template_body_parameters(template_info)
@@ -1,3 +1,7 @@
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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@
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,6 +68,8 @@ 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,6 +68,8 @@ 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
+3 -1
View File
@@ -68,6 +68,8 @@ 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
@@ -133,7 +135,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 apagada
deleted: Esta mensagem foi excluída
delivery_status:
error_code: 'Código de erro: %{error_code}'
activity:
+2
View File
@@ -68,6 +68,8 @@ 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
+5 -3
View File
@@ -68,6 +68,8 @@ 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
@@ -165,7 +167,7 @@ ru:
removed: '%{user_name} удалил политику SLA %{sla_name}'
muted: '%{user_name} заглушил(а) этот разговор'
unmuted: '%{user_name} включил(а) уведомления для разговора'
auto_resolution_message: 'Resolving the conversation as it has been inactive for a while. Please start a new conversation if you need further assistance.'
auto_resolution_message: 'Разговор закрывается, поскольку он был неактивен в течение длительного времени. Пожалуйста, начните новый разговор, если потребуется дополнительная помощь.'
templates:
greeting_message_body: '%{account_name} как правило отвечает в течении несколько часов.'
ways_to_reach_you_message_body: 'Оставьте ваш email для связи'
@@ -216,8 +218,8 @@ ru:
name: 'Linear'
description: 'Создавайте или прикрепляйте уже существующие задачи в Linear непосредственно из окна диалога для более упорядоченного и эффективного процесса отслеживания проблем.'
captain:
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.'
copilot_error: 'Пожалуйста, подключите ассистента к этому источнику входящих для использования Copilot'
copilot_limit: 'У вас закончились кредиты для Copilot. Вы можете купить дополнительные кредиты в разделе биллинга.'
public_portal:
search:
search_placeholder: Поиск статьи по названию или содержанию...
+2
View File
@@ -68,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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,6 +68,8 @@ 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
+2
View File
@@ -68,6 +68,8 @@ th:
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,6 +68,8 @@ tl:
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,6 +68,8 @@ tr:
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: Raporlama aralığı %{since}'dan %{until}'a
utc_warning: Oluşturulan rapor UTC zaman dilimindedir.

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