Compare commits

..
Author SHA1 Message Date
Sojan a954e1eaca Merge branch 'release/4.1.0'
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Waiting to run
Publish Chatwoot EE docker images / merge (push) Blocked by required conditions
Publish Chatwoot CE docker images / merge (push) Blocked by required conditions
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Waiting to run
2025-04-16 23:10:46 -07:00
Sojan bc5f1722e1 Merge branch 'release/4.0.4'
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Waiting to run
Publish Chatwoot EE docker images / merge (push) Blocked by required conditions
Publish Chatwoot CE docker images / merge (push) Blocked by required conditions
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Waiting to run
2025-03-21 18:55:06 -07:00
Sojan 8f39e62570 Merge branch 'release/4.0.3'
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Waiting to run
Publish Chatwoot EE docker images / merge (push) Blocked by required conditions
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Waiting to run
Publish Chatwoot CE docker images / merge (push) Blocked by required conditions
2025-02-27 21:05:35 -08:00
Sojan 7520ca7a99 Merge branch 'release/4.0.2'
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Waiting to run
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Chatwoot EE docker images / merge (push) Blocked by required conditions
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Waiting to run
Publish Chatwoot CE docker images / merge (push) Blocked by required conditions
2025-02-21 17:04:31 -08:00
Sojan 35f4e63605 Merge branch 'release/4.0.1'
Publish Chatwoot CE docker images / build (push) Waiting to run
2025-01-17 01:13:27 +05:30
Sojan 5773089865 Merge branch 'release/4.0.0'
Publish Chatwoot CE docker images / build (push) Waiting to run
2025-01-16 17:11:45 +05:30
Sojan d01c7d3fa7 Merge branch 'release/3.16.0'
Publish Chatwoot CE docker images / build (push) Waiting to run
2024-12-17 23:03:24 +05:30
Sojan 959d2c0d8c Merge branch 'release/3.15.0'
Publish Chatwoot CE docker images / build (push) Waiting to run
2024-11-19 18:02:07 +08:00
Vishnu Narayanan 622f29a0b7 Merge branch 'hotfix/3.14.1'
Publish Chatwoot CE docker images / build (push) Waiting to run
2024-10-22 16:03:47 +05:30
783 changed files with 3331 additions and 19157 deletions
+3 -3
View File
@@ -4,8 +4,8 @@
# lint js and vue files # lint js and vue files
npx --no-install lint-staged npx --no-install lint-staged
# lint only staged ruby files that still exist (not deleted) # lint only staged ruby files
git diff --name-only --cached | xargs -I {} sh -c 'test -f "{}" && echo "{}"' | grep '\.rb$' | xargs -I {} bundle exec rubocop --force-exclusion -a "{}" || true git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion -a
# stage rubocop changes to files # stage rubocop changes to files
git diff --name-only --cached | xargs -I {} sh -c 'test -f "{}" && git add "{}"' || true git diff --name-only --cached | xargs git add
+5 -5
View File
@@ -485,7 +485,7 @@ GEM
uri uri
net-http-persistent (4.0.2) net-http-persistent (4.0.2)
connection_pool (~> 2.2) connection_pool (~> 2.2)
net-imap (0.4.20) net-imap (0.4.19)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
@@ -501,14 +501,14 @@ GEM
newrelic_rpm (9.6.0) newrelic_rpm (9.6.0)
base64 base64
nio4r (2.7.3) nio4r (2.7.3)
nokogiri (1.18.8) nokogiri (1.18.4)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-arm64-darwin) nokogiri (1.18.4-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-x86_64-darwin) nokogiri (1.18.4-x86_64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-x86_64-linux-gnu) nokogiri (1.18.4-x86_64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
oauth (1.1.0) oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1) oauth-tty (~> 1.0, >= 1.0.1)
@@ -9,7 +9,7 @@ class Campaigns::CampaignConversationBuilder
@contact_inbox.lock! @contact_inbox.lock!
# We won't send campaigns if a conversation is already present # We won't send campaigns if a conversation is already present
raise 'Conversation already present' if @contact_inbox.reload.conversations.present? raise 'Conversation alread present' if @contact_inbox.reload.conversations.present?
@conversation = ::Conversation.create!(conversation_params) @conversation = ::Conversation.create!(conversation_params)
Messages::MessageBuilder.new(@campaign.sender, @conversation, message_params).perform Messages::MessageBuilder.new(@campaign.sender, @conversation, message_params).perform
@@ -163,16 +163,9 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
@contact.custom_attributes @contact.custom_attributes
end end
def contact_additional_attributes
return @contact.additional_attributes.merge(permitted_params[:additional_attributes]) if permitted_params[:additional_attributes]
@contact.additional_attributes
end
def contact_update_params def contact_update_params
permitted_params.except(:custom_attributes, :avatar_url) # we want the merged custom attributes not the original one
.merge({ custom_attributes: contact_custom_attributes }) permitted_params.except(:custom_attributes, :avatar_url).merge({ custom_attributes: contact_custom_attributes })
.merge({ additional_attributes: contact_additional_attributes })
end end
def set_include_contact_inboxes def set_include_contact_inboxes
@@ -1,6 +1,4 @@
class Api::V1::Accounts::Conversations::MessagesController < Api::V1::Accounts::Conversations::BaseController class Api::V1::Accounts::Conversations::MessagesController < Api::V1::Accounts::Conversations::BaseController
before_action :ensure_api_inbox, only: :update
def index def index
@messages = message_finder.perform @messages = message_finder.perform
end end
@@ -13,11 +11,6 @@ class Api::V1::Accounts::Conversations::MessagesController < Api::V1::Accounts::
render_could_not_create_error(e.message) render_could_not_create_error(e.message)
end end
def update
Messages::StatusUpdateService.new(message, permitted_params[:status], permitted_params[:external_error]).perform
@message = message
end
def destroy def destroy
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
message.update!(content: I18n.t('conversations.messages.deleted'), content_type: :text, content_attributes: { deleted: true }) message.update!(content: I18n.t('conversations.messages.deleted'), content_type: :text, content_attributes: { deleted: true })
@@ -28,9 +21,7 @@ class Api::V1::Accounts::Conversations::MessagesController < Api::V1::Accounts::
def retry def retry
return if message.blank? return if message.blank?
service = Messages::StatusUpdateService.new(message, 'sent') message.update!(status: :sent, content_attributes: {})
service.perform
message.update!(content_attributes: {})
::SendReplyJob.perform_later(message.id) ::SendReplyJob.perform_later(message.id)
rescue StandardError => e rescue StandardError => e
render_could_not_create_error(e.message) render_could_not_create_error(e.message)
@@ -65,16 +56,10 @@ class Api::V1::Accounts::Conversations::MessagesController < Api::V1::Accounts::
end end
def permitted_params def permitted_params
params.permit(:id, :target_language, :status, :external_error) params.permit(:id, :target_language)
end end
def already_translated_content_available? def already_translated_content_available?
message.translations.present? && message.translations[permitted_params[:target_language]].present? message.translations.present? && message.translations[permitted_params[:target_language]].present?
end end
# API inbox check
def ensure_api_inbox
# Only API inboxes can update messages
render json: { error: 'Message status update is only allowed for API inboxes' }, status: :forbidden unless @conversation.inbox.api?
end
end end
@@ -44,9 +44,8 @@ class Api::V1::AccountsController < Api::BaseController
end end
def update def update
@account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email)) @account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email, :auto_resolve_duration))
@account.custom_attributes.merge!(custom_attributes_params) @account.custom_attributes.merge!(custom_attributes_params)
@account.settings.merge!(settings_params)
@account.custom_attributes['onboarding_step'] = 'invite_team' if @account.custom_attributes['onboarding_step'] == 'account_update' @account.custom_attributes['onboarding_step'] = 'invite_team' if @account.custom_attributes['onboarding_step'] == 'account_update'
@account.save! @account.save!
end end
@@ -84,17 +83,13 @@ class Api::V1::AccountsController < Api::BaseController
end end
def account_params def account_params
params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :user_full_name) params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :auto_resolve_duration, :user_full_name)
end end
def custom_attributes_params def custom_attributes_params
params.permit(:industry, :company_size, :timezone) params.permit(:industry, :company_size, :timezone)
end end
def settings_params
params.permit(:auto_resolve_after, :auto_resolve_message)
end
def check_signup_enabled def check_signup_enabled
raise ActionController::RoutingError, 'Not Found' if GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false') == 'false' raise ActionController::RoutingError, 'Not Found' if GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false') == 'false'
end end
@@ -2,15 +2,10 @@ class Api::V1::Widget::CampaignsController < Api::V1::Widget::BaseController
skip_before_action :set_contact skip_before_action :set_contact
def index def index
account = @web_widget.inbox.account @campaigns = @web_widget
@campaigns = if account.feature_enabled?('campaigns') .inbox
@web_widget .campaigns
.inbox .where(enabled: true, account_id: @web_widget.inbox.account_id)
.campaigns .includes(:sender)
.where(enabled: true, account_id: account.id)
.includes(:sender)
else
[]
end
end end
end end
@@ -54,7 +54,7 @@ class Api::V2::AccountsController < Api::BaseController
end end
def account_params def account_params
params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :user_full_name) params.permit(:account_name, :email, :name, :password, :locale, :domain, :support_email, :auto_resolve_duration, :user_full_name)
end end
def check_signup_enabled def check_signup_enabled
@@ -66,5 +66,3 @@ class SuperAdmin::AccountsController < SuperAdmin::ApplicationController
# rubocop:enable Rails/I18nLocaleTexts # rubocop:enable Rails/I18nLocaleTexts
end end
end end
SuperAdmin::AccountsController.prepend_mod_with('SuperAdmin::AccountsController')
+6 -32
View File
@@ -9,17 +9,10 @@ class AccountDashboard < Administrate::BaseDashboard
# on pages throughout the dashboard. # on pages throughout the dashboard.
enterprise_attribute_types = if ChatwootApp.enterprise? enterprise_attribute_types = if ChatwootApp.enterprise?
attributes = { {
limits: AccountLimitsField limits: Enterprise::AccountLimitsField,
all_features: Enterprise::AccountFeaturesField
} }
# Only show manually managed features in Chatwoot Cloud deployment
attributes[:manually_managed_features] = ManuallyManagedFeaturesField if ChatwootApp.chatwoot_cloud?
# Add all_features last so it appears after manually_managed_features
attributes[:all_features] = AccountFeaturesField
attributes
else else
{} {}
end end
@@ -53,14 +46,7 @@ class AccountDashboard < Administrate::BaseDashboard
# SHOW_PAGE_ATTRIBUTES # SHOW_PAGE_ATTRIBUTES
# an array of attributes that will be displayed on the model's show page. # an array of attributes that will be displayed on the model's show page.
enterprise_show_page_attributes = if ChatwootApp.enterprise? enterprise_show_page_attributes = ChatwootApp.enterprise? ? %i[custom_attributes limits all_features] : []
attrs = %i[custom_attributes limits]
attrs << :manually_managed_features if ChatwootApp.chatwoot_cloud?
attrs << :all_features
attrs
else
[]
end
SHOW_PAGE_ATTRIBUTES = (%i[ SHOW_PAGE_ATTRIBUTES = (%i[
id id
name name
@@ -75,14 +61,7 @@ class AccountDashboard < Administrate::BaseDashboard
# FORM_ATTRIBUTES # FORM_ATTRIBUTES
# an array of attributes that will be displayed # an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages. # on the model's form (`new` and `edit`) pages.
enterprise_form_attributes = if ChatwootApp.enterprise? enterprise_form_attributes = ChatwootApp.enterprise? ? %i[limits all_features] : []
attrs = %i[limits]
attrs << :manually_managed_features if ChatwootApp.chatwoot_cloud?
attrs << :all_features
attrs
else
[]
end
FORM_ATTRIBUTES = (%i[ FORM_ATTRIBUTES = (%i[
name name
locale locale
@@ -117,11 +96,6 @@ class AccountDashboard < Administrate::BaseDashboard
# to prevent an error from being raised (wrong number of arguments) # to prevent an error from being raised (wrong number of arguments)
# Reference: https://github.com/thoughtbot/administrate/pull/2356/files#diff-4e220b661b88f9a19ac527c50d6f1577ef6ab7b0bed2bfdf048e22e6bfa74a05R204 # Reference: https://github.com/thoughtbot/administrate/pull/2356/files#diff-4e220b661b88f9a19ac527c50d6f1577ef6ab7b0bed2bfdf048e22e6bfa74a05R204
def permitted_attributes(action) def permitted_attributes(action)
attrs = super + [limits: {}] super + [limits: {}]
# Add manually_managed_features to permitted attributes only for Chatwoot Cloud
attrs << { manually_managed_features: [] } if ChatwootApp.chatwoot_cloud?
attrs
end end
end end
@@ -0,0 +1,7 @@
require 'administrate/field/base'
class Enterprise::AccountFeaturesField < Administrate::Field::Base
def to_s
data
end
end
@@ -1,6 +1,6 @@
require 'administrate/field/base' require 'administrate/field/base'
class AccountLimitsField < Administrate::Field::Base class Enterprise::AccountLimitsField < Administrate::Field::Base
def to_s def to_s
data.present? ? data.to_json : { agents: nil, inboxes: nil, captain_responses: nil, captain_documents: nil }.to_json data.present? ? data.to_json : { agents: nil, inboxes: nil, captain_responses: nil, captain_documents: nil }.to_json
end end
@@ -15,7 +15,7 @@ module SuperAdmin::AccountFeaturesHelper
end end
def self.filter_internal_features(features) def self.filter_internal_features(features)
return features if ChatwootApp.chatwoot_cloud? return features if GlobalConfig.get_value('DEPLOYMENT_ENV') == 'cloud'
internal_features = account_features.select { |f| f['chatwoot_internal'] }.pluck('name') internal_features = account_features.select { |f| f['chatwoot_internal'] }.pluck('name')
features.except(*internal_features) features.except(*internal_features)
@@ -14,13 +14,6 @@ class CaptainAssistant extends ApiClient {
}, },
}); });
} }
playground({ assistantId, messageContent, messageHistory }) {
return axios.post(`${this.url}/${assistantId}/playground`, {
message_content: messageContent,
message_history: messageHistory,
});
}
} }
export default new CaptainAssistant(); export default new CaptainAssistant();
@@ -1,39 +0,0 @@
<script setup>
import { ref, watch } from 'vue';
const props = defineProps({
title: { type: String, required: true },
isOpen: { type: Boolean, default: false },
});
const isExpanded = ref(props.isOpen);
const toggleAccordion = () => {
isExpanded.value = !isExpanded.value;
};
watch(
() => props.isOpen,
newValue => {
isExpanded.value = newValue;
}
);
</script>
<template>
<div class="border rounded-lg border-n-slate-4">
<button
class="flex items-center justify-between w-full p-4 text-left"
@click="toggleAccordion"
>
<span class="text-sm font-medium text-n-slate-12">{{ title }}</span>
<span
class="w-5 h-5 transition-transform duration-200 i-lucide-chevron-down"
:class="{ 'rotate-180': isExpanded }"
/>
</button>
<div v-if="isExpanded" class="p-4 pt-0">
<slot />
</div>
</div>
</template>
@@ -87,10 +87,8 @@ useKeyboardEvents(keyboardEvents);
<ContactNoteItem <ContactNoteItem
v-for="note in notes" v-for="note in notes"
:key="note.id" :key="note.id"
class="mx-6 py-4"
:note="note" :note="note"
:written-by="getWrittenBy(note)" :written-by="getWrittenBy(note)"
allow-delete
@delete="onDelete" @delete="onDelete"
/> />
</div> </div>
@@ -1,8 +1,6 @@
<script setup> <script setup>
import { useTemplateRef, onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { dynamicTime } from 'shared/helpers/timeHelper'; import { dynamicTime } from 'shared/helpers/timeHelper';
import { useToggle } from '@vueuse/core';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter'; import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import Avatar from 'dashboard/components-next/avatar/Avatar.vue'; import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
import Button from 'dashboard/components-next/button/Button.vue'; import Button from 'dashboard/components-next/button/Button.vue';
@@ -16,63 +14,39 @@ const props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
allowDelete: {
type: Boolean,
default: false,
},
collapsible: {
type: Boolean,
default: false,
},
}); });
const emit = defineEmits(['delete']); const emit = defineEmits(['delete']);
const noteContentRef = useTemplateRef('noteContentRef');
const needsCollapse = ref(false);
const [isExpanded, toggleExpanded] = useToggle();
const { t } = useI18n(); const { t } = useI18n();
const { formatMessage } = useMessageFormatter(); const { formatMessage } = useMessageFormatter();
const handleDelete = () => { const handleDelete = () => {
emit('delete', props.note.id); emit('delete', props.note.id);
}; };
onMounted(() => {
if (props.collapsible) {
// Check if content height exceeds approximately 4 lines
// Assuming line height is ~1.625 and font size is ~14px
const threshold = 14 * 1.625 * 4; // ~84px
needsCollapse.value = noteContentRef.value?.clientHeight > threshold;
}
});
</script> </script>
<template> <template>
<div class="flex flex-col gap-2 border-b border-n-strong group/note"> <div
<div class="flex items-center justify-between gap-2"> class="flex flex-col gap-2 py-2 mx-6 border-b border-n-strong group/note"
<div class="flex items-center gap-1.5 min-w-0"> >
<div class="flex items-center justify-between">
<div class="flex items-center gap-1.5 py-2.5 min-w-0">
<Avatar <Avatar
:name="note?.user?.name || 'Bot'" :name="note?.user?.name || 'Bot'"
:src=" :src="note?.user?.thumbnail || '/assets/images/chatwoot_bot.png'"
note?.user?.name
? note?.user?.thumbnail
: '/assets/images/chatwoot_bot.png'
"
:size="16" :size="16"
rounded-full rounded-full
/> />
<div class="min-w-0 truncate"> <div class="min-w-0 truncate">
<span class="inline-flex items-center gap-1 text-sm text-n-slate-11"> <span class="inline-flex items-center gap-1 text-sm text-n-slate-11">
<span class="font-medium text-n-slate-12">{{ writtenBy }}</span> <span class="font-medium">{{ writtenBy }}</span>
{{ t('CONTACTS_LAYOUT.SIDEBAR.NOTES.WROTE') }} {{ t('CONTACTS_LAYOUT.SIDEBAR.NOTES.WROTE') }}
<span class="font-medium text-n-slate-12"> <span class="font-medium">{{ dynamicTime(note.createdAt) }}</span>
{{ dynamicTime(note.createdAt) }}
</span>
</span> </span>
</div> </div>
</div> </div>
<Button <Button
v-if="allowDelete"
variant="faded" variant="faded"
color="ruby" color="ruby"
size="xs" size="xs"
@@ -82,28 +56,8 @@ onMounted(() => {
/> />
</div> </div>
<p <p
ref="noteContentRef"
v-dompurify-html="formatMessage(note.content || '')" v-dompurify-html="formatMessage(note.content || '')"
class="mb-0 prose-sm prose-p:text-sm prose-p:leading-relaxed prose-p:mb-1 prose-p:mt-0 prose-ul:mb-1 prose-ul:mt-0 text-n-slate-12" class="mb-0 prose-sm prose-p:mb-1 prose-p:mt-0 prose-ul:mb-1 prose-ul:mt-0 text-n-slate-12"
:class="{
'line-clamp-4': collapsible && !isExpanded && needsCollapse,
}"
/> />
<p v-if="collapsible && needsCollapse">
<Button
variant="faded"
color="blue"
size="xs"
:icon="isExpanded ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'"
@click="() => toggleExpanded()"
>
<template v-if="isExpanded">
{{ t('CONTACTS_LAYOUT.SIDEBAR.NOTES.COLLAPSE') }}
</template>
<template v-else>
{{ t('CONTACTS_LAYOUT.SIDEBAR.NOTES.EXPAND') }}
</template>
</Button>
</p>
</div> </div>
</template> </template>
@@ -7,8 +7,8 @@ import { useStore, useStoreGetters } from 'dashboard/composables/store';
import { uploadFile } from 'dashboard/helper/uploadHelper'; import { uploadFile } from 'dashboard/helper/uploadHelper';
import { checkFileSizeLimit } from 'shared/helpers/FileHelper'; import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
import { useVuelidate } from '@vuelidate/core'; import { useVuelidate } from '@vuelidate/core';
import { required, minLength, helpers } from '@vuelidate/validators'; import { required, minLength } from '@vuelidate/validators';
import { shouldBeUrl, isValidSlug } from 'shared/helpers/Validators'; import { shouldBeUrl } from 'shared/helpers/Validators';
import Button from 'dashboard/components-next/button/Button.vue'; import Button from 'dashboard/components-next/button/Button.vue';
import Input from 'dashboard/components-next/input/Input.vue'; import Input from 'dashboard/components-next/input/Input.vue';
@@ -61,16 +61,7 @@ const liveChatWidgets = computed(() => {
const rules = { const rules = {
name: { required, minLength: minLength(2) }, name: { required, minLength: minLength(2) },
slug: { slug: { required },
required: helpers.withMessage(
() => t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.ERROR'),
required
),
isValidSlug: helpers.withMessage(
() => t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.FORMAT_ERROR'),
isValidSlug
),
},
homePageLink: { shouldBeUrl }, homePageLink: { shouldBeUrl },
}; };
@@ -80,9 +71,9 @@ const nameError = computed(() =>
v$.value.name.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.ERROR') : '' v$.value.name.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.ERROR') : ''
); );
const slugError = computed(() => { const slugError = computed(() =>
return v$.value.slug.$errors[0]?.$message || ''; v$.value.slug.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.ERROR') : ''
}); );
const homePageLinkError = computed(() => const homePageLinkError = computed(() =>
v$.value.homePageLink.$error v$.value.homePageLink.$error
@@ -6,9 +6,8 @@ import { useAlert, useTrack } from 'dashboard/composables';
import { PORTALS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events'; import { PORTALS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
import { convertToCategorySlug } from 'dashboard/helper/commons.js'; import { convertToCategorySlug } from 'dashboard/helper/commons.js';
import { useVuelidate } from '@vuelidate/core'; import { useVuelidate } from '@vuelidate/core';
import { required, minLength, helpers } from '@vuelidate/validators'; import { required, minLength } from '@vuelidate/validators';
import { buildPortalURL } from 'dashboard/helper/portalHelper'; import { buildPortalURL } from 'dashboard/helper/portalHelper';
import { isValidSlug } from 'shared/helpers/Validators';
import Dialog from 'dashboard/components-next/dialog/Dialog.vue'; import Dialog from 'dashboard/components-next/dialog/Dialog.vue';
import Input from 'dashboard/components-next/input/Input.vue'; import Input from 'dashboard/components-next/input/Input.vue';
@@ -32,16 +31,7 @@ const state = reactive({
const rules = { const rules = {
name: { required, minLength: minLength(2) }, name: { required, minLength: minLength(2) },
slug: { slug: { required },
required: helpers.withMessage(
() => t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.ERROR'),
required
),
isValidSlug: helpers.withMessage(
() => t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.FORMAT_ERROR'),
isValidSlug
),
},
}; };
const v$ = useVuelidate(rules, state); const v$ = useVuelidate(rules, state);
@@ -50,9 +40,9 @@ const nameError = computed(() =>
v$.value.name.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.ERROR') : '' v$.value.name.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.ERROR') : ''
); );
const slugError = computed(() => { const slugError = computed(() =>
return v$.value.slug.$errors[0]?.$message || ''; v$.value.slug.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.ERROR') : ''
}); );
const isSubmitDisabled = computed(() => v$.value.$invalid); const isSubmitDisabled = computed(() => v$.value.$invalid);
@@ -141,7 +131,6 @@ defineExpose({ dialogRef });
:message=" :message="
nameError || t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.MESSAGE') nameError || t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.MESSAGE')
" "
@blur="v$.name.$touch()"
/> />
<Input <Input
id="portal-slug" id="portal-slug"
@@ -151,8 +140,6 @@ defineExpose({ dialogRef });
:label="t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.LABEL')" :label="t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.LABEL')"
:message-type="slugError ? 'error' : 'info'" :message-type="slugError ? 'error' : 'info'"
:message="slugError || buildPortalURL(state.slug)" :message="slugError || buildPortalURL(state.slug)"
@input="v$.slug.$touch()"
@blur="v$.slug.$touch()"
/> />
</div> </div>
</Dialog> </Dialog>
@@ -2,7 +2,6 @@
import { computed } from 'vue'; import { computed } from 'vue';
import { usePolicy } from 'dashboard/composables/usePolicy'; import { usePolicy } from 'dashboard/composables/usePolicy';
import Button from 'dashboard/components-next/button/Button.vue'; import Button from 'dashboard/components-next/button/Button.vue';
import BackButton from 'dashboard/components/widgets/BackButton.vue';
import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue'; import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue';
import Spinner from 'dashboard/components-next/spinner/Spinner.vue'; import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
import Policy from 'dashboard/components/policy.vue'; import Policy from 'dashboard/components/policy.vue';
@@ -24,10 +23,6 @@ const props = defineProps({
type: String, type: String,
default: '', default: '',
}, },
backUrl: {
type: [String, Object],
default: '',
},
buttonPolicy: { buttonPolicy: {
type: Array, type: Array,
default: () => [], default: () => [],
@@ -44,10 +39,6 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
showKnowMore: {
type: Boolean,
default: true,
},
isEmpty: { isEmpty: {
type: Boolean, type: Boolean,
default: false, default: false,
@@ -82,23 +73,19 @@ const handlePageChange = event => {
class="flex items-start lg:items-center justify-between w-full py-6 lg:py-0 lg:h-20 gap-4 lg:gap-2 flex-col lg:flex-row" class="flex items-start lg:items-center justify-between w-full py-6 lg:py-0 lg:h-20 gap-4 lg:gap-2 flex-col lg:flex-row"
> >
<div class="flex gap-4 items-center"> <div class="flex gap-4 items-center">
<BackButton v-if="backUrl" :to="backUrl" />
<slot name="headerTitle"> <slot name="headerTitle">
<span class="text-xl font-medium text-n-slate-12"> <span class="text-xl font-medium text-n-slate-12">
{{ headerTitle }} {{ headerTitle }}
</span> </span>
</slot> </slot>
<div <div v-if="!isEmpty" class="flex items-center gap-2">
v-if="!isEmpty && showKnowMore"
class="flex items-center gap-2"
>
<div class="w-0.5 h-4 rounded-2xl bg-n-weak" /> <div class="w-0.5 h-4 rounded-2xl bg-n-weak" />
<slot name="knowMore" /> <slot name="knowMore" />
</div> </div>
</div> </div>
<div <div
v-if="!showPaywall && buttonLabel" v-if="!showPaywall"
v-on-clickaway="() => emit('close')" v-on-clickaway="() => emit('close')"
class="relative group/campaign-button" class="relative group/campaign-button"
> >
@@ -117,7 +104,7 @@ const handlePageChange = event => {
</div> </div>
</header> </header>
<main class="flex-1 px-6 overflow-y-auto xl:px-0"> <main class="flex-1 px-6 overflow-y-auto xl:px-0">
<div class="w-full max-w-[60rem] h-full mx-auto py-4"> <div class="w-full max-w-[60rem] mx-auto py-4">
<slot v-if="!showPaywall" name="controls" /> <slot v-if="!showPaywall" name="controls" />
<div <div
v-if="isFetching" v-if="isFetching"
@@ -76,12 +76,9 @@ const handleAction = ({ action, value }) => {
<template> <template>
<CardLayout> <CardLayout>
<div class="flex justify-between w-full gap-1"> <div class="flex justify-between w-full gap-1">
<router-link <span class="text-base text-n-slate-12 line-clamp-1">
:to="{ name: 'captain_assistants_edit', params: { assistantId: id } }"
class="text-base text-n-slate-12 line-clamp-1 hover:underline transition-colors"
>
{{ name }} {{ name }}
</router-link> </span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div <div
v-on-clickaway="() => toggleDropdown(false)" v-on-clickaway="() => toggleDropdown(false)"
@@ -1,111 +0,0 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import NextButton from 'dashboard/components-next/button/Button.vue';
import MessageList from './MessageList.vue';
import CaptainAssistant from 'dashboard/api/captain/assistant';
const { assistantId } = defineProps({
assistantId: {
type: Number,
required: true,
},
});
const { t } = useI18n();
const messages = ref([]);
const newMessage = ref('');
const isLoading = ref(false);
const formatMessagesForApi = () => {
return messages.value.map(message => ({
role: message.sender,
content: message.content,
}));
};
const resetConversation = () => {
messages.value = [];
newMessage.value = '';
};
const sendMessage = async () => {
if (!newMessage.value.trim() || isLoading.value) return;
const userMessage = {
content: newMessage.value,
sender: 'user',
timestamp: new Date().toISOString(),
};
messages.value.push(userMessage);
const currentMessage = newMessage.value;
newMessage.value = '';
try {
isLoading.value = true;
const { data } = await CaptainAssistant.playground({
assistantId,
messageContent: currentMessage,
messageHistory: formatMessagesForApi(),
});
messages.value.push({
content: data.response,
sender: 'assistant',
timestamp: new Date().toISOString(),
});
} catch (error) {
// eslint-disable-next-line no-console
console.error('Error getting assistant response:', error);
} finally {
isLoading.value = false;
}
};
</script>
<template>
<div
class="flex flex-col h-full rounded-lg p-4 border border-n-slate-4 text-n-slate-11"
>
<div class="mb-4">
<div class="flex justify-between items-center mb-1">
<h3 class="text-lg font-medium">
{{ t('CAPTAIN.PLAYGROUND.HEADER') }}
</h3>
<NextButton
ghost
size="small"
icon="i-lucide-rotate-ccw"
@click="resetConversation"
/>
</div>
<p class="text-sm text-n-slate-11">
{{ t('CAPTAIN.PLAYGROUND.DESCRIPTION') }}
</p>
</div>
<MessageList :messages="messages" :is-loading="isLoading" />
<div
class="flex items-center bg-n-solid-1 outline outline-n-container rounded-lg p-3"
>
<input
v-model="newMessage"
class="flex-1 bg-transparent border-none focus:outline-none text-sm mb-0"
:placeholder="t('CAPTAIN.PLAYGROUND.MESSAGE_PLACEHOLDER')"
@keyup.enter="sendMessage"
/>
<NextButton
ghost
size="small"
:disabled="!newMessage.trim()"
icon="i-lucide-send"
@click="sendMessage"
/>
</div>
<p class="text-xs text-n-slate-11 pt-2 text-center">
{{ t('CAPTAIN.PLAYGROUND.CREDIT_NOTE') }}
</p>
</div>
</template>
@@ -1,91 +0,0 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { ref, watch, nextTick } from 'vue';
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
const props = defineProps({
messages: {
type: Array,
required: true,
},
isLoading: {
type: Boolean,
default: false,
},
});
const messageContainer = ref(null);
const { t } = useI18n();
const { formatMessage } = useMessageFormatter();
const isUserMessage = sender => sender === 'user';
const getMessageAlignment = sender =>
isUserMessage(sender) ? 'justify-end' : 'justify-start';
const getMessageDirection = sender =>
isUserMessage(sender) ? 'flex-row-reverse' : 'flex-row';
const getAvatarName = sender =>
isUserMessage(sender)
? t('CAPTAIN.PLAYGROUND.USER')
: t('CAPTAIN.PLAYGROUND.ASSISTANT');
const getMessageStyle = sender =>
isUserMessage(sender)
? 'bg-n-strong text-n-white'
: 'bg-n-solid-iris text-n-slate-12';
const scrollToBottom = async () => {
await nextTick();
if (messageContainer.value) {
messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
}
};
watch(() => props.messages.length, scrollToBottom);
</script>
<template>
<div ref="messageContainer" class="flex-1 overflow-y-auto mb-4 space-y-2">
<div
v-for="(message, index) in messages"
:key="index"
class="flex"
:class="getMessageAlignment(message.sender)"
>
<div
class="flex items-start gap-1.5"
:class="getMessageDirection(message.sender)"
>
<Avatar :name="getAvatarName(message.sender)" rounded-full :size="24" />
<div
class="max-w-[80%] rounded-lg p-3 text-sm"
:class="getMessageStyle(message.sender)"
>
<div class="break-words" v-html="formatMessage(message.content)" />
</div>
</div>
</div>
<div v-if="isLoading" class="flex justify-start">
<div class="flex items-start gap-1.5">
<Avatar :name="getAvatarName('assistant')" rounded-full :size="24" />
<div
class="max-w-sm rounded-lg p-3 text-sm bg-n-solid-iris text-n-slate-12"
>
<div class="flex gap-1">
<div class="w-2 h-2 rounded-full bg-n-iris-10 animate-bounce" />
<div
class="w-2 h-2 rounded-full bg-n-iris-10 animate-bounce [animation-delay:0.2s]"
/>
<div
class="w-2 h-2 rounded-full bg-n-iris-10 animate-bounce [animation-delay:0.4s]"
/>
</div>
</div>
</div>
</div>
</div>
</template>
@@ -1,306 +0,0 @@
<script setup>
import { reactive, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useVuelidate } from '@vuelidate/core';
import { required, minLength } from '@vuelidate/validators';
import { useMapGetter } from 'dashboard/composables/store';
import Input from 'dashboard/components-next/input/Input.vue';
import Button from 'dashboard/components-next/button/Button.vue';
import Editor from 'dashboard/components-next/Editor/Editor.vue';
import Accordion from 'dashboard/components-next/Accordion/Accordion.vue';
const props = defineProps({
mode: {
type: String,
required: true,
validator: value => ['edit', 'create'].includes(value),
},
assistant: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(['submit']);
const { t } = useI18n();
const formState = {
uiFlags: useMapGetter('captainAssistants/getUIFlags'),
};
const initialState = {
name: '',
description: '',
productName: '',
welcomeMessage: '',
handoffMessage: '',
resolutionMessage: '',
instructions: '',
features: {
conversationFaqs: false,
memories: false,
},
};
const state = reactive({ ...initialState });
const validationRules = {
name: { required, minLength: minLength(1) },
description: { required, minLength: minLength(1) },
productName: { required, minLength: minLength(1) },
welcomeMessage: { minLength: minLength(1) },
handoffMessage: { minLength: minLength(1) },
resolutionMessage: { minLength: minLength(1) },
instructions: { minLength: minLength(1) },
};
const v$ = useVuelidate(validationRules, state);
const isLoading = computed(() => formState.uiFlags.value.creatingItem);
const getErrorMessage = field => {
return v$.value[field].$error ? v$.value[field].$errors[0].$message : '';
};
const formErrors = computed(() => ({
name: getErrorMessage('name'),
description: getErrorMessage('description'),
productName: getErrorMessage('productName'),
welcomeMessage: getErrorMessage('welcomeMessage'),
handoffMessage: getErrorMessage('handoffMessage'),
resolutionMessage: getErrorMessage('resolutionMessage'),
instructions: getErrorMessage('instructions'),
}));
const updateStateFromAssistant = assistant => {
const { config = {} } = assistant;
state.name = assistant.name;
state.description = assistant.description;
state.productName = config.product_name;
state.welcomeMessage = config.welcome_message;
state.handoffMessage = config.handoff_message;
state.resolutionMessage = config.resolution_message;
state.instructions = config.instructions;
state.features = {
conversationFaqs: config.feature_faq || false,
memories: config.feature_memory || false,
};
};
const handleBasicInfoUpdate = async () => {
const result = await Promise.all([
v$.value.name.$validate(),
v$.value.description.$validate(),
v$.value.productName.$validate(),
]).then(results => results.every(Boolean));
if (!result) return;
const payload = {
name: state.name,
description: state.description,
product_name: state.productName,
};
emit('submit', payload);
};
const handleSystemMessagesUpdate = async () => {
const result = await Promise.all([
v$.value.welcomeMessage.$validate(),
v$.value.handoffMessage.$validate(),
v$.value.resolutionMessage.$validate(),
]).then(results => results.every(Boolean));
if (!result) return;
const payload = {
config: {
...props.assistant.config,
welcome_message: state.welcomeMessage,
handoff_message: state.handoffMessage,
resolution_message: state.resolutionMessage,
},
};
emit('submit', payload);
};
const handleInstructionsUpdate = async () => {
const result = await v$.value.instructions.$validate();
if (!result) return;
const payload = {
config: {
...props.assistant.config,
instructions: state.instructions,
},
};
emit('submit', payload);
};
const handleFeaturesUpdate = () => {
const payload = {
config: {
...props.assistant.config,
feature_faq: state.features.conversationFaqs,
feature_memory: state.features.memories,
},
};
emit('submit', payload);
};
watch(
() => props.assistant,
newAssistant => {
if (props.mode === 'edit' && newAssistant) {
updateStateFromAssistant(newAssistant);
}
},
{ immediate: true }
);
</script>
<template>
<form class="flex flex-col gap-4" @submit.prevent="handleSubmit">
<!-- Basic Information Section -->
<Accordion
:title="t('CAPTAIN.ASSISTANTS.FORM.SECTIONS.BASIC_INFO')"
is-open
>
<div class="flex flex-col gap-4 pt-4">
<Input
v-model="state.name"
:label="t('CAPTAIN.ASSISTANTS.FORM.NAME.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.NAME.PLACEHOLDER')"
:message="formErrors.name"
:message-type="formErrors.name ? 'error' : 'info'"
/>
<Editor
v-model="state.description"
:label="t('CAPTAIN.ASSISTANTS.FORM.DESCRIPTION.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.DESCRIPTION.PLACEHOLDER')"
:message="formErrors.description"
:message-type="formErrors.description ? 'error' : 'info'"
/>
<Input
v-model="state.productName"
:label="t('CAPTAIN.ASSISTANTS.FORM.PRODUCT_NAME.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.PRODUCT_NAME.PLACEHOLDER')"
:message="formErrors.productName"
:message-type="formErrors.productName ? 'error' : 'info'"
/>
<div class="flex justify-end">
<Button
size="small"
:loading="isLoading"
@click="handleBasicInfoUpdate"
>
{{ t('CAPTAIN.ASSISTANTS.FORM.UPDATE') }}
</Button>
</div>
</div>
</Accordion>
<!-- Instructions Section -->
<Accordion :title="t('CAPTAIN.ASSISTANTS.FORM.SECTIONS.INSTRUCTIONS')">
<div class="flex flex-col gap-4 pt-4">
<Editor
v-model="state.instructions"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.INSTRUCTIONS.PLACEHOLDER')"
:message="formErrors.instructions"
:max-length="20000"
:message-type="formErrors.instructions ? 'error' : 'info'"
/>
<div class="flex justify-end">
<Button
size="small"
:loading="isLoading"
:label="t('CAPTAIN.ASSISTANTS.FORM.UPDATE')"
@click="handleInstructionsUpdate"
/>
</div>
</div>
</Accordion>
<!-- Greeting Messages Section -->
<Accordion :title="t('CAPTAIN.ASSISTANTS.FORM.SECTIONS.SYSTEM_MESSAGES')">
<div class="flex flex-col gap-4 pt-4">
<Editor
v-model="state.handoffMessage"
:label="t('CAPTAIN.ASSISTANTS.FORM.HANDOFF_MESSAGE.LABEL')"
:placeholder="
t('CAPTAIN.ASSISTANTS.FORM.HANDOFF_MESSAGE.PLACEHOLDER')
"
:message="formErrors.handoffMessage"
:message-type="formErrors.handoffMessage ? 'error' : 'info'"
/>
<Editor
v-model="state.resolutionMessage"
:label="t('CAPTAIN.ASSISTANTS.FORM.RESOLUTION_MESSAGE.LABEL')"
:placeholder="
t('CAPTAIN.ASSISTANTS.FORM.RESOLUTION_MESSAGE.PLACEHOLDER')
"
:message="formErrors.resolutionMessage"
:message-type="formErrors.resolutionMessage ? 'error' : 'info'"
/>
<div class="flex justify-end">
<Button
size="small"
:loading="isLoading"
:label="t('CAPTAIN.ASSISTANTS.FORM.UPDATE')"
@click="handleSystemMessagesUpdate"
/>
</div>
</div>
</Accordion>
<!-- Features Section -->
<Accordion :title="t('CAPTAIN.ASSISTANTS.FORM.SECTIONS.FEATURES')">
<div class="flex flex-col gap-4 pt-4">
<div class="flex flex-col gap-2">
<label class="text-sm font-medium text-n-slate-12">
{{ t('CAPTAIN.ASSISTANTS.FORM.FEATURES.TITLE') }}
</label>
<div class="flex flex-col gap-2">
<label class="flex items-center gap-2">
<input
v-model="state.features.conversationFaqs"
type="checkbox"
class="form-checkbox"
/>
{{
t('CAPTAIN.ASSISTANTS.FORM.FEATURES.ALLOW_CONVERSATION_FAQS')
}}
</label>
<label class="flex items-center gap-2">
<input
v-model="state.features.memories"
type="checkbox"
class="form-checkbox"
/>
{{ t('CAPTAIN.ASSISTANTS.FORM.FEATURES.ALLOW_MEMORIES') }}
</label>
</div>
</div>
<div class="flex justify-end">
<Button
size="small"
:loading="isLoading"
:label="t('CAPTAIN.ASSISTANTS.FORM.UPDATE')"
@click="handleFeaturesUpdate"
/>
</div>
</div>
</Accordion>
</form>
</template>
@@ -1,6 +1,5 @@
<script setup> <script setup>
import { nextTick, ref, watch } from 'vue'; import { nextTick, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useTrack } from 'dashboard/composables'; import { useTrack } from 'dashboard/composables';
import { COPILOT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events'; import { COPILOT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
@@ -40,8 +39,6 @@ const props = defineProps({
const emit = defineEmits(['sendMessage', 'reset', 'setAssistant']); const emit = defineEmits(['sendMessage', 'reset', 'setAssistant']);
const { t } = useI18n();
const COPILOT_USER_ROLES = ['assistant', 'system']; const COPILOT_USER_ROLES = ['assistant', 'system'];
const sendMessage = message => { const sendMessage = message => {
@@ -50,7 +47,7 @@ const sendMessage = message => {
}; };
const useSuggestion = opt => { const useSuggestion = opt => {
emit('sendMessage', t(opt.prompt)); emit('sendMessage', opt.prompt);
useTrack(COPILOT_EVENTS.SEND_SUGGESTED); useTrack(COPILOT_EVENTS.SEND_SUGGESTED);
}; };
@@ -69,16 +66,16 @@ const scrollToBottom = async () => {
const promptOptions = [ const promptOptions = [
{ {
label: 'CAPTAIN.COPILOT.PROMPTS.SUMMARIZE.LABEL', label: 'Summarize this conversation',
prompt: 'CAPTAIN.COPILOT.PROMPTS.SUMMARIZE.CONTENT', prompt: `Summarize the key points discussed between the customer and the support agent, including the customer's concerns, questions, and the solutions or responses provided by the support agent`,
}, },
{ {
label: 'CAPTAIN.COPILOT.PROMPTS.SUGGEST.LABEL', label: 'Suggest an answer',
prompt: 'CAPTAIN.COPILOT.PROMPTS.SUGGEST.CONTENT', prompt: `Analyze the customers inquiry, and draft a response that effectively addresses their concerns or questions. Ensure the reply is clear, concise, and provides helpful information.`,
}, },
{ {
label: 'CAPTAIN.COPILOT.PROMPTS.RATE.LABEL', label: 'Rate this conversation',
prompt: 'CAPTAIN.COPILOT.PROMPTS.RATE.CONTENT', prompt: `Review the conversation to see how well it meets the customers needs. Share a rating out of 5 based on tone, clarity, and effectiveness.`,
}, },
]; ];
@@ -92,7 +89,7 @@ watch(
</script> </script>
<template> <template>
<div class="flex flex-col h-full text-sm leading-6 tracking-tight w-full"> <div class="flex flex-col h-full text-sm leading-6 tracking-tight">
<div ref="chatContainer" class="flex-1 px-4 py-4 space-y-6 overflow-y-auto"> <div ref="chatContainer" class="flex-1 px-4 py-4 space-y-6 overflow-y-auto">
<template v-for="message in messages" :key="message.id"> <template v-for="message in messages" :key="message.id">
<CopilotAgentMessage <CopilotAgentMessage
@@ -124,7 +121,7 @@ watch(
class="px-2 py-1 rounded-md border border-n-weak bg-n-slate-2 text-n-slate-11 flex items-center gap-1" class="px-2 py-1 rounded-md border border-n-weak bg-n-slate-2 text-n-slate-11 flex items-center gap-1"
@click="() => useSuggestion(prompt)" @click="() => useSuggestion(prompt)"
> >
<span>{{ t(prompt.label) }}</span> <span>{{ prompt.label }}</span>
<Icon icon="i-lucide-chevron-right" /> <Icon icon="i-lucide-chevron-right" />
</button> </button>
</div> </div>
@@ -1,66 +0,0 @@
<script setup>
import { computed, ref } from 'vue';
import Input from './Input.vue';
import { useI18n } from 'vue-i18n';
const props = defineProps({
min: { type: Number, default: 0 },
max: { type: Number, default: Infinity },
disabled: { type: Boolean, default: false },
});
const { t } = useI18n();
const duration = defineModel('modelValue', { type: Number, default: null });
const UNIT_TYPES = {
MINUTES: 'minutes',
HOURS: 'hours',
DAYS: 'days',
};
const unit = ref(UNIT_TYPES.MINUTES);
const transformedValue = computed({
get() {
if (unit.value === UNIT_TYPES.MINUTES) return duration.value;
if (unit.value === UNIT_TYPES.HOURS) return Math.floor(duration.value / 60);
if (unit.value === UNIT_TYPES.DAYS)
return Math.floor(duration.value / 24 / 60);
return 0;
},
set(newValue) {
let minuteValue;
if (unit.value === UNIT_TYPES.MINUTES) {
minuteValue = Math.floor(newValue);
} else if (unit.value === UNIT_TYPES.HOURS) {
minuteValue = Math.floor(newValue * 60);
} else if (unit.value === UNIT_TYPES.DAYS) {
minuteValue = Math.floor(newValue * 24 * 60);
}
duration.value = Math.min(Math.max(minuteValue, props.min), props.max);
},
});
</script>
<template>
<Input
v-model="transformedValue"
type="number"
autocomplete="off"
:disabled="disabled"
:placeholder="t('DURATION_INPUT.PLACEHOLDER')"
class="flex-grow w-full disabled:"
/>
<select
v-model="unit"
:disabled="disabled"
class="mb-0 text-sm disabled:outline-n-weak disabled:opacity-40"
>
<option :value="UNIT_TYPES.MINUTES">
{{ t('DURATION_INPUT.MINUTES') }}
</option>
<option :value="UNIT_TYPES.HOURS">{{ t('DURATION_INPUT.HOURS') }}</option>
<option :value="UNIT_TYPES.DAYS">{{ t('DURATION_INPUT.DAYS') }}</option>
</select>
</template>
@@ -117,7 +117,7 @@ const props = defineProps({
}, },
conversationId: { type: Number, required: true }, conversationId: { type: Number, required: true },
createdAt: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties createdAt: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties
currentUserId: { type: Number, required: true }, // eslint-disable-line vue/no-unused-properties currentUserId: { type: Number, required: true },
groupWithNext: { type: Boolean, default: false }, groupWithNext: { type: Boolean, default: false },
inboxId: { type: Number, default: null }, // eslint-disable-line vue/no-unused-properties inboxId: { type: Number, default: null }, // eslint-disable-line vue/no-unused-properties
inboxSupportsReplyTo: { type: Object, default: () => ({}) }, inboxSupportsReplyTo: { type: Object, default: () => ({}) },
@@ -173,10 +173,7 @@ const variant = computed(() => {
return variants[props.messageType] || MESSAGE_VARIANTS.USER; return variants[props.messageType] || MESSAGE_VARIANTS.USER;
}); });
const isBotOrAgentMessage = computed(() => { const isMyMessage = computed(() => {
if (props.messageType === MESSAGE_TYPES.ACTIVITY) {
return false;
}
// if an outgoing message is still processing, then it's definitely a // if an outgoing message is still processing, then it's definitely a
// message sent by the current user // message sent by the current user
if ( if (
@@ -189,10 +186,13 @@ const isBotOrAgentMessage = computed(() => {
const senderType = props.senderType ?? props.sender?.type; const senderType = props.senderType ?? props.sender?.type;
if (!senderType || !senderId) { if (!senderType || !senderId) {
return true; return false;
} }
return senderType.toLowerCase() === SENDER_TYPES.USER.toLowerCase(); return (
senderType.toLowerCase() === SENDER_TYPES.USER.toLowerCase() &&
props.currentUserId === senderId
);
}); });
/** /**
@@ -200,7 +200,7 @@ const isBotOrAgentMessage = computed(() => {
* @returns {import('vue').ComputedRef<'left'|'right'|'center'>} The computed orientation * @returns {import('vue').ComputedRef<'left'|'right'|'center'>} The computed orientation
*/ */
const orientation = computed(() => { const orientation = computed(() => {
if (isBotOrAgentMessage.value) { if (isMyMessage.value) {
return ORIENTATION.RIGHT; return ORIENTATION.RIGHT;
} }
@@ -221,8 +221,8 @@ const flexOrientationClass = computed(() => {
const gridClass = computed(() => { const gridClass = computed(() => {
const map = { const map = {
[ORIENTATION.LEFT]: 'grid grid-cols-1fr', [ORIENTATION.LEFT]: 'grid grid-cols-[24px_1fr]',
[ORIENTATION.RIGHT]: 'grid grid-cols-[1fr_24px]', [ORIENTATION.RIGHT]: 'grid grid-cols-1fr',
}; };
return map[orientation.value]; return map[orientation.value];
@@ -231,12 +231,12 @@ const gridClass = computed(() => {
const gridTemplate = computed(() => { const gridTemplate = computed(() => {
const map = { const map = {
[ORIENTATION.LEFT]: ` [ORIENTATION.LEFT]: `
"bubble" "avatar bubble"
"meta" "spacer meta"
`, `,
[ORIENTATION.RIGHT]: ` [ORIENTATION.RIGHT]: `
"bubble avatar" "bubble"
"meta spacer" "meta"
`, `,
}; };
@@ -251,7 +251,7 @@ const shouldGroupWithNext = computed(() => {
const shouldShowAvatar = computed(() => { const shouldShowAvatar = computed(() => {
if (props.messageType === MESSAGE_TYPES.ACTIVITY) return false; if (props.messageType === MESSAGE_TYPES.ACTIVITY) return false;
if (orientation.value === ORIENTATION.LEFT) return false; if (orientation.value === ORIENTATION.RIGHT) return false;
return true; return true;
}); });
@@ -394,29 +394,23 @@ function handleReplyTo() {
} }
const avatarInfo = computed(() => { const avatarInfo = computed(() => {
// If no sender, return bot info if (!props.sender || props.sender.type === SENDER_TYPES.AGENT_BOT) {
if (!props.sender) {
return { return {
name: t('CONVERSATION.BOT'), name: t('CONVERSATION.BOT'),
src: '', src: '',
}; };
} }
const { sender } = props; if (props.sender) {
const { name, type, avatarUrl, thumbnail } = sender || {};
// If sender type is agent bot, use avatarUrl
if (type === SENDER_TYPES.AGENT_BOT) {
return { return {
name: name ?? '', name: props.sender.name,
src: avatarUrl ?? '', src: props.sender?.thumbnail,
}; };
} }
// For all other senders, use thumbnail
return { return {
name: name ?? '', name: '',
src: thumbnail ?? '', src: '',
}; };
}); });
@@ -444,7 +438,7 @@ provideMessageContext({
isPrivate: computed(() => props.private), isPrivate: computed(() => props.private),
variant, variant,
orientation, orientation,
isBotOrAgentMessage, isMyMessage,
shouldGroupWithNext, shouldGroupWithNext,
}); });
</script> </script>
@@ -476,14 +470,14 @@ provideMessageContext({
'w-full': variant === MESSAGE_VARIANTS.EMAIL, 'w-full': variant === MESSAGE_VARIANTS.EMAIL,
}, },
]" ]"
class="gap-x-2" class="gap-x-3"
:style="{ :style="{
gridTemplateAreas: gridTemplate, gridTemplateAreas: gridTemplate,
}" }"
> >
<div <div
v-if="!shouldGroupWithNext && shouldShowAvatar" v-if="!shouldGroupWithNext && shouldShowAvatar"
v-tooltip.left-end="avatarTooltip" v-tooltip.right-end="avatarTooltip"
class="[grid-area:avatar] flex items-end" class="[grid-area:avatar] flex items-end"
> >
<Avatar v-bind="avatarInfo" :size="24" /> <Avatar v-bind="avatarInfo" :size="24" />
@@ -491,8 +485,7 @@ provideMessageContext({
<div <div
class="[grid-area:bubble] flex" class="[grid-area:bubble] flex"
:class="{ :class="{
'ltr:pl-8 rtl:pr-8 justify-end': orientation === ORIENTATION.RIGHT, 'ltr:pl-9 rtl:pl-0 justify-end': orientation === ORIENTATION.RIGHT,
'ltr:pr-8 rtl:pl-8': orientation === ORIENTATION.LEFT,
'min-w-0': variant === MESSAGE_VARIANTS.EMAIL, 'min-w-0': variant === MESSAGE_VARIANTS.EMAIL,
}" }"
@contextmenu="openContextMenu($event)" @contextmenu="openContextMenu($event)"
@@ -19,7 +19,7 @@ const {
isAWebWidgetInbox, isAWebWidgetInbox,
isAWhatsAppChannel, isAWhatsAppChannel,
isAnEmailChannel, isAnEmailChannel,
isAnInstagramChannel, isAInstagramChannel,
} = useInbox(); } = useInbox();
const { const {
@@ -60,7 +60,7 @@ const isSent = computed(() => {
isAFacebookInbox.value || isAFacebookInbox.value ||
isASmsInbox.value || isASmsInbox.value ||
isATelegramChannel.value || isATelegramChannel.value ||
isAnInstagramChannel.value isAInstagramChannel.value
) { ) {
return sourceId.value && status.value === MESSAGE_STATUS.SENT; return sourceId.value && status.value === MESSAGE_STATUS.SENT;
} }
@@ -100,7 +100,7 @@ const isRead = computed(() => {
isAWhatsAppChannel.value || isAWhatsAppChannel.value ||
isATwilioChannel.value || isATwilioChannel.value ||
isAFacebookInbox.value || isAFacebookInbox.value ||
isAnInstagramChannel.value isAInstagramChannel.value
) { ) {
return sourceId.value && status.value === MESSAGE_STATUS.READ; return sourceId.value && status.value === MESSAGE_STATUS.READ;
} }
@@ -14,7 +14,7 @@ const readableTime = computed(() =>
<template> <template>
<BaseBubble <BaseBubble
v-tooltip.top="readableTime" v-tooltip.top="readableTime"
class="px-3 py-1 !rounded-xl flex min-w-0 items-center gap-2" class="px-2 py-0.5 !rounded-full flex min-w-0 items-center gap-2"
data-bubble-name="activity" data-bubble-name="activity"
> >
<span v-dompurify-html="content" :title="content" /> <span v-dompurify-html="content" :title="content" />
@@ -98,7 +98,7 @@ const MessageControl = Symbol('MessageControl');
* @property {import('vue').Ref<Sender|null>} [sender=null] - The sender information * @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<MessageOrientation>} orientation - The visual variant of the message
* @property {import('vue').ComputedRef<MessageVariant>} variant - The visual variant of the message * @property {import('vue').ComputedRef<MessageVariant>} variant - The visual variant of the message
* @property {import('vue').ComputedRef<boolean>} isBotOrAgentMessage - Does the message belong to the current user * @property {import('vue').ComputedRef<boolean>} isMyMessage - Does the message belong to the current user
* @property {import('vue').ComputedRef<boolean>} isPrivate - Proxy computed value for private * @property {import('vue').ComputedRef<boolean>} 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 * @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
*/ */
@@ -15,13 +15,6 @@ const props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
subMenuPosition: {
type: String,
default: 'right',
validator: value => {
return ['right', 'left', 'bottom'].includes(value);
},
},
}); });
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
@@ -51,21 +44,14 @@ const handleSelect = value => {
trailing-icon trailing-icon
color="slate" color="slate"
variant="faded" variant="faded"
class="!w-fit max-w-40" class="!w-fit"
:class="{ 'dark:!bg-n-alpha-2 !bg-n-slate-9/20': isOpen }" :class="{ 'dark:!bg-n-alpha-2 !bg-n-slate-9/20': isOpen }"
:label="labelValue" :label="labelValue"
@click="toggleMenu" @click="toggleMenu"
/> />
<div <div
v-if="isOpen" v-if="isOpen"
class="absolute select-none max-w-64 flex flex-col gap-1 bg-n-alpha-3 backdrop-blur-[100px] p-1 top-0 shadow-lg z-40 rounded-lg border border-n-weak dark:border-n-strong/50" class="absolute ltr:left-full rtl:right-full select-none max-w-48 ltr:ml-1 rtl:mr-1 flex flex-col gap-1 bg-n-alpha-3 backdrop-blur-[100px] p-1 top-0 shadow-lg rounded-lg border border-n-weak"
:class="{
'ltr:left-full rtl:right-full ltr:ml-1 rtl:mr-1':
subMenuPosition === 'right',
'ltr:right-full rtl:left-full ltr:mr-1 rtl:ml-1':
subMenuPosition === 'left',
'top-full mt-1 ltr:right-0 rtl:left-0': subMenuPosition === 'bottom',
}"
> >
<Button <Button
v-for="option in options" v-for="option in options"
@@ -27,10 +27,10 @@ const updateValue = () => {
> >
<span class="sr-only">{{ t('SWITCH.TOGGLE') }}</span> <span class="sr-only">{{ t('SWITCH.TOGGLE') }}</span>
<span <span
class="absolute top-0.5 left-0.5 h-3 w-3 transform rounded-full shadow-sm transition-transform duration-200 ease-in-out" class="absolute top-[0.07rem] left-0.5 h-3 w-3 transform rounded-full shadow-sm transition-transform duration-200 ease-in-out"
:class=" :class="
modelValue modelValue
? 'translate-x-3 bg-white' ? 'translate-x-2.5 bg-white'
: 'translate-x-0 bg-white dark:bg-n-black' : 'translate-x-0 bg-white dark:bg-n-black'
" "
/> />
@@ -202,7 +202,7 @@ export default {
if (this.isALineChannel) { if (this.isALineChannel) {
return ALLOWED_FILE_TYPES_FOR_LINE; return ALLOWED_FILE_TYPES_FOR_LINE;
} }
if (this.isAnInstagramChannel || this.isInstagramDM) { if (this.isAInstagramChannel || this.isInstagramDM) {
return ALLOWED_FILE_TYPES_FOR_INSTAGRAM; return ALLOWED_FILE_TYPES_FOR_INSTAGRAM;
} }
@@ -1,129 +1,93 @@
<script setup> <script>
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useToggle } from '@vueuse/core';
import { vOnClickOutside } from '@vueuse/components';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { useMapGetter, useStore } from 'dashboard/composables/store.js';
import wootConstants from 'dashboard/constants/globals'; import wootConstants from 'dashboard/constants/globals';
import SelectMenu from 'dashboard/components-next/selectmenu/SelectMenu.vue'; import { mapGetters } from 'vuex';
import FilterItem from './FilterItem.vue';
import { useUISettings } from 'dashboard/composables/useUISettings';
import NextButton from 'dashboard/components-next/button/Button.vue'; import NextButton from 'dashboard/components-next/button/Button.vue';
defineProps({ const CHAT_STATUS_FILTER_ITEMS = Object.freeze([
isOnExpandedLayout: { 'open',
type: Boolean, 'resolved',
required: true, 'pending',
}, 'snoozed',
}); 'all',
]);
const emit = defineEmits(['changeFilter']); const SORT_ORDER_ITEMS = Object.freeze([
'last_activity_at_asc',
'last_activity_at_desc',
'created_at_desc',
'created_at_asc',
'priority_desc',
'priority_asc',
'waiting_since_asc',
'waiting_since_desc',
]);
const store = useStore(); export default {
const { t } = useI18n(); components: {
FilterItem,
const { updateUISettings } = useUISettings(); NextButton,
const chatStatusFilter = useMapGetter('getChatStatusFilter');
const chatSortFilter = useMapGetter('getChatSortFilter');
const [showActionsDropdown, toggleDropdown] = useToggle();
const currentStatusFilter = computed(() => {
return chatStatusFilter.value || wootConstants.STATUS_TYPE.OPEN;
});
const currentSortBy = computed(() => {
return (
chatSortFilter.value || wootConstants.SORT_BY_TYPE.LAST_ACTIVITY_AT_DESC
);
});
const chatStatusOptions = [
{
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.open.TEXT'),
value: 'open',
}, },
{ props: {
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.resolved.TEXT'), isOnExpandedLayout: {
value: 'resolved', type: Boolean,
}, required: true,
{
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.pending.TEXT'),
value: 'pending',
},
{
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.snoozed.TEXT'),
value: 'snoozed',
},
{
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.all.TEXT'),
value: 'all',
},
];
const chatSortOptions = [
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.last_activity_at_asc.TEXT'),
value: 'last_activity_at_asc',
},
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.last_activity_at_desc.TEXT'),
value: 'last_activity_at_desc',
},
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.created_at_desc.TEXT'),
value: 'created_at_desc',
},
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.created_at_asc.TEXT'),
value: 'created_at_asc',
},
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.priority_desc.TEXT'),
value: 'priority_desc',
},
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.priority_asc.TEXT'),
value: 'priority_asc',
},
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.waiting_since_asc.TEXT'),
value: 'waiting_since_asc',
},
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.waiting_since_desc.TEXT'),
value: 'waiting_since_desc',
},
];
const activeChatStatusLabel = computed(
() =>
chatStatusOptions.find(m => m.value === chatStatusFilter.value)?.label || ''
);
const activeChatSortLabel = computed(
() => chatSortOptions.find(m => m.value === chatSortFilter.value)?.label || ''
);
const saveSelectedFilter = (type, value) => {
updateUISettings({
conversations_filter_by: {
status: type === 'status' ? value : currentStatusFilter.value,
order_by: type === 'sort' ? value : currentSortBy.value,
}, },
}); },
}; emits: ['changeFilter'],
setup() {
const { updateUISettings } = useUISettings();
const handleStatusChange = value => { return {
emit('changeFilter', value, 'status'); updateUISettings,
store.dispatch('setChatStatusFilter', value); };
saveSelectedFilter('status', value); },
}; data() {
return {
const handleSortChange = value => { showActionsDropdown: false,
emit('changeFilter', value, 'sort'); chatStatusItems: CHAT_STATUS_FILTER_ITEMS,
store.dispatch('setChatSortFilter', value); chatSortItems: SORT_ORDER_ITEMS,
saveSelectedFilter('sort', value); };
},
computed: {
...mapGetters({
chatStatusFilter: 'getChatStatusFilter',
chatSortFilter: 'getChatSortFilter',
}),
chatStatus() {
return this.chatStatusFilter || wootConstants.STATUS_TYPE.OPEN;
},
sortFilter() {
return (
this.chatSortFilter || wootConstants.SORT_BY_TYPE.LAST_ACTIVITY_AT_DESC
);
},
},
methods: {
onTabChange(value) {
this.$emit('changeFilter', value);
this.closeDropdown();
},
toggleDropdown() {
this.showActionsDropdown = !this.showActionsDropdown;
},
closeDropdown() {
this.showActionsDropdown = false;
},
onChangeFilter(value, type) {
this.$emit('changeFilter', value, type);
this.saveSelectedFilter(type, value);
},
saveSelectedFilter(type, value) {
this.updateUISettings({
conversations_filter_by: {
status: type === 'status' ? value : this.chatStatus,
order_by: type === 'sort' ? value : this.sortFilter,
},
});
},
},
}; };
</script> </script>
@@ -135,39 +99,39 @@ const handleSortChange = value => {
slate slate
faded faded
xs xs
@click="toggleDropdown()" @click="toggleDropdown"
/> />
<div <div
v-if="showActionsDropdown" v-if="showActionsDropdown"
v-on-click-outside="() => toggleDropdown()" v-on-clickaway="closeDropdown"
class="mt-1 bg-n-alpha-3 backdrop-blur-[100px] border border-n-weak w-72 rounded-xl p-4 absolute z-40 top-full" class="mt-1 dropdown-pane dropdown-pane--open !w-52 !p-4 top-6 border !border-n-weak dark:!border-n-weak !bg-n-alpha-3 dark:!bg-n-alpha-3 backdrop-blur-[100px]"
:class="{ :class="{
'ltr:left-0 rtl:right-0': !isOnExpandedLayout, 'ltr:left-0 rtl:right-0': !isOnExpandedLayout,
'ltr:right-0 rtl:left-0': isOnExpandedLayout, 'ltr:right-0 rtl:left-0': isOnExpandedLayout,
}" }"
> >
<div class="flex items-center justify-between last:mt-4 gap-2"> <div class="flex items-center justify-between last:mt-4">
<span class="text-sm truncate text-n-slate-12"> <span class="text-xs font-medium text-n-slate-12">{{
{{ $t('CHAT_LIST.CHAT_SORT.STATUS') }} $t('CHAT_LIST.CHAT_SORT.STATUS')
</span> }}</span>
<SelectMenu <FilterItem
:model-value="chatStatusFilter" type="status"
:options="chatStatusOptions" :selected-value="chatStatus"
:label="activeChatStatusLabel" :items="chatStatusItems"
:sub-menu-position="isOnExpandedLayout ? 'left' : 'right'" path-prefix="CHAT_LIST.CHAT_STATUS_FILTER_ITEMS"
@update:model-value="handleStatusChange" @on-change-filter="onChangeFilter"
/> />
</div> </div>
<div class="flex items-center justify-between last:mt-4 gap-2"> <div class="flex items-center justify-between last:mt-4">
<span class="text-sm truncate text-n-slate-12"> <span class="text-xs font-medium text-n-slate-12">{{
{{ $t('CHAT_LIST.CHAT_SORT.ORDER_BY') }} $t('CHAT_LIST.CHAT_SORT.ORDER_BY')
</span> }}</span>
<SelectMenu <FilterItem
:model-value="chatSortFilter" type="sort"
:options="chatSortOptions" :selected-value="sortFilter"
:label="activeChatSortLabel" :items="chatSortItems"
:sub-menu-position="isOnExpandedLayout ? 'left' : 'right'" path-prefix="CHAT_LIST.SORT_ORDER_ITEMS"
@update:model-value="handleSortChange" @on-change-filter="onChangeFilter"
/> />
</div> </div>
</div> </div>
@@ -213,17 +213,12 @@ export default {
// Check there is a instagram inbox exists with the same instagram_id // Check there is a instagram inbox exists with the same instagram_id
hasDuplicateInstagramInbox() { hasDuplicateInstagramInbox() {
const instagramId = this.inbox.instagram_id; const instagramId = this.inbox.instagram_id;
const { additional_attributes: additionalAttributes = {} } = this.inbox;
const instagramInbox = const instagramInbox =
this.$store.getters['inboxes/getInstagramInboxByInstagramId']( this.$store.getters['inboxes/getInstagramInboxByInstagramId'](
instagramId instagramId
); );
return ( return this.inbox.channel_type === INBOX_TYPES.FB && instagramInbox;
this.inbox.channel_type === INBOX_TYPES.FB &&
additionalAttributes.type === 'instagram_direct_message' &&
instagramInbox
);
}, },
replyWindowBannerMessage() { replyWindowBannerMessage() {
@@ -249,7 +244,7 @@ export default {
return this.$t('CONVERSATION.CANNOT_REPLY'); return this.$t('CONVERSATION.CANNOT_REPLY');
}, },
replyWindowLink() { replyWindowLink() {
if (this.isAFacebookInbox || this.isAnInstagramChannel) { if (this.isAFacebookInbox || this.isAInstagramChannel) {
return REPLY_POLICY.FACEBOOK; return REPLY_POLICY.FACEBOOK;
} }
if (this.isAWhatsAppCloudChannel) { if (this.isAWhatsAppCloudChannel) {
@@ -264,7 +259,7 @@ export default {
if ( if (
this.isAWhatsAppChannel || this.isAWhatsAppChannel ||
this.isAFacebookInbox || this.isAFacebookInbox ||
this.isAnInstagramChannel this.isAInstagramChannel
) { ) {
return this.$t('CONVERSATION.24_HOURS_WINDOW'); return this.$t('CONVERSATION.24_HOURS_WINDOW');
} }
@@ -241,27 +241,15 @@ export default {
if (this.isAFacebookInbox) { if (this.isAFacebookInbox) {
return MESSAGE_MAX_LENGTH.FACEBOOK; return MESSAGE_MAX_LENGTH.FACEBOOK;
} }
if (this.isAnInstagramChannel) { if (this.isAWhatsAppChannel) {
return MESSAGE_MAX_LENGTH.INSTAGRAM;
}
if (this.isATwilioWhatsAppChannel) {
return MESSAGE_MAX_LENGTH.TWILIO_WHATSAPP; return MESSAGE_MAX_LENGTH.TWILIO_WHATSAPP;
} }
if (this.isAWhatsAppCloudChannel) {
return MESSAGE_MAX_LENGTH.WHATSAPP_CLOUD;
}
if (this.isASmsInbox) { if (this.isASmsInbox) {
return MESSAGE_MAX_LENGTH.TWILIO_SMS; return MESSAGE_MAX_LENGTH.TWILIO_SMS;
} }
if (this.isAnEmailChannel) { if (this.isAnEmailChannel) {
return MESSAGE_MAX_LENGTH.EMAIL; return MESSAGE_MAX_LENGTH.EMAIL;
} }
if (this.isATwilioSMSChannel) {
return MESSAGE_MAX_LENGTH.TWILIO_SMS;
}
if (this.isAWhatsAppChannel) {
return MESSAGE_MAX_LENGTH.WHATSAPP_CLOUD;
}
return MESSAGE_MAX_LENGTH.GENERAL; return MESSAGE_MAX_LENGTH.GENERAL;
}, },
showFileUpload() { showFileUpload() {
@@ -274,7 +262,7 @@ export default {
this.isASmsInbox || this.isASmsInbox ||
this.isATelegramChannel || this.isATelegramChannel ||
this.isALineChannel || this.isALineChannel ||
this.isAnInstagramChannel this.isAInstagramChannel
); );
}, },
replyButtonLabel() { replyButtonLabel() {
@@ -400,14 +388,9 @@ export default {
}, },
}, },
watch: { watch: {
currentChat(conversation, oldConversation) { currentChat(conversation) {
const { can_reply: canReply } = conversation; const { can_reply: canReply } = conversation;
if (oldConversation && oldConversation.id !== conversation.id) { this.setCCAndToEmailsFromLastChat();
// Only update email fields when switching to a completely different conversation (by ID)
// This prevents overwriting user input (e.g., CC/BCC fields) when performing actions
// like self-assign or other updates that do not actually change the conversation context
this.setCCAndToEmailsFromLastChat();
}
if (this.isOnPrivateNote) { if (this.isOnPrivateNote) {
return; return;
@@ -423,12 +406,13 @@ export default {
}, },
// When moving from one conversation to another, the store may not have the // When moving from one conversation to another, the store may not have the
// list of all the messages. A fetch is subsequently made to get the messages. // list of all the messages. A fetch is subsequently made to get the messages.
// This watcher handles two main cases: // However, this update does not trigger the `currentChat` watcher.
// 1. When switching conversations and messages are fetched/updated, ensures CC/BCC fields are set from the latest OUTGOING/INCOMING email (not activity/private messages). // We can add a deep watcher to it, but then, that would be too broad of a net to cast
// 2. Fixes and issue where CC/BCC fields could be reset/lost after assignment/activity actions or message mutations that did not represent a true email context change. // And would impact performance too. So we watch the messages directly.
lastEmail: { // The watcher here is `deep` too, because the messages array is mutated and
handler(lastEmail) { // not replaced. So, a shallow watcher would not catch the change.
if (!lastEmail) return; 'currentChat.messages': {
handler() {
this.setCCAndToEmailsFromLastChat(); this.setCCAndToEmailsFromLastChat();
}, },
deep: true, deep: true,
@@ -705,7 +689,7 @@ export default {
// When users send messages containing both text and attachments on Instagram, Instagram treats them as separate messages. // When users send messages containing both text and attachments on Instagram, Instagram treats them as separate messages.
// Although Chatwoot combines these into a single message, Instagram sends separate echo events for each component. // Although Chatwoot combines these into a single message, Instagram sends separate echo events for each component.
// This can create duplicate messages in Chatwoot. To prevent this issue, we'll handle text and attachments as separate messages. // This can create duplicate messages in Chatwoot. To prevent this issue, we'll handle text and attachments as separate messages.
const isOnInstagram = this.isAnInstagramChannel; const isOnInstagram = this.isAInstagramChannel;
if ((isOnWhatsApp || isOnInstagram) && !this.isPrivate) { if ((isOnWhatsApp || isOnInstagram) && !this.isPrivate) {
this.sendMessageAsMultipleMessages(this.message); this.sendMessageAsMultipleMessages(this.message);
} else { } else {
@@ -959,7 +943,7 @@ export default {
const multipleMessagePayload = []; const multipleMessagePayload = [];
if (this.attachedFiles && this.attachedFiles.length) { if (this.attachedFiles && this.attachedFiles.length) {
let caption = this.isAnInstagramChannel ? '' : message; let caption = this.isAInstagramChannel ? '' : message;
this.attachedFiles.forEach(attachment => { this.attachedFiles.forEach(attachment => {
const attachedFile = this.globalConfig.directUploadsEnabled const attachedFile = this.globalConfig.directUploadsEnabled
? attachment.blobSignedId ? attachment.blobSignedId
@@ -975,7 +959,7 @@ export default {
attachmentPayload = this.setReplyToInPayload(attachmentPayload); attachmentPayload = this.setReplyToInPayload(attachmentPayload);
multipleMessagePayload.push(attachmentPayload); multipleMessagePayload.push(attachmentPayload);
// For WhatsApp, only the first attachment gets a caption // For WhatsApp, only the first attachment gets a caption
if (!this.isAnInstagramChannel) caption = ''; if (!this.isAInstagramChannel) caption = '';
}); });
} }
@@ -984,8 +968,8 @@ export default {
// For Instagram, we need a separate text message // For Instagram, we need a separate text message
// For WhatsApp, we only need a text message if there are no attachments // For WhatsApp, we only need a text message if there are no attachments
if ( if (
(this.isAnInstagramChannel && this.message) || (this.isAInstagramChannel && this.message) ||
(!this.isAnInstagramChannel && hasNoAttachments) (!this.isAInstagramChannel && hasNoAttachments)
) { ) {
let messagePayload = { let messagePayload = {
conversationId: this.currentChat.id, conversationId: this.currentChat.id,
@@ -56,7 +56,7 @@ const unlinkIssue = () => {
<template> <template>
<div <div
class="absolute flex flex-col items-start bg-n-alpha-3 backdrop-blur-[100px] z-50 px-4 py-3 border border-solid border-n-container w-[384px] rounded-xl gap-4 max-h-96 overflow-auto" class="absolute flex flex-col items-start bg-white dark:bg-slate-800 z-50 px-4 py-3 border border-solid border-ash-200 w-[384px] rounded-xl gap-4 max-h-96 overflow-auto"
> >
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<IssueHeader <IssueHeader
@@ -66,37 +66,37 @@ const unlinkIssue = () => {
@unlink-issue="unlinkIssue" @unlink-issue="unlinkIssue"
/> />
<span class="mt-2 text-sm font-medium text-n-slate-12"> <span class="mt-2 text-sm font-medium text-ash-900">
{{ issue.title }} {{ issue.title }}
</span> </span>
<span <span
v-if="issue.description" v-if="issue.description"
class="mt-1 text-sm text-n-slate-11 line-clamp-3" class="mt-1 text-sm text-ash-800 line-clamp-3"
> >
{{ issue.description }} {{ issue.description }}
</span> </span>
</div> </div>
<div class="flex flex-row items-center h-6 gap-2"> <div class="flex flex-row items-center h-6 gap-2">
<UserAvatarWithName v-if="assignee" :user="assignee" class="py-1" /> <UserAvatarWithName v-if="assignee" :user="assignee" class="py-1" />
<div v-if="assignee" class="w-px h-3 bg-n-slate-4" /> <div v-if="assignee" class="w-px h-3 bg-ash-200" />
<div class="flex items-center gap-1 py-1"> <div class="flex items-center gap-1 py-1">
<fluent-icon <fluent-icon
icon="status" icon="status"
size="14" size="14"
:style="{ color: issue.state.color }" :style="{ color: issue.state.color }"
/> />
<h6 class="text-xs text-n-slate-12"> <h6 class="text-xs text-ash-900">
{{ issue.state.name }} {{ issue.state.name }}
</h6> </h6>
</div> </div>
<div v-if="priorityLabel" class="w-px h-3 bg-n-slate-4" /> <div v-if="priorityLabel" class="w-px h-3 bg-ash-200" />
<div v-if="priorityLabel" class="flex items-center gap-1 py-1"> <div v-if="priorityLabel" class="flex items-center gap-1 py-1">
<fluent-icon <fluent-icon
:icon="`priority-${priorityLabel.toLowerCase()}`" :icon="`priority-${priorityLabel.toLowerCase()}`"
size="14" size="14"
view-box="0 0 12 12" view-box="0 0 12 12"
/> />
<h6 class="text-xs text-n-slate-12">{{ priorityLabel }}</h6> <h6 class="text-xs text-ash-900">{{ priorityLabel }}</h6>
</div> </div>
</div> </div>
<div v-if="labels.length" class="flex flex-wrap items-center gap-1"> <div v-if="labels.length" class="flex flex-wrap items-center gap-1">
@@ -111,7 +111,7 @@ const unlinkIssue = () => {
/> />
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<span class="text-xs text-n-slate-11"> <span class="text-xs text-ash-800">
{{ {{
$t('INTEGRATION_SETTINGS.LINEAR.ISSUE.CREATED_AT', { $t('INTEGRATION_SETTINGS.LINEAR.ISSUE.CREATED_AT', {
createdAt: formattedDate, createdAt: formattedDate,
@@ -100,17 +100,14 @@ onMounted(() => {
</script> </script>
<template> <template>
<div <div class="relative" :class="{ group: linkedIssue }">
class="relative after:content-[''] after:h-5 after:bg-transparent after:top-5 after:w-full after:block after:absolute after:z-0"
:class="{ group: linkedIssue }"
>
<Button <Button
v-on-clickaway="closeIssue" v-on-clickaway="closeIssue"
v-tooltip="tooltipText" v-tooltip="tooltipText"
sm sm
ghost ghost
slate slate
class="!gap-1 group-hover:bg-n-alpha-2" class="!gap-1"
@click="openIssue" @click="openIssue"
> >
<fluent-icon <fluent-icon
@@ -127,7 +124,7 @@ onMounted(() => {
v-if="linkedIssue" v-if="linkedIssue"
:issue="linkedIssue.issue" :issue="linkedIssue.issue"
:link-id="linkedIssue.id" :link-id="linkedIssue.id"
class="absolute right-0 top-[36px] invisible group-hover:visible" class="absolute right-0 top-[40px] invisible group-hover:visible"
@unlink-issue="unlinkIssue" @unlink-issue="unlinkIssue"
/> />
<woot-modal <woot-modal
@@ -1,6 +1,6 @@
import { computed } from 'vue'; import { computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useMapGetter, useStore } from './store'; import { useMapGetter } from './store';
/** /**
* Composable for account-related operations. * Composable for account-related operations.
@@ -12,7 +12,6 @@ export function useAccount() {
* @type {import('vue').ComputedRef<number>} * @type {import('vue').ComputedRef<number>}
*/ */
const route = useRoute(); const route = useRoute();
const store = useStore();
const getAccountFn = useMapGetter('accounts/getAccount'); const getAccountFn = useMapGetter('accounts/getAccount');
const isOnChatwootCloud = useMapGetter('globalConfig/isOnChatwootCloud'); const isOnChatwootCloud = useMapGetter('globalConfig/isOnChatwootCloud');
const isFeatureEnabledonAccount = useMapGetter( const isFeatureEnabledonAccount = useMapGetter(
@@ -45,12 +44,6 @@ export function useAccount() {
}; };
}; };
const updateAccount = async data => {
await store.dispatch('accounts/update', {
...data,
});
};
return { return {
accountId, accountId,
route, route,
@@ -59,6 +52,5 @@ export function useAccount() {
accountScopedRoute, accountScopedRoute,
isCloudFeatureEnabled, isCloudFeatureEnabled,
isOnChatwootCloud, isOnChatwootCloud,
updateAccount,
}; };
} }
@@ -121,7 +121,7 @@ export const useInbox = () => {
); );
}); });
const isAnInstagramChannel = computed(() => { const isAInstagramChannel = computed(() => {
return channelType.value === INBOX_TYPES.INSTAGRAM; return channelType.value === INBOX_TYPES.INSTAGRAM;
}); });
@@ -141,6 +141,6 @@ export const useInbox = () => {
isAWhatsAppCloudChannel, isAWhatsAppCloudChannel,
is360DialogWhatsAppChannel, is360DialogWhatsAppChannel,
isAnEmailChannel, isAnEmailChannel,
isAnInstagramChannel, isAInstagramChannel,
}; };
}; };
@@ -6,7 +6,6 @@ export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = Object.freeze([
{ name: 'macros' }, { name: 'macros' },
{ name: 'conversation_info' }, { name: 'conversation_info' },
{ name: 'contact_attributes' }, { name: 'contact_attributes' },
{ name: 'contact_notes' },
{ name: 'previous_conversation' }, { name: 'previous_conversation' },
{ name: 'conversation_participants' }, { name: 'conversation_participants' },
{ name: 'shopify_orders' }, { name: 'shopify_orders' },
@@ -1,44 +1,6 @@
/** export const buildPortalURL = portalSlug => {
* Formats a custom domain with https protocol if needed const { hostURL, helpCenterURL } = window.chatwootConfig;
* @param {string} customDomain - The custom domain to format
* @returns {string} Formatted domain with https protocol
*/
const formatCustomDomain = customDomain =>
customDomain.startsWith('https') ? customDomain : `https://${customDomain}`;
/**
* Gets the default base URL from configuration
* @returns {string} The default base URL
* @throws {Error} If no valid base URL is found
*/
const getDefaultBaseURL = () => {
const { hostURL, helpCenterURL } = window.chatwootConfig || {};
const baseURL = helpCenterURL || hostURL || ''; const baseURL = helpCenterURL || hostURL || '';
if (!baseURL) {
throw new Error('No valid base URL found in configuration');
}
return baseURL;
};
/**
* Gets the base URL from configuration or custom domain
* @param {string} [customDomain] - Optional custom domain for the portal
* @returns {string} The base URL for the portal
*/
const getPortalBaseURL = customDomain =>
customDomain ? formatCustomDomain(customDomain) : getDefaultBaseURL();
/**
* Builds a portal URL using the provided portal slug and optional custom domain
* @param {string} portalSlug - The slug identifier for the portal
* @param {string} [customDomain] - Optional custom domain for the portal
* @returns {string} The complete portal URL
* @throws {Error} If portalSlug is not provided or invalid
*/
export const buildPortalURL = (portalSlug, customDomain) => {
const baseURL = getPortalBaseURL(customDomain);
return `${baseURL}/hc/${portalSlug}`; return `${baseURL}/hc/${portalSlug}`;
}; };
@@ -46,10 +8,9 @@ export const buildPortalArticleURL = (
portalSlug, portalSlug,
categorySlug, categorySlug,
locale, locale,
articleSlug, articleSlug
customDomain
) => { ) => {
const portalURL = buildPortalURL(portalSlug, customDomain); const portalURL = buildPortalURL(portalSlug);
return `${portalURL}/articles/${articleSlug}`; return `${portalURL}/articles/${articleSlug}`;
}; };
@@ -25,47 +25,5 @@ describe('PortalHelper', () => {
).toEqual('https://help.chatwoot.com/hc/handbook/articles/article-slug'); ).toEqual('https://help.chatwoot.com/hc/handbook/articles/article-slug');
window.chatwootConfig = {}; window.chatwootConfig = {};
}); });
it('returns the correct url with custom domain', () => {
window.chatwootConfig = {
hostURL: 'https://app.chatwoot.com',
helpCenterURL: 'https://help.chatwoot.com',
};
expect(
buildPortalArticleURL(
'handbook',
'culture',
'fr',
'article-slug',
'custom-domain.dev'
)
).toEqual('https://custom-domain.dev/hc/handbook/articles/article-slug');
});
it('handles https in custom domain correctly', () => {
window.chatwootConfig = {
hostURL: 'https://app.chatwoot.com',
helpCenterURL: 'https://help.chatwoot.com',
};
expect(
buildPortalArticleURL(
'handbook',
'culture',
'fr',
'article-slug',
'https://custom-domain.dev'
)
).toEqual('https://custom-domain.dev/hc/handbook/articles/article-slug');
});
it('uses hostURL when helpCenterURL is not available', () => {
window.chatwootConfig = {
hostURL: 'https://app.chatwoot.com',
helpCenterURL: '',
};
expect(
buildPortalArticleURL('handbook', 'culture', 'fr', 'article-slug')
).toEqual('https://app.chatwoot.com/hc/handbook/articles/article-slug');
});
}); });
}); });
@@ -2,13 +2,23 @@
"AGENT_BOTS": { "AGENT_BOTS": {
"HEADER": "Bots", "HEADER": "Bots",
"LOADING_EDITOR": "Loading editor...", "LOADING_EDITOR": "Loading editor...",
"DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try. You can manage your bots from this page or create new ones using the 'Add Bot' button.", "DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.You can manage your bots from this page or create new ones using the 'Configure new bot' button.",
"LEARN_MORE": "Learn about agent bots", "LEARN_MORE": "Learn about agent bots",
"GLOBAL_BOT": "System bot", "CSML_BOT_EDITOR": {
"GLOBAL_BOT_BADGE": "System", "NAME": {
"AVATAR": { "LABEL": "Bot name",
"SUCCESS_DELETE": "Bot avatar deleted successfully", "PLACEHOLDER": "Name your bot.",
"ERROR_DELETE": "Error deleting bot avatar, please try again" "ERROR": "Bot name is required."
},
"DESCRIPTION": {
"LABEL": "Bot description",
"PLACEHOLDER": "What does this bot do?"
},
"BOT_CONFIG": {
"ERROR": "Please enter your CSML bot configuration above.",
"API_ERROR": "Your CSML configuration is invalid. Please fix it and try again."
},
"SUBMIT": "Validate and save"
}, },
"BOT_CONFIGURATION": { "BOT_CONFIGURATION": {
"TITLE": "Select an agent bot", "TITLE": "Select an agent bot",
@@ -22,7 +32,7 @@
"SELECT_PLACEHOLDER": "Select bot" "SELECT_PLACEHOLDER": "Select bot"
}, },
"ADD": { "ADD": {
"TITLE": "Add Bot", "TITLE": "Configure new bot",
"CANCEL_BUTTON_TEXT": "Cancel", "CANCEL_BUTTON_TEXT": "Cancel",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot added successfully.", "SUCCESS_MESSAGE": "Bot added successfully.",
@@ -30,22 +40,16 @@
} }
}, },
"LIST": { "LIST": {
"404": "No bots found. You can create a bot by clicking the 'Add Bot' button.", "404": "No bots found. You can create a bot by clicking the 'Configure new bot' button",
"LOADING": "Fetching bots...", "LOADING": "Fetching bots...",
"TABLE_HEADER": { "TYPE": "Bot type"
"DETAILS": "Bot Details",
"URL": "Webhook URL"
}
}, },
"DELETE": { "DELETE": {
"BUTTON_TEXT": "Delete", "BUTTON_TEXT": "Delete",
"TITLE": "Delete bot", "TITLE": "Delete bot",
"CONFIRM": { "SUBMIT": "Delete",
"TITLE": "Confirm Deletion", "CANCEL_BUTTON_TEXT": "Cancel",
"MESSAGE": "Are you sure you want to delete {name}?", "DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible.",
"YES": "Yes, Delete",
"NO": "No, Keep"
},
"API": { "API": {
"SUCCESS_MESSAGE": "Bot deleted successfully.", "SUCCESS_MESSAGE": "Bot deleted successfully.",
"ERROR_MESSAGE": "Could not delete bot. Please try again." "ERROR_MESSAGE": "Could not delete bot. Please try again."
@@ -53,44 +57,17 @@
}, },
"EDIT": { "EDIT": {
"BUTTON_TEXT": "Edit", "BUTTON_TEXT": "Edit",
"LOADING": "Fetching bots...",
"TITLE": "Edit bot", "TITLE": "Edit bot",
"CANCEL_BUTTON_TEXT": "Cancel",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot updated successfully.", "SUCCESS_MESSAGE": "Bot updated successfully.",
"ERROR_MESSAGE": "Could not update bot. Please try again." "ERROR_MESSAGE": "Could not update bot. Please try again."
} }
}, },
"FORM": {
"AVATAR": {
"LABEL": "Bot avatar"
},
"NAME": {
"LABEL": "Bot name",
"PLACEHOLDER": "Enter bot name",
"REQUIRED": "Bot name is required"
},
"DESCRIPTION": {
"LABEL": "Description",
"PLACEHOLDER": "What does this bot do?"
},
"WEBHOOK_URL": {
"LABEL": "Webhook URL",
"PLACEHOLDER": "https://example.com/webhook",
"REQUIRED": "Webhook URL is required"
},
"ERRORS": {
"NAME": "Bot name is required",
"URL": "Webhook URL is required",
"VALID_URL": "Please enter a valid URL starting with http:// or https://"
},
"CANCEL": "Cancel",
"CREATE": "Create Bot",
"UPDATE": "Update Bot"
},
"WEBHOOK": {
"DESCRIPTION": "Configure a webhook bot to integrate with your custom services. The bot will receive and process events from conversations and can respond to them."
},
"TYPES": { "TYPES": {
"WEBHOOK": "Webhook bot" "WEBHOOK": "Webhook bot",
"CSML": "CSML bot"
} }
} }
} }
@@ -126,44 +126,6 @@
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
}, },
"NONE_OPTION": "None", "NONE_OPTION": "None"
"EVENTS": {
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
"ASSIGN_AGENT": "Assign to Agent",
"ASSIGN_TEAM": "Assign a Team",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Mute Conversation",
"SNOOZE_CONVERSATION": "Snooze Conversation",
"RESOLVE_CONVERSATION": "Resolve Conversation",
"SEND_WEBHOOK_EVENT": "Send Webhook Event",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "Change Priority",
"ADD_SLA": "Add SLA"
},
"ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains",
"EMAIL": "Email",
"INBOX": "Inbox",
"CONVERSATION_LANGUAGE": "Conversation Language",
"PHONE_NUMBER": "Phone Number",
"STATUS": "Status",
"BROWSER_LANGUAGE": "Browser Language",
"MAIL_SUBJECT": "Email Subject",
"COUNTRY_NAME": "Country",
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
"PRIORITY": "Priority"
}
} }
} }
@@ -544,9 +544,6 @@
"WROTE": "wrote", "WROTE": "wrote",
"YOU": "You", "YOU": "You",
"SAVE": "Save note", "SAVE": "Save note",
"EXPAND": "Expand",
"COLLAPSE": "Collapse",
"NO_NOTES": "No notes, you can add notes from the contact details page.",
"EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above." "EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above."
} }
}, },
@@ -32,12 +32,10 @@
"LOADING_CONVERSATIONS": "Loading Conversations", "LOADING_CONVERSATIONS": "Loading Conversations",
"CANNOT_REPLY": "You cannot reply due to", "CANNOT_REPLY": "You cannot reply due to",
"24_HOURS_WINDOW": "24 hour message window restriction", "24_HOURS_WINDOW": "24 hour message window restriction",
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?", "NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me", "ASSIGN_TO_ME": "Assign to me",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to", "TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You wont be able to send messages from this conversation anymore.",
"REPLYING_TO": "You are replying to:", "REPLYING_TO": "You are replying to:",
"REMOVE_SELECTION": "Remove Selection", "REMOVE_SELECTION": "Remove Selection",
"DOWNLOAD": "Download", "DOWNLOAD": "Download",
@@ -295,7 +293,6 @@
"CONVERSATION_ACTIONS": "Conversation Actions", "CONVERSATION_ACTIONS": "Conversation Actions",
"CONVERSATION_LABELS": "Conversation Labels", "CONVERSATION_LABELS": "Conversation Labels",
"CONVERSATION_INFO": "Conversation Information", "CONVERSATION_INFO": "Conversation Information",
"CONTACT_NOTES": "Contact Notes",
"CONTACT_ATTRIBUTES": "Contact Attributes", "CONTACT_ATTRIBUTES": "Contact Attributes",
"PREVIOUS_CONVERSATION": "Previous Conversations", "PREVIOUS_CONVERSATION": "Previous Conversations",
"MACROS": "Macros", "MACROS": "Macros",
@@ -1,11 +1,5 @@
{ {
"GENERAL_SETTINGS": { "GENERAL_SETTINGS": {
"LIMIT_MESSAGES": {
"CONVERSATION": "You have exceeded the conversation limit. Hacker plan allows only 500 conversations.",
"INBOXES": "You have exceeded the inbox limit. Hacker plan only supports website live-chat. Additional inboxes like email, WhatsApp etc. require a paid plan.",
"AGENTS": "You have exceeded the agent limit. Hacker plan allows only 2 agents.",
"NON_ADMIN": "Please contact your administrator to upgrade the plan and continue using all features."
},
"TITLE": "Account settings", "TITLE": "Account settings",
"SUBMIT": "Update settings", "SUBMIT": "Update settings",
"BACK": "Back", "BACK": "Back",
@@ -14,26 +8,6 @@
"ERROR": "Could not update settings, try again!", "ERROR": "Could not update settings, try again!",
"SUCCESS": "Successfully updated account settings" "SUCCESS": "Successfully updated account settings"
}, },
"ACCOUNT_DELETE_SECTION": {
"TITLE": "Delete your Account",
"NOTE": "Once you delete your account, all your data will be deleted.",
"BUTTON_TEXT": "Delete Your Account",
"CONFIRM": {
"TITLE": "Delete Account",
"MESSAGE": "Deleting your Account is irreversible. Enter your account name below to confirm you want to permanently delete it.",
"BUTTON_TEXT": "Delete",
"DISMISS": "Cancel",
"PLACE_HOLDER": "Please type {accountName} to confirm"
},
"SUCCESS": "Account marked for deletion",
"FAILURE": "Could not delete account, try again!",
"SCHEDULED_DELETION": {
"TITLE": "Account Scheduled for Deletion",
"MESSAGE_MANUAL": "This account is scheduled for deletion on {deletionDate}. This was requested by an administrator. You can cancel the deletion before this date.",
"MESSAGE_INACTIVITY": "This account is scheduled for deletion on {deletionDate} due to account inactivity. You can cancel the deletion before this date.",
"CLEAR_BUTTON": "Cancel Scheduled Deletion"
}
},
"FORM": { "FORM": {
"ERROR": "Please fix form errors", "ERROR": "Please fix form errors",
"GENERAL_SECTION": { "GENERAL_SECTION": {
@@ -77,7 +51,6 @@
"UPDATE_CHATWOOT": "An update {latestChatwootVersion} for Chatwoot is available. Please update your instance.", "UPDATE_CHATWOOT": "An update {latestChatwootVersion} for Chatwoot is available. Please update your instance.",
"LEARN_MORE": "Learn more", "LEARN_MORE": "Learn more",
"PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot", "PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot",
"UPGRADE": "Upgrade to continue using Chatwoot",
"LIMITS_UPGRADE": "Your account has exceeded the usage limits, please upgrade your plan to continue using Chatwoot", "LIMITS_UPGRADE": "Your account has exceeded the usage limits, please upgrade your plan to continue using Chatwoot",
"OPEN_BILLING": "Open billing" "OPEN_BILLING": "Open billing"
}, },
@@ -696,8 +696,7 @@
"SLUG": { "SLUG": {
"LABEL": "Slug", "LABEL": "Slug",
"PLACEHOLDER": "user-guide", "PLACEHOLDER": "user-guide",
"ERROR": "Slug is required", "ERROR": "Slug is required"
"FORMAT_ERROR": "Please enter a valid slug, for eg: user-guide"
} }
}, },
"PORTAL_SETTINGS": { "PORTAL_SETTINGS": {
@@ -43,17 +43,7 @@
"INBOX_NAME": "Inbox Name", "INBOX_NAME": "Inbox Name",
"ADD_NAME": "Add a name for your inbox", "ADD_NAME": "Add a name for your inbox",
"PICK_NAME": "Pick a Name for your Inbox", "PICK_NAME": "Pick a Name for your Inbox",
"PICK_A_VALUE": "Pick a value", "PICK_A_VALUE": "Pick a value"
"CREATE_INBOX": "Create Inbox"
},
"INSTAGRAM": {
"CONTINUE_WITH_INSTAGRAM": "Continue with Instagram",
"CONNECT_YOUR_INSTAGRAM_PROFILE": "Connect your Instagram Profile",
"HELP": "To add your Instagram profile as a channel, you need to authenticate your Instagram Profile by clicking on 'Continue with Instagram' ",
"ERROR_MESSAGE": "There was an error connecting to Instagram, please try again",
"ERROR_AUTH": "There was an error connecting to Instagram, please try again",
"NEW_INBOX_SUGGESTION": "This Instagram account was previously linked to a different inbox and has now been migrated here. All new messages will appear here. The old inbox will no longer be able to send or receive messages for this account.",
"DUPLICATE_INBOX_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. You wont be able to send/receive Instagram messages from this inbox anymore."
}, },
"TWITTER": { "TWITTER": {
"HELP": "To add your Twitter profile as a channel, you need to authenticate your Twitter Profile by clicking on 'Sign in with Twitter' ", "HELP": "To add your Twitter profile as a channel, you need to authenticate your Twitter Profile by clicking on 'Sign in with Twitter' ",
@@ -763,8 +753,7 @@
"EMAIL": "Email", "EMAIL": "Email",
"TELEGRAM": "Telegram", "TELEGRAM": "Telegram",
"LINE": "Line", "LINE": "Line",
"API": "API Channel", "API": "API Channel"
"INSTAGRAM": "Instagram"
} }
} }
} }
@@ -329,34 +329,11 @@
"HEADER_KNOW_MORE": "Know more", "HEADER_KNOW_MORE": "Know more",
"COPILOT": { "COPILOT": {
"SEND_MESSAGE": "Send message...", "SEND_MESSAGE": "Send message...",
"EMPTY_MESSAGE": "There was an error generating the response. Please try again.",
"LOADER": "Captain is thinking", "LOADER": "Captain is thinking",
"YOU": "You", "YOU": "You",
"USE": "Use this", "USE": "Use this",
"RESET": "Reset", "RESET": "Reset",
"SELECT_ASSISTANT": "Select Assistant", "SELECT_ASSISTANT": "Select Assistant"
"PROMPTS": {
"SUMMARIZE": {
"LABEL": "Summarize this conversation",
"CONTENT": "Summarize the key points discussed between the customer and the support agent, including the customer's concerns, questions, and the solutions or responses provided by the support agent"
},
"SUGGEST": {
"LABEL": "Suggest an answer",
"CONTENT": "Analyze the customer's inquiry, and draft a response that effectively addresses their concerns or questions. Ensure the reply is clear, concise, and provides helpful information."
},
"RATE": {
"LABEL": "Rate this conversation",
"CONTENT": "Review the conversation to see how well it meets the customer's needs. Share a rating out of 5 based on tone, clarity, and effectiveness."
}
}
},
"PLAYGROUND": {
"USER": "You",
"ASSISTANT": "Assistant",
"MESSAGE_PLACEHOLDER": "Type your message...",
"HEADER": "Playground",
"DESCRIPTION": "Use this playground to send messages to your assistant and check if it responds accurately, quickly, and in the tone you expect.",
"CREDIT_NOTE": "Messages sent here will count toward your Captain credits."
}, },
"PAYWALL": { "PAYWALL": {
"TITLE": "Upgrade to use Captain AI", "TITLE": "Upgrade to use Captain AI",
@@ -396,45 +373,21 @@
"ERROR_MESSAGE": "There was an error creating the assistant, please try again." "ERROR_MESSAGE": "There was an error creating the assistant, please try again."
}, },
"FORM": { "FORM": {
"UPDATE": "Update",
"SECTIONS": {
"BASIC_INFO": "Basic Information",
"SYSTEM_MESSAGES": "System Messages",
"INSTRUCTIONS": "Instructions",
"FEATURES": "Features",
"TOOLS": "Tools "
},
"NAME": { "NAME": {
"LABEL": "Name", "LABEL": "Assistant Name",
"PLACEHOLDER": "Enter assistant name", "PLACEHOLDER": "Enter a name for the assistant",
"ERROR": "The name is required" "ERROR": "Please provide a name for the assistant"
}, },
"DESCRIPTION": { "DESCRIPTION": {
"LABEL": "Description", "LABEL": "Assistant Description",
"PLACEHOLDER": "Enter assistant description", "PLACEHOLDER": "Describe how and where this assistant will be used",
"ERROR": "The description is required" "ERROR": "A description is required"
}, },
"PRODUCT_NAME": { "PRODUCT_NAME": {
"LABEL": "Product Name", "LABEL": "Product Name",
"PLACEHOLDER": "Enter product name", "PLACEHOLDER": "Enter the name of the product this assistant is designed for",
"ERROR": "The product name is required" "ERROR": "The product name is required"
}, },
"WELCOME_MESSAGE": {
"LABEL": "Welcome Message",
"PLACEHOLDER": "Enter welcome message"
},
"HANDOFF_MESSAGE": {
"LABEL": "Handoff Message",
"PLACEHOLDER": "Enter handoff message"
},
"RESOLUTION_MESSAGE": {
"LABEL": "Resolution Message",
"PLACEHOLDER": "Enter resolution message"
},
"INSTRUCTIONS": {
"LABEL": "Instructions",
"PLACEHOLDER": "Enter instructions for the assistant"
},
"FEATURES": { "FEATURES": {
"TITLE": "Features", "TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations", "ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
@@ -444,8 +397,7 @@
"EDIT": { "EDIT": {
"TITLE": "Update the assistant", "TITLE": "Update the assistant",
"SUCCESS_MESSAGE": "The assistant has been successfully updated", "SUCCESS_MESSAGE": "The assistant has been successfully updated",
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again."
"NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
@@ -83,22 +83,6 @@
"ACTION_PARAMETERS_REQUIRED": "Action parameters are required", "ACTION_PARAMETERS_REQUIRED": "Action parameters are required",
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
},
"ACTIONS": {
"ASSIGN_TEAM": "Assign a Team",
"ASSIGN_AGENT": "Assign an Agent",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Mute Conversation",
"SNOOZE_CONVERSATION": "Snooze Conversation",
"RESOLVE_CONVERSATION": "Resolve Conversation",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event"
} }
} }
} }
@@ -387,8 +387,7 @@
"LABEL": "Company Name", "LABEL": "Company Name",
"PLACEHOLDER": "Wayne Enterprises" "PLACEHOLDER": "Wayne Enterprises"
}, },
"SUBMIT": "Submit", "SUBMIT": "Submit"
"CANCEL": "Cancel"
} }
}, },
"KEYBOARD_SHORTCUTS": { "KEYBOARD_SHORTCUTS": {
@@ -2,13 +2,23 @@
"AGENT_BOTS": { "AGENT_BOTS": {
"HEADER": "الروبوتات", "HEADER": "الروبوتات",
"LOADING_EDITOR": "جار جلب المحرر...", "LOADING_EDITOR": "جار جلب المحرر...",
"DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try. You can manage your bots from this page or create new ones using the 'Add Bot' button.", "DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.You can manage your bots from this page or create new ones using the 'Configure new bot' button.",
"LEARN_MORE": "Learn about agent bots", "LEARN_MORE": "Learn about agent bots",
"GLOBAL_BOT": "System bot", "CSML_BOT_EDITOR": {
"GLOBAL_BOT_BADGE": "النظام", "NAME": {
"AVATAR": { "LABEL": "اسم الروبوت",
"SUCCESS_DELETE": "Bot avatar deleted successfully", "PLACEHOLDER": "قم بتسمية الروبوت الخاص بك.",
"ERROR_DELETE": "Error deleting bot avatar, please try again" "ERROR": "اسم الروبوت مطلوب."
},
"DESCRIPTION": {
"LABEL": "وصف الروبوت",
"PLACEHOLDER": "ماذا يفعل هذا الروبوت؟"
},
"BOT_CONFIG": {
"ERROR": "يرجى إدخال تكوين نبوت CSML الخاص بك أعلاه.",
"API_ERROR": "تكوين CSML الخاص بك غير صالح. يرجى إصلاحه والمحاولة مرة أخرى."
},
"SUBMIT": "التحقق والحفظ"
}, },
"BOT_CONFIGURATION": { "BOT_CONFIGURATION": {
"TITLE": "اختر الروبوت", "TITLE": "اختر الروبوت",
@@ -22,7 +32,7 @@
"SELECT_PLACEHOLDER": "اختر الروبوت" "SELECT_PLACEHOLDER": "اختر الروبوت"
}, },
"ADD": { "ADD": {
"TITLE": "Add Bot", "TITLE": "تكوين روبوت جديد",
"CANCEL_BUTTON_TEXT": "إلغاء", "CANCEL_BUTTON_TEXT": "إلغاء",
"API": { "API": {
"SUCCESS_MESSAGE": "تمت إضافة الروبوت بنجاح.", "SUCCESS_MESSAGE": "تمت إضافة الروبوت بنجاح.",
@@ -30,22 +40,16 @@
} }
}, },
"LIST": { "LIST": {
"404": "No bots found. You can create a bot by clicking the 'Add Bot' button.", "404": "لم يتم العثور على أي روبوتات. يمكنك إنشاء الروبوت بالنقر على زر 'تكوين روبوت جديد' ↗",
"LOADING": "جار جلب الروبوتات...", "LOADING": "جار جلب الروبوتات...",
"TABLE_HEADER": { "TYPE": "نوع الروبوت"
"DETAILS": "Bot Details",
"URL": "رابط Webhook"
}
}, },
"DELETE": { "DELETE": {
"BUTTON_TEXT": "حذف", "BUTTON_TEXT": "حذف",
"TITLE": "حذف الروبوت", "TITLE": "حذف الروبوت",
"CONFIRM": { "SUBMIT": "حذف",
"TITLE": "تأكيد الحذف", "CANCEL_BUTTON_TEXT": "إلغاء",
"MESSAGE": "Are you sure you want to delete {name}?", "DESCRIPTION": "هل أنت متأكد أنك تريد حذف هذا الروبوت؟ هذا الإجراء لا يمكن التراجع عنه.",
"YES": "نعم، احذف",
"NO": "لا، احتفظ"
},
"API": { "API": {
"SUCCESS_MESSAGE": "تم حذف الروبوت بنجاح.", "SUCCESS_MESSAGE": "تم حذف الروبوت بنجاح.",
"ERROR_MESSAGE": "تعذر حذف الروبوت. يرجى المحاولة مرة أخرى." "ERROR_MESSAGE": "تعذر حذف الروبوت. يرجى المحاولة مرة أخرى."
@@ -53,44 +57,17 @@
}, },
"EDIT": { "EDIT": {
"BUTTON_TEXT": "تعديل", "BUTTON_TEXT": "تعديل",
"LOADING": "جار جلب الروبوتات...",
"TITLE": "تعديل الروبوت", "TITLE": "تعديل الروبوت",
"CANCEL_BUTTON_TEXT": "إلغاء",
"API": { "API": {
"SUCCESS_MESSAGE": "تم تحديث الروبوت بنجاح.", "SUCCESS_MESSAGE": "تم تحديث الروبوت بنجاح.",
"ERROR_MESSAGE": "تعذر تحديث الروبوت. يرجى المحاولة مرة أخرى." "ERROR_MESSAGE": "تعذر تحديث الروبوت. يرجى المحاولة مرة أخرى."
} }
}, },
"FORM": {
"AVATAR": {
"LABEL": "Bot avatar"
},
"NAME": {
"LABEL": "اسم الروبوت",
"PLACEHOLDER": "Enter bot name",
"REQUIRED": "اسم الروبوت مطلوب"
},
"DESCRIPTION": {
"LABEL": "الوصف",
"PLACEHOLDER": "ماذا يفعل هذا الروبوت؟"
},
"WEBHOOK_URL": {
"LABEL": "رابط Webhook",
"PLACEHOLDER": "https://example.com/webhook",
"REQUIRED": "Webhook URL is required"
},
"ERRORS": {
"NAME": "اسم الروبوت مطلوب",
"URL": "Webhook URL is required",
"VALID_URL": "Please enter a valid URL starting with http:// or https://"
},
"CANCEL": "إلغاء",
"CREATE": "Create Bot",
"UPDATE": "Update Bot"
},
"WEBHOOK": {
"DESCRIPTION": "Configure a webhook bot to integrate with your custom services. The bot will receive and process events from conversations and can respond to them."
},
"TYPES": { "TYPES": {
"WEBHOOK": "روبوت الـWebhook" "WEBHOOK": "روبوت الـWebhook",
"CSML": "بوت CSML"
} }
} }
} }
@@ -126,44 +126,6 @@
"ATLEAST_ONE_CONDITION_REQUIRED": "شرط واحد على الأقل مطلوب", "ATLEAST_ONE_CONDITION_REQUIRED": "شرط واحد على الأقل مطلوب",
"ATLEAST_ONE_ACTION_REQUIRED": "إجراء واحد على الأقل مطلوب" "ATLEAST_ONE_ACTION_REQUIRED": "إجراء واحد على الأقل مطلوب"
}, },
"NONE_OPTION": "لا شيء", "NONE_OPTION": "لا شيء"
"EVENTS": {
"CONVERSATION_CREATED": "تم إنشاء المحادثة",
"CONVERSATION_UPDATED": "تم تحديث المحادثة",
"MESSAGE_CREATED": "Message Created",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
"ASSIGN_AGENT": "Assign to Agent",
"ASSIGN_TEAM": "Assign a Team",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "كتم المحادثة",
"SNOOZE_CONVERSATION": "تأجيل المحادثة",
"RESOLVE_CONVERSATION": "إعادة فتح المحادثة",
"SEND_WEBHOOK_EVENT": "Send Webhook Event",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "تغيير الأولوية",
"ADD_SLA": "Add SLA"
},
"ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains",
"EMAIL": "البريد الإلكتروني",
"INBOX": "صندوق الوارد",
"CONVERSATION_LANGUAGE": "Conversation Language",
"PHONE_NUMBER": "رقم الهاتف",
"STATUS": "الحالة",
"BROWSER_LANGUAGE": "لغة المتصفح",
"MAIL_SUBJECT": "Email Subject",
"COUNTRY_NAME": "الدولة",
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "المكلَّف",
"TEAM_NAME": "الفريق",
"PRIORITY": "الأولوية"
}
} }
} }
@@ -544,9 +544,6 @@
"WROTE": "كتب", "WROTE": "كتب",
"YOU": "أنت", "YOU": "أنت",
"SAVE": "Save note", "SAVE": "Save note",
"EXPAND": "Expand",
"COLLAPSE": "Collapse",
"NO_NOTES": "No notes, you can add notes from the contact details page.",
"EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above." "EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above."
} }
}, },
@@ -32,12 +32,10 @@
"LOADING_CONVERSATIONS": "جاري جلب المحادثات", "LOADING_CONVERSATIONS": "جاري جلب المحادثات",
"CANNOT_REPLY": "لا يمكنك الرد بسبب", "CANNOT_REPLY": "لا يمكنك الرد بسبب",
"24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة", "24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة",
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "لم يتم تعيين هذه المحادثة لك. هل ترغب في تعيين هذه المحادثة لنفسك؟", "NOT_ASSIGNED_TO_YOU": "لم يتم تعيين هذه المحادثة لك. هل ترغب في تعيين هذه المحادثة لنفسك؟",
"ASSIGN_TO_ME": "إسناد لي", "ASSIGN_TO_ME": "إسناد لي",
"TWILIO_WHATSAPP_CAN_REPLY": "يمكنك فقط الرد على هذه المحادثة باستخدام رسالة قالب بسبب", "TWILIO_WHATSAPP_CAN_REPLY": "يمكنك فقط الرد على هذه المحادثة باستخدام رسالة قالب بسبب",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You wont be able to send messages from this conversation anymore.",
"REPLYING_TO": "أنت ترد على:", "REPLYING_TO": "أنت ترد على:",
"REMOVE_SELECTION": "إزالة التحديد", "REMOVE_SELECTION": "إزالة التحديد",
"DOWNLOAD": "تحميل", "DOWNLOAD": "تحميل",
@@ -295,7 +293,6 @@
"CONVERSATION_ACTIONS": "إجراءات المحادثة", "CONVERSATION_ACTIONS": "إجراءات المحادثة",
"CONVERSATION_LABELS": "وسوم المحادثة", "CONVERSATION_LABELS": "وسوم المحادثة",
"CONVERSATION_INFO": "معلومات المحادثة", "CONVERSATION_INFO": "معلومات المحادثة",
"CONTACT_NOTES": "Contact Notes",
"CONTACT_ATTRIBUTES": "سمات جهة الاتصال", "CONTACT_ATTRIBUTES": "سمات جهة الاتصال",
"PREVIOUS_CONVERSATION": "المحادثات السابقة", "PREVIOUS_CONVERSATION": "المحادثات السابقة",
"MACROS": "ماكروس", "MACROS": "ماكروس",
@@ -1,11 +1,5 @@
{ {
"GENERAL_SETTINGS": { "GENERAL_SETTINGS": {
"LIMIT_MESSAGES": {
"CONVERSATION": "You have exceeded the conversation limit. Hacker plan allows only 500 conversations.",
"INBOXES": "You have exceeded the inbox limit. Hacker plan only supports website live-chat. Additional inboxes like email, WhatsApp etc. require a paid plan.",
"AGENTS": "You have exceeded the agent limit. Hacker plan allows only 2 agents.",
"NON_ADMIN": "Please contact your administrator to upgrade the plan and continue using all features."
},
"TITLE": "إعدادات الحساب", "TITLE": "إعدادات الحساب",
"SUBMIT": "تحديث الإعدادات", "SUBMIT": "تحديث الإعدادات",
"BACK": "العودة", "BACK": "العودة",
@@ -14,26 +8,6 @@
"ERROR": "تعذر تحديث الإعدادات، الرجاء المحاولة مرة أخرى!", "ERROR": "تعذر تحديث الإعدادات، الرجاء المحاولة مرة أخرى!",
"SUCCESS": "تم تحديث إعدادات الحساب بنجاح" "SUCCESS": "تم تحديث إعدادات الحساب بنجاح"
}, },
"ACCOUNT_DELETE_SECTION": {
"TITLE": "Delete your Account",
"NOTE": "Once you delete your account, all your data will be deleted.",
"BUTTON_TEXT": "Delete Your Account",
"CONFIRM": {
"TITLE": "Delete Account",
"MESSAGE": "Deleting your Account is irreversible. Enter your account name below to confirm you want to permanently delete it.",
"BUTTON_TEXT": "حذف",
"DISMISS": "إلغاء",
"PLACE_HOLDER": "الرجاء كتابة {accountName} للتأكيد"
},
"SUCCESS": "Account marked for deletion",
"FAILURE": "Could not delete account, try again!",
"SCHEDULED_DELETION": {
"TITLE": "Account Scheduled for Deletion",
"MESSAGE_MANUAL": "This account is scheduled for deletion on {deletionDate}. This was requested by an administrator. You can cancel the deletion before this date.",
"MESSAGE_INACTIVITY": "This account is scheduled for deletion on {deletionDate} due to account inactivity. You can cancel the deletion before this date.",
"CLEAR_BUTTON": "Cancel Scheduled Deletion"
}
},
"FORM": { "FORM": {
"ERROR": "الرجاء إصلاح الأخطاء في النموذج", "ERROR": "الرجاء إصلاح الأخطاء في النموذج",
"GENERAL_SECTION": { "GENERAL_SECTION": {
@@ -77,7 +51,6 @@
"UPDATE_CHATWOOT": "يتوفر تحديث {latestChatwootVersion} لـ Chatwoot. الرجاء التحديث.", "UPDATE_CHATWOOT": "يتوفر تحديث {latestChatwootVersion} لـ Chatwoot. الرجاء التحديث.",
"LEARN_MORE": "اعرف المزيد", "LEARN_MORE": "اعرف المزيد",
"PAYMENT_PENDING": "الدفعة الخاصة بك معلقة. الرجاء تحديث معلومات الدفع الخاصة بك للاستمرار في استخدام Chatwoot", "PAYMENT_PENDING": "الدفعة الخاصة بك معلقة. الرجاء تحديث معلومات الدفع الخاصة بك للاستمرار في استخدام Chatwoot",
"UPGRADE": "Upgrade to continue using Chatwoot",
"LIMITS_UPGRADE": "لقد تجاوز حسابك حدود الاستخدام، يرجى ترقية خطتك للاستمرار في استخدام Chatwoot", "LIMITS_UPGRADE": "لقد تجاوز حسابك حدود الاستخدام، يرجى ترقية خطتك للاستمرار في استخدام Chatwoot",
"OPEN_BILLING": "فتح الفواتير" "OPEN_BILLING": "فتح الفواتير"
}, },
@@ -696,8 +696,7 @@
"SLUG": { "SLUG": {
"LABEL": "Slug", "LABEL": "Slug",
"PLACEHOLDER": "user-guide", "PLACEHOLDER": "user-guide",
"ERROR": "Slug is required", "ERROR": "Slug is required"
"FORMAT_ERROR": "Please enter a valid slug, for eg: user-guide"
} }
}, },
"PORTAL_SETTINGS": { "PORTAL_SETTINGS": {
@@ -43,17 +43,7 @@
"INBOX_NAME": "اسم صندوق الوارد لقناة التواصل", "INBOX_NAME": "اسم صندوق الوارد لقناة التواصل",
"ADD_NAME": "قم بتعيين اسم لصندوق الوارد الخاص بقناتك الجديدة", "ADD_NAME": "قم بتعيين اسم لصندوق الوارد الخاص بقناتك الجديدة",
"PICK_NAME": "Pick a Name for your Inbox", "PICK_NAME": "Pick a Name for your Inbox",
"PICK_A_VALUE": "اختر قيمة", "PICK_A_VALUE": "اختر قيمة"
"CREATE_INBOX": "إنشاء قناة تواصل"
},
"INSTAGRAM": {
"CONTINUE_WITH_INSTAGRAM": "Continue with Instagram",
"CONNECT_YOUR_INSTAGRAM_PROFILE": "Connect your Instagram Profile",
"HELP": "To add your Instagram profile as a channel, you need to authenticate your Instagram Profile by clicking on 'Continue with Instagram' ",
"ERROR_MESSAGE": "There was an error connecting to Instagram, please try again",
"ERROR_AUTH": "There was an error connecting to Instagram, please try again",
"NEW_INBOX_SUGGESTION": "This Instagram account was previously linked to a different inbox and has now been migrated here. All new messages will appear here. The old inbox will no longer be able to send or receive messages for this account.",
"DUPLICATE_INBOX_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. You wont be able to send/receive Instagram messages from this inbox anymore."
}, },
"TWITTER": { "TWITTER": {
"HELP": "لإضافة حساب تويتر الخاص بك كقناة تواصل، تحتاج إلى مصادقة حسابك على تويتر بك بالنقر على زر \"تسجيل الدخول باستخدام تويتر\" ", "HELP": "لإضافة حساب تويتر الخاص بك كقناة تواصل، تحتاج إلى مصادقة حسابك على تويتر بك بالنقر على زر \"تسجيل الدخول باستخدام تويتر\" ",
@@ -763,8 +753,7 @@
"EMAIL": "البريد الإلكتروني", "EMAIL": "البريد الإلكتروني",
"TELEGRAM": "تيليجرام", "TELEGRAM": "تيليجرام",
"LINE": "Line", "LINE": "Line",
"API": "قناة API", "API": "قناة API"
"INSTAGRAM": "Instagram"
} }
} }
} }
@@ -329,34 +329,11 @@
"HEADER_KNOW_MORE": "Know more", "HEADER_KNOW_MORE": "Know more",
"COPILOT": { "COPILOT": {
"SEND_MESSAGE": "إرسال الرسالة...", "SEND_MESSAGE": "إرسال الرسالة...",
"EMPTY_MESSAGE": "There was an error generating the response. Please try again.",
"LOADER": "Captain is thinking", "LOADER": "Captain is thinking",
"YOU": "أنت", "YOU": "أنت",
"USE": "Use this", "USE": "Use this",
"RESET": "Reset", "RESET": "Reset",
"SELECT_ASSISTANT": "Select Assistant", "SELECT_ASSISTANT": "Select Assistant"
"PROMPTS": {
"SUMMARIZE": {
"LABEL": "Summarize this conversation",
"CONTENT": "Summarize the key points discussed between the customer and the support agent, including the customer's concerns, questions, and the solutions or responses provided by the support agent"
},
"SUGGEST": {
"LABEL": "Suggest an answer",
"CONTENT": "Analyze the customer's inquiry, and draft a response that effectively addresses their concerns or questions. Ensure the reply is clear, concise, and provides helpful information."
},
"RATE": {
"LABEL": "Rate this conversation",
"CONTENT": "Review the conversation to see how well it meets the customer's needs. Share a rating out of 5 based on tone, clarity, and effectiveness."
}
}
},
"PLAYGROUND": {
"USER": "أنت",
"ASSISTANT": "Assistant",
"MESSAGE_PLACEHOLDER": "أكتب رسالتك...",
"HEADER": "Playground",
"DESCRIPTION": "Use this playground to send messages to your assistant and check if it responds accurately, quickly, and in the tone you expect.",
"CREDIT_NOTE": "Messages sent here will count toward your Captain credits."
}, },
"PAYWALL": { "PAYWALL": {
"TITLE": "Upgrade to use Captain AI", "TITLE": "Upgrade to use Captain AI",
@@ -396,45 +373,21 @@
"ERROR_MESSAGE": "There was an error creating the assistant, please try again." "ERROR_MESSAGE": "There was an error creating the assistant, please try again."
}, },
"FORM": { "FORM": {
"UPDATE": "تحديث",
"SECTIONS": {
"BASIC_INFO": "Basic Information",
"SYSTEM_MESSAGES": "System Messages",
"INSTRUCTIONS": "Instructions",
"FEATURES": "الخصائص",
"TOOLS": "Tools "
},
"NAME": { "NAME": {
"LABEL": "الاسم", "LABEL": "Assistant Name",
"PLACEHOLDER": "Enter assistant name", "PLACEHOLDER": "Enter a name for the assistant",
"ERROR": "The name is required" "ERROR": "Please provide a name for the assistant"
}, },
"DESCRIPTION": { "DESCRIPTION": {
"LABEL": "الوصف", "LABEL": "Assistant Description",
"PLACEHOLDER": "Enter assistant description", "PLACEHOLDER": "Describe how and where this assistant will be used",
"ERROR": "The description is required" "ERROR": "A description is required"
}, },
"PRODUCT_NAME": { "PRODUCT_NAME": {
"LABEL": "Product Name", "LABEL": "Product Name",
"PLACEHOLDER": "Enter product name", "PLACEHOLDER": "Enter the name of the product this assistant is designed for",
"ERROR": "The product name is required" "ERROR": "The product name is required"
}, },
"WELCOME_MESSAGE": {
"LABEL": "Welcome Message",
"PLACEHOLDER": "Enter welcome message"
},
"HANDOFF_MESSAGE": {
"LABEL": "Handoff Message",
"PLACEHOLDER": "Enter handoff message"
},
"RESOLUTION_MESSAGE": {
"LABEL": "Resolution Message",
"PLACEHOLDER": "Enter resolution message"
},
"INSTRUCTIONS": {
"LABEL": "Instructions",
"PLACEHOLDER": "Enter instructions for the assistant"
},
"FEATURES": { "FEATURES": {
"TITLE": "الخصائص", "TITLE": "الخصائص",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations", "ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
@@ -444,8 +397,7 @@
"EDIT": { "EDIT": {
"TITLE": "Update the assistant", "TITLE": "Update the assistant",
"SUCCESS_MESSAGE": "The assistant has been successfully updated", "SUCCESS_MESSAGE": "The assistant has been successfully updated",
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again."
"NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
@@ -83,22 +83,6 @@
"ACTION_PARAMETERS_REQUIRED": "معلمات الإجراء مطلوبة", "ACTION_PARAMETERS_REQUIRED": "معلمات الإجراء مطلوبة",
"ATLEAST_ONE_CONDITION_REQUIRED": "شرط واحد على الأقل مطلوب", "ATLEAST_ONE_CONDITION_REQUIRED": "شرط واحد على الأقل مطلوب",
"ATLEAST_ONE_ACTION_REQUIRED": "إجراء واحد على الأقل مطلوب" "ATLEAST_ONE_ACTION_REQUIRED": "إجراء واحد على الأقل مطلوب"
},
"ACTIONS": {
"ASSIGN_TEAM": "Assign a Team",
"ASSIGN_AGENT": "Assign an Agent",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "كتم المحادثة",
"SNOOZE_CONVERSATION": "تأجيل المحادثة",
"RESOLVE_CONVERSATION": "إعادة فتح المحادثة",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "تغيير الأولوية",
"ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event"
} }
} }
} }
@@ -387,8 +387,7 @@
"LABEL": "اسم المنشأة", "LABEL": "اسم المنشأة",
"PLACEHOLDER": "مؤسسة Wayne" "PLACEHOLDER": "مؤسسة Wayne"
}, },
"SUBMIT": "إرسال", "SUBMIT": "إرسال"
"CANCEL": "إلغاء"
} }
}, },
"KEYBOARD_SHORTCUTS": { "KEYBOARD_SHORTCUTS": {
@@ -2,13 +2,23 @@
"AGENT_BOTS": { "AGENT_BOTS": {
"HEADER": "Bots", "HEADER": "Bots",
"LOADING_EDITOR": "Loading editor...", "LOADING_EDITOR": "Loading editor...",
"DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try. You can manage your bots from this page or create new ones using the 'Add Bot' button.", "DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.You can manage your bots from this page or create new ones using the 'Configure new bot' button.",
"LEARN_MORE": "Learn about agent bots", "LEARN_MORE": "Learn about agent bots",
"GLOBAL_BOT": "System bot", "CSML_BOT_EDITOR": {
"GLOBAL_BOT_BADGE": "System", "NAME": {
"AVATAR": { "LABEL": "Bot name",
"SUCCESS_DELETE": "Bot avatar deleted successfully", "PLACEHOLDER": "Name your bot.",
"ERROR_DELETE": "Error deleting bot avatar, please try again" "ERROR": "Bot name is required."
},
"DESCRIPTION": {
"LABEL": "Bot description",
"PLACEHOLDER": "What does this bot do?"
},
"BOT_CONFIG": {
"ERROR": "Please enter your CSML bot configuration above.",
"API_ERROR": "Your CSML configuration is invalid. Please fix it and try again."
},
"SUBMIT": "Validate and save"
}, },
"BOT_CONFIGURATION": { "BOT_CONFIGURATION": {
"TITLE": "Select an agent bot", "TITLE": "Select an agent bot",
@@ -22,7 +32,7 @@
"SELECT_PLACEHOLDER": "Select bot" "SELECT_PLACEHOLDER": "Select bot"
}, },
"ADD": { "ADD": {
"TITLE": "Add Bot", "TITLE": "Configure new bot",
"CANCEL_BUTTON_TEXT": "Cancel", "CANCEL_BUTTON_TEXT": "Cancel",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot added successfully.", "SUCCESS_MESSAGE": "Bot added successfully.",
@@ -30,22 +40,16 @@
} }
}, },
"LIST": { "LIST": {
"404": "No bots found. You can create a bot by clicking the 'Add Bot' button.", "404": "No bots found. You can create a bot by clicking the 'Configure new bot' button",
"LOADING": "Fetching bots...", "LOADING": "Fetching bots...",
"TABLE_HEADER": { "TYPE": "Bot type"
"DETAILS": "Bot Details",
"URL": "Webhook URL"
}
}, },
"DELETE": { "DELETE": {
"BUTTON_TEXT": "Delete", "BUTTON_TEXT": "Delete",
"TITLE": "Delete bot", "TITLE": "Delete bot",
"CONFIRM": { "SUBMIT": "Delete",
"TITLE": "Confirm Deletion", "CANCEL_BUTTON_TEXT": "Cancel",
"MESSAGE": "Are you sure you want to delete {name}?", "DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible.",
"YES": "Yes, Delete",
"NO": "No, Keep"
},
"API": { "API": {
"SUCCESS_MESSAGE": "Bot deleted successfully.", "SUCCESS_MESSAGE": "Bot deleted successfully.",
"ERROR_MESSAGE": "Could not delete bot. Please try again." "ERROR_MESSAGE": "Could not delete bot. Please try again."
@@ -53,44 +57,17 @@
}, },
"EDIT": { "EDIT": {
"BUTTON_TEXT": "Edit", "BUTTON_TEXT": "Edit",
"LOADING": "Fetching bots...",
"TITLE": "Edit bot", "TITLE": "Edit bot",
"CANCEL_BUTTON_TEXT": "Cancel",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot updated successfully.", "SUCCESS_MESSAGE": "Bot updated successfully.",
"ERROR_MESSAGE": "Could not update bot. Please try again." "ERROR_MESSAGE": "Could not update bot. Please try again."
} }
}, },
"FORM": {
"AVATAR": {
"LABEL": "Bot avatar"
},
"NAME": {
"LABEL": "Bot name",
"PLACEHOLDER": "Enter bot name",
"REQUIRED": "Bot name is required"
},
"DESCRIPTION": {
"LABEL": "Description",
"PLACEHOLDER": "What does this bot do?"
},
"WEBHOOK_URL": {
"LABEL": "Webhook URL",
"PLACEHOLDER": "https://example.com/webhook",
"REQUIRED": "Webhook URL is required"
},
"ERRORS": {
"NAME": "Bot name is required",
"URL": "Webhook URL is required",
"VALID_URL": "Please enter a valid URL starting with http:// or https://"
},
"CANCEL": "Cancel",
"CREATE": "Create Bot",
"UPDATE": "Update Bot"
},
"WEBHOOK": {
"DESCRIPTION": "Configure a webhook bot to integrate with your custom services. The bot will receive and process events from conversations and can respond to them."
},
"TYPES": { "TYPES": {
"WEBHOOK": "Webhook bot" "WEBHOOK": "Webhook bot",
"CSML": "CSML bot"
} }
} }
} }
@@ -126,44 +126,6 @@
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
}, },
"NONE_OPTION": "None", "NONE_OPTION": "None"
"EVENTS": {
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
"ASSIGN_AGENT": "Assign to Agent",
"ASSIGN_TEAM": "Assign a Team",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Mute Conversation",
"SNOOZE_CONVERSATION": "Snooze Conversation",
"RESOLVE_CONVERSATION": "Resolve Conversation",
"SEND_WEBHOOK_EVENT": "Send Webhook Event",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "Change Priority",
"ADD_SLA": "Add SLA"
},
"ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains",
"EMAIL": "Email",
"INBOX": "Inbox",
"CONVERSATION_LANGUAGE": "Conversation Language",
"PHONE_NUMBER": "Phone Number",
"STATUS": "Status",
"BROWSER_LANGUAGE": "Browser Language",
"MAIL_SUBJECT": "Email Subject",
"COUNTRY_NAME": "Country",
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
"PRIORITY": "Priority"
}
} }
} }
@@ -544,9 +544,6 @@
"WROTE": "wrote", "WROTE": "wrote",
"YOU": "You", "YOU": "You",
"SAVE": "Save note", "SAVE": "Save note",
"EXPAND": "Expand",
"COLLAPSE": "Collapse",
"NO_NOTES": "No notes, you can add notes from the contact details page.",
"EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above." "EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above."
} }
}, },
@@ -32,12 +32,10 @@
"LOADING_CONVERSATIONS": "Loading Conversations", "LOADING_CONVERSATIONS": "Loading Conversations",
"CANNOT_REPLY": "You cannot reply due to", "CANNOT_REPLY": "You cannot reply due to",
"24_HOURS_WINDOW": "24 hour message window restriction", "24_HOURS_WINDOW": "24 hour message window restriction",
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?", "NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me", "ASSIGN_TO_ME": "Assign to me",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to", "TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You wont be able to send messages from this conversation anymore.",
"REPLYING_TO": "You are replying to:", "REPLYING_TO": "You are replying to:",
"REMOVE_SELECTION": "Remove Selection", "REMOVE_SELECTION": "Remove Selection",
"DOWNLOAD": "Download", "DOWNLOAD": "Download",
@@ -295,7 +293,6 @@
"CONVERSATION_ACTIONS": "Conversation Actions", "CONVERSATION_ACTIONS": "Conversation Actions",
"CONVERSATION_LABELS": "Conversation Labels", "CONVERSATION_LABELS": "Conversation Labels",
"CONVERSATION_INFO": "Conversation Information", "CONVERSATION_INFO": "Conversation Information",
"CONTACT_NOTES": "Contact Notes",
"CONTACT_ATTRIBUTES": "Contact Attributes", "CONTACT_ATTRIBUTES": "Contact Attributes",
"PREVIOUS_CONVERSATION": "Previous Conversations", "PREVIOUS_CONVERSATION": "Previous Conversations",
"MACROS": "Macros", "MACROS": "Macros",
@@ -1,11 +1,5 @@
{ {
"GENERAL_SETTINGS": { "GENERAL_SETTINGS": {
"LIMIT_MESSAGES": {
"CONVERSATION": "You have exceeded the conversation limit. Hacker plan allows only 500 conversations.",
"INBOXES": "You have exceeded the inbox limit. Hacker plan only supports website live-chat. Additional inboxes like email, WhatsApp etc. require a paid plan.",
"AGENTS": "You have exceeded the agent limit. Hacker plan allows only 2 agents.",
"NON_ADMIN": "Please contact your administrator to upgrade the plan and continue using all features."
},
"TITLE": "Account settings", "TITLE": "Account settings",
"SUBMIT": "Update settings", "SUBMIT": "Update settings",
"BACK": "Back", "BACK": "Back",
@@ -14,26 +8,6 @@
"ERROR": "Could not update settings, try again!", "ERROR": "Could not update settings, try again!",
"SUCCESS": "Successfully updated account settings" "SUCCESS": "Successfully updated account settings"
}, },
"ACCOUNT_DELETE_SECTION": {
"TITLE": "Delete your Account",
"NOTE": "Once you delete your account, all your data will be deleted.",
"BUTTON_TEXT": "Delete Your Account",
"CONFIRM": {
"TITLE": "Delete Account",
"MESSAGE": "Deleting your Account is irreversible. Enter your account name below to confirm you want to permanently delete it.",
"BUTTON_TEXT": "Delete",
"DISMISS": "Cancel",
"PLACE_HOLDER": "Please type {accountName} to confirm"
},
"SUCCESS": "Account marked for deletion",
"FAILURE": "Could not delete account, try again!",
"SCHEDULED_DELETION": {
"TITLE": "Account Scheduled for Deletion",
"MESSAGE_MANUAL": "This account is scheduled for deletion on {deletionDate}. This was requested by an administrator. You can cancel the deletion before this date.",
"MESSAGE_INACTIVITY": "This account is scheduled for deletion on {deletionDate} due to account inactivity. You can cancel the deletion before this date.",
"CLEAR_BUTTON": "Cancel Scheduled Deletion"
}
},
"FORM": { "FORM": {
"ERROR": "Please fix form errors", "ERROR": "Please fix form errors",
"GENERAL_SECTION": { "GENERAL_SECTION": {
@@ -77,7 +51,6 @@
"UPDATE_CHATWOOT": "An update {latestChatwootVersion} for Chatwoot is available. Please update your instance.", "UPDATE_CHATWOOT": "An update {latestChatwootVersion} for Chatwoot is available. Please update your instance.",
"LEARN_MORE": "Learn more", "LEARN_MORE": "Learn more",
"PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot", "PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot",
"UPGRADE": "Upgrade to continue using Chatwoot",
"LIMITS_UPGRADE": "Your account has exceeded the usage limits, please upgrade your plan to continue using Chatwoot", "LIMITS_UPGRADE": "Your account has exceeded the usage limits, please upgrade your plan to continue using Chatwoot",
"OPEN_BILLING": "Open billing" "OPEN_BILLING": "Open billing"
}, },
@@ -696,8 +696,7 @@
"SLUG": { "SLUG": {
"LABEL": "Slug", "LABEL": "Slug",
"PLACEHOLDER": "user-guide", "PLACEHOLDER": "user-guide",
"ERROR": "Slug is required", "ERROR": "Slug is required"
"FORMAT_ERROR": "Please enter a valid slug, for eg: user-guide"
} }
}, },
"PORTAL_SETTINGS": { "PORTAL_SETTINGS": {
@@ -43,17 +43,7 @@
"INBOX_NAME": "Inbox Name", "INBOX_NAME": "Inbox Name",
"ADD_NAME": "Add a name for your inbox", "ADD_NAME": "Add a name for your inbox",
"PICK_NAME": "Pick a Name for your Inbox", "PICK_NAME": "Pick a Name for your Inbox",
"PICK_A_VALUE": "Pick a value", "PICK_A_VALUE": "Pick a value"
"CREATE_INBOX": "Create Inbox"
},
"INSTAGRAM": {
"CONTINUE_WITH_INSTAGRAM": "Continue with Instagram",
"CONNECT_YOUR_INSTAGRAM_PROFILE": "Connect your Instagram Profile",
"HELP": "To add your Instagram profile as a channel, you need to authenticate your Instagram Profile by clicking on 'Continue with Instagram' ",
"ERROR_MESSAGE": "There was an error connecting to Instagram, please try again",
"ERROR_AUTH": "There was an error connecting to Instagram, please try again",
"NEW_INBOX_SUGGESTION": "This Instagram account was previously linked to a different inbox and has now been migrated here. All new messages will appear here. The old inbox will no longer be able to send or receive messages for this account.",
"DUPLICATE_INBOX_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. You wont be able to send/receive Instagram messages from this inbox anymore."
}, },
"TWITTER": { "TWITTER": {
"HELP": "To add your Twitter profile as a channel, you need to authenticate your Twitter Profile by clicking on 'Sign in with Twitter' ", "HELP": "To add your Twitter profile as a channel, you need to authenticate your Twitter Profile by clicking on 'Sign in with Twitter' ",
@@ -763,8 +753,7 @@
"EMAIL": "Email", "EMAIL": "Email",
"TELEGRAM": "Telegram", "TELEGRAM": "Telegram",
"LINE": "Line", "LINE": "Line",
"API": "API Channel", "API": "API Channel"
"INSTAGRAM": "Instagram"
} }
} }
} }
@@ -329,34 +329,11 @@
"HEADER_KNOW_MORE": "Know more", "HEADER_KNOW_MORE": "Know more",
"COPILOT": { "COPILOT": {
"SEND_MESSAGE": "Send message...", "SEND_MESSAGE": "Send message...",
"EMPTY_MESSAGE": "There was an error generating the response. Please try again.",
"LOADER": "Captain is thinking", "LOADER": "Captain is thinking",
"YOU": "You", "YOU": "You",
"USE": "Use this", "USE": "Use this",
"RESET": "Reset", "RESET": "Reset",
"SELECT_ASSISTANT": "Select Assistant", "SELECT_ASSISTANT": "Select Assistant"
"PROMPTS": {
"SUMMARIZE": {
"LABEL": "Summarize this conversation",
"CONTENT": "Summarize the key points discussed between the customer and the support agent, including the customer's concerns, questions, and the solutions or responses provided by the support agent"
},
"SUGGEST": {
"LABEL": "Suggest an answer",
"CONTENT": "Analyze the customer's inquiry, and draft a response that effectively addresses their concerns or questions. Ensure the reply is clear, concise, and provides helpful information."
},
"RATE": {
"LABEL": "Rate this conversation",
"CONTENT": "Review the conversation to see how well it meets the customer's needs. Share a rating out of 5 based on tone, clarity, and effectiveness."
}
}
},
"PLAYGROUND": {
"USER": "You",
"ASSISTANT": "Assistant",
"MESSAGE_PLACEHOLDER": "Type your message...",
"HEADER": "Playground",
"DESCRIPTION": "Use this playground to send messages to your assistant and check if it responds accurately, quickly, and in the tone you expect.",
"CREDIT_NOTE": "Messages sent here will count toward your Captain credits."
}, },
"PAYWALL": { "PAYWALL": {
"TITLE": "Upgrade to use Captain AI", "TITLE": "Upgrade to use Captain AI",
@@ -396,45 +373,21 @@
"ERROR_MESSAGE": "There was an error creating the assistant, please try again." "ERROR_MESSAGE": "There was an error creating the assistant, please try again."
}, },
"FORM": { "FORM": {
"UPDATE": "Update",
"SECTIONS": {
"BASIC_INFO": "Basic Information",
"SYSTEM_MESSAGES": "System Messages",
"INSTRUCTIONS": "Instructions",
"FEATURES": "Features",
"TOOLS": "Tools "
},
"NAME": { "NAME": {
"LABEL": "Name", "LABEL": "Assistant Name",
"PLACEHOLDER": "Enter assistant name", "PLACEHOLDER": "Enter a name for the assistant",
"ERROR": "The name is required" "ERROR": "Please provide a name for the assistant"
}, },
"DESCRIPTION": { "DESCRIPTION": {
"LABEL": "Description", "LABEL": "Assistant Description",
"PLACEHOLDER": "Enter assistant description", "PLACEHOLDER": "Describe how and where this assistant will be used",
"ERROR": "The description is required" "ERROR": "A description is required"
}, },
"PRODUCT_NAME": { "PRODUCT_NAME": {
"LABEL": "Product Name", "LABEL": "Product Name",
"PLACEHOLDER": "Enter product name", "PLACEHOLDER": "Enter the name of the product this assistant is designed for",
"ERROR": "The product name is required" "ERROR": "The product name is required"
}, },
"WELCOME_MESSAGE": {
"LABEL": "Welcome Message",
"PLACEHOLDER": "Enter welcome message"
},
"HANDOFF_MESSAGE": {
"LABEL": "Handoff Message",
"PLACEHOLDER": "Enter handoff message"
},
"RESOLUTION_MESSAGE": {
"LABEL": "Resolution Message",
"PLACEHOLDER": "Enter resolution message"
},
"INSTRUCTIONS": {
"LABEL": "Instructions",
"PLACEHOLDER": "Enter instructions for the assistant"
},
"FEATURES": { "FEATURES": {
"TITLE": "Features", "TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations", "ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
@@ -444,8 +397,7 @@
"EDIT": { "EDIT": {
"TITLE": "Update the assistant", "TITLE": "Update the assistant",
"SUCCESS_MESSAGE": "The assistant has been successfully updated", "SUCCESS_MESSAGE": "The assistant has been successfully updated",
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again."
"NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
@@ -83,22 +83,6 @@
"ACTION_PARAMETERS_REQUIRED": "Action parameters are required", "ACTION_PARAMETERS_REQUIRED": "Action parameters are required",
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
},
"ACTIONS": {
"ASSIGN_TEAM": "Assign a Team",
"ASSIGN_AGENT": "Assign an Agent",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Mute Conversation",
"SNOOZE_CONVERSATION": "Snooze Conversation",
"RESOLVE_CONVERSATION": "Resolve Conversation",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event"
} }
} }
} }
@@ -387,8 +387,7 @@
"LABEL": "Company Name", "LABEL": "Company Name",
"PLACEHOLDER": "Wayne Enterprises" "PLACEHOLDER": "Wayne Enterprises"
}, },
"SUBMIT": "Submit", "SUBMIT": "Submit"
"CANCEL": "Cancel"
} }
}, },
"KEYBOARD_SHORTCUTS": { "KEYBOARD_SHORTCUTS": {
@@ -2,13 +2,23 @@
"AGENT_BOTS": { "AGENT_BOTS": {
"HEADER": "Bots", "HEADER": "Bots",
"LOADING_EDITOR": "Loading editor...", "LOADING_EDITOR": "Loading editor...",
"DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try. You can manage your bots from this page or create new ones using the 'Add Bot' button.", "DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.You can manage your bots from this page or create new ones using the 'Configure new bot' button.",
"LEARN_MORE": "Learn about agent bots", "LEARN_MORE": "Learn about agent bots",
"GLOBAL_BOT": "System bot", "CSML_BOT_EDITOR": {
"GLOBAL_BOT_BADGE": "System", "NAME": {
"AVATAR": { "LABEL": "Bot name",
"SUCCESS_DELETE": "Bot avatar deleted successfully", "PLACEHOLDER": "Name your bot.",
"ERROR_DELETE": "Error deleting bot avatar, please try again" "ERROR": "Bot name is required."
},
"DESCRIPTION": {
"LABEL": "Bot description",
"PLACEHOLDER": "What does this bot do?"
},
"BOT_CONFIG": {
"ERROR": "Please enter your CSML bot configuration above.",
"API_ERROR": "Your CSML configuration is invalid. Please fix it and try again."
},
"SUBMIT": "Validate and save"
}, },
"BOT_CONFIGURATION": { "BOT_CONFIGURATION": {
"TITLE": "Select an agent bot", "TITLE": "Select an agent bot",
@@ -22,7 +32,7 @@
"SELECT_PLACEHOLDER": "Select bot" "SELECT_PLACEHOLDER": "Select bot"
}, },
"ADD": { "ADD": {
"TITLE": "Add Bot", "TITLE": "Configure new bot",
"CANCEL_BUTTON_TEXT": "Отмени", "CANCEL_BUTTON_TEXT": "Отмени",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot added successfully.", "SUCCESS_MESSAGE": "Bot added successfully.",
@@ -30,22 +40,16 @@
} }
}, },
"LIST": { "LIST": {
"404": "No bots found. You can create a bot by clicking the 'Add Bot' button.", "404": "No bots found. You can create a bot by clicking the 'Configure new bot' button",
"LOADING": "Fetching bots...", "LOADING": "Fetching bots...",
"TABLE_HEADER": { "TYPE": "Bot type"
"DETAILS": "Bot Details",
"URL": "Webhook URL"
}
}, },
"DELETE": { "DELETE": {
"BUTTON_TEXT": "Изтрий", "BUTTON_TEXT": "Изтрий",
"TITLE": "Delete bot", "TITLE": "Delete bot",
"CONFIRM": { "SUBMIT": "Изтрий",
"TITLE": "Потвърди изтриването", "CANCEL_BUTTON_TEXT": "Отмени",
"MESSAGE": "Are you sure you want to delete {name}?", "DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible.",
"YES": "Да, изтрий",
"NO": "Не, запази"
},
"API": { "API": {
"SUCCESS_MESSAGE": "Bot deleted successfully.", "SUCCESS_MESSAGE": "Bot deleted successfully.",
"ERROR_MESSAGE": "Could not delete bot. Please try again." "ERROR_MESSAGE": "Could not delete bot. Please try again."
@@ -53,44 +57,17 @@
}, },
"EDIT": { "EDIT": {
"BUTTON_TEXT": "Редактирай", "BUTTON_TEXT": "Редактирай",
"LOADING": "Fetching bots...",
"TITLE": "Edit bot", "TITLE": "Edit bot",
"CANCEL_BUTTON_TEXT": "Отмени",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot updated successfully.", "SUCCESS_MESSAGE": "Bot updated successfully.",
"ERROR_MESSAGE": "Could not update bot. Please try again." "ERROR_MESSAGE": "Could not update bot. Please try again."
} }
}, },
"FORM": {
"AVATAR": {
"LABEL": "Bot avatar"
},
"NAME": {
"LABEL": "Bot name",
"PLACEHOLDER": "Enter bot name",
"REQUIRED": "Bot name is required"
},
"DESCRIPTION": {
"LABEL": "Описание",
"PLACEHOLDER": "What does this bot do?"
},
"WEBHOOK_URL": {
"LABEL": "Webhook URL",
"PLACEHOLDER": "https://example.com/webhook",
"REQUIRED": "Webhook URL is required"
},
"ERRORS": {
"NAME": "Bot name is required",
"URL": "Webhook URL is required",
"VALID_URL": "Please enter a valid URL starting with http:// or https://"
},
"CANCEL": "Отмени",
"CREATE": "Create Bot",
"UPDATE": "Update Bot"
},
"WEBHOOK": {
"DESCRIPTION": "Configure a webhook bot to integrate with your custom services. The bot will receive and process events from conversations and can respond to them."
},
"TYPES": { "TYPES": {
"WEBHOOK": "Webhook bot" "WEBHOOK": "Webhook bot",
"CSML": "CSML bot"
} }
} }
} }
@@ -126,44 +126,6 @@
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
}, },
"NONE_OPTION": "Нито един", "NONE_OPTION": "Нито един"
"EVENTS": {
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message Created",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
"ASSIGN_AGENT": "Assign to Agent",
"ASSIGN_TEAM": "Assign a Team",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Заглушаване на разговора",
"SNOOZE_CONVERSATION": "Snooze Conversation",
"RESOLVE_CONVERSATION": "Resolve Conversation",
"SEND_WEBHOOK_EVENT": "Send Webhook Event",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "Change Priority",
"ADD_SLA": "Add SLA"
},
"ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains",
"EMAIL": "Email",
"INBOX": "Входяща кутия",
"CONVERSATION_LANGUAGE": "Conversation Language",
"PHONE_NUMBER": "Phone Number",
"STATUS": "Статус",
"BROWSER_LANGUAGE": "Език на браузъра",
"MAIL_SUBJECT": "Email Subject",
"COUNTRY_NAME": "Държава",
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
"PRIORITY": "Priority"
}
} }
} }
@@ -544,9 +544,6 @@
"WROTE": "wrote", "WROTE": "wrote",
"YOU": "You", "YOU": "You",
"SAVE": "Save note", "SAVE": "Save note",
"EXPAND": "Expand",
"COLLAPSE": "Collapse",
"NO_NOTES": "No notes, you can add notes from the contact details page.",
"EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above." "EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above."
} }
}, },
@@ -32,12 +32,10 @@
"LOADING_CONVERSATIONS": "Loading Conversations", "LOADING_CONVERSATIONS": "Loading Conversations",
"CANNOT_REPLY": "You cannot reply due to", "CANNOT_REPLY": "You cannot reply due to",
"24_HOURS_WINDOW": "24 hour message window restriction", "24_HOURS_WINDOW": "24 hour message window restriction",
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?", "NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me", "ASSIGN_TO_ME": "Assign to me",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to", "TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You wont be able to send messages from this conversation anymore.",
"REPLYING_TO": "You are replying to:", "REPLYING_TO": "You are replying to:",
"REMOVE_SELECTION": "Remove Selection", "REMOVE_SELECTION": "Remove Selection",
"DOWNLOAD": "Download", "DOWNLOAD": "Download",
@@ -295,7 +293,6 @@
"CONVERSATION_ACTIONS": "Conversation Actions", "CONVERSATION_ACTIONS": "Conversation Actions",
"CONVERSATION_LABELS": "Етикети на разговора", "CONVERSATION_LABELS": "Етикети на разговора",
"CONVERSATION_INFO": "Conversation Information", "CONVERSATION_INFO": "Conversation Information",
"CONTACT_NOTES": "Contact Notes",
"CONTACT_ATTRIBUTES": "Contact Attributes", "CONTACT_ATTRIBUTES": "Contact Attributes",
"PREVIOUS_CONVERSATION": "Предишни разговори", "PREVIOUS_CONVERSATION": "Предишни разговори",
"MACROS": "Macros", "MACROS": "Macros",
@@ -1,11 +1,5 @@
{ {
"GENERAL_SETTINGS": { "GENERAL_SETTINGS": {
"LIMIT_MESSAGES": {
"CONVERSATION": "You have exceeded the conversation limit. Hacker plan allows only 500 conversations.",
"INBOXES": "You have exceeded the inbox limit. Hacker plan only supports website live-chat. Additional inboxes like email, WhatsApp etc. require a paid plan.",
"AGENTS": "You have exceeded the agent limit. Hacker plan allows only 2 agents.",
"NON_ADMIN": "Please contact your administrator to upgrade the plan and continue using all features."
},
"TITLE": "Account settings", "TITLE": "Account settings",
"SUBMIT": "Update settings", "SUBMIT": "Update settings",
"BACK": "Back", "BACK": "Back",
@@ -14,26 +8,6 @@
"ERROR": "Could not update settings, try again!", "ERROR": "Could not update settings, try again!",
"SUCCESS": "Successfully updated account settings" "SUCCESS": "Successfully updated account settings"
}, },
"ACCOUNT_DELETE_SECTION": {
"TITLE": "Delete your Account",
"NOTE": "Once you delete your account, all your data will be deleted.",
"BUTTON_TEXT": "Delete Your Account",
"CONFIRM": {
"TITLE": "Delete Account",
"MESSAGE": "Deleting your Account is irreversible. Enter your account name below to confirm you want to permanently delete it.",
"BUTTON_TEXT": "Изтрий",
"DISMISS": "Отмени",
"PLACE_HOLDER": "Please type {accountName} to confirm"
},
"SUCCESS": "Account marked for deletion",
"FAILURE": "Could not delete account, try again!",
"SCHEDULED_DELETION": {
"TITLE": "Account Scheduled for Deletion",
"MESSAGE_MANUAL": "This account is scheduled for deletion on {deletionDate}. This was requested by an administrator. You can cancel the deletion before this date.",
"MESSAGE_INACTIVITY": "This account is scheduled for deletion on {deletionDate} due to account inactivity. You can cancel the deletion before this date.",
"CLEAR_BUTTON": "Cancel Scheduled Deletion"
}
},
"FORM": { "FORM": {
"ERROR": "Please fix form errors", "ERROR": "Please fix form errors",
"GENERAL_SECTION": { "GENERAL_SECTION": {
@@ -77,7 +51,6 @@
"UPDATE_CHATWOOT": "An update {latestChatwootVersion} for Chatwoot is available. Please update your instance.", "UPDATE_CHATWOOT": "An update {latestChatwootVersion} for Chatwoot is available. Please update your instance.",
"LEARN_MORE": "Learn more", "LEARN_MORE": "Learn more",
"PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot", "PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot",
"UPGRADE": "Upgrade to continue using Chatwoot",
"LIMITS_UPGRADE": "Your account has exceeded the usage limits, please upgrade your plan to continue using Chatwoot", "LIMITS_UPGRADE": "Your account has exceeded the usage limits, please upgrade your plan to continue using Chatwoot",
"OPEN_BILLING": "Open billing" "OPEN_BILLING": "Open billing"
}, },
@@ -696,8 +696,7 @@
"SLUG": { "SLUG": {
"LABEL": "Slug", "LABEL": "Slug",
"PLACEHOLDER": "user-guide", "PLACEHOLDER": "user-guide",
"ERROR": "Slug is required", "ERROR": "Slug is required"
"FORMAT_ERROR": "Please enter a valid slug, for eg: user-guide"
} }
}, },
"PORTAL_SETTINGS": { "PORTAL_SETTINGS": {
@@ -43,17 +43,7 @@
"INBOX_NAME": "Inbox Name", "INBOX_NAME": "Inbox Name",
"ADD_NAME": "Add a name for your inbox", "ADD_NAME": "Add a name for your inbox",
"PICK_NAME": "Pick a Name for your Inbox", "PICK_NAME": "Pick a Name for your Inbox",
"PICK_A_VALUE": "Pick a value", "PICK_A_VALUE": "Pick a value"
"CREATE_INBOX": "Create Inbox"
},
"INSTAGRAM": {
"CONTINUE_WITH_INSTAGRAM": "Continue with Instagram",
"CONNECT_YOUR_INSTAGRAM_PROFILE": "Connect your Instagram Profile",
"HELP": "To add your Instagram profile as a channel, you need to authenticate your Instagram Profile by clicking on 'Continue with Instagram' ",
"ERROR_MESSAGE": "There was an error connecting to Instagram, please try again",
"ERROR_AUTH": "There was an error connecting to Instagram, please try again",
"NEW_INBOX_SUGGESTION": "This Instagram account was previously linked to a different inbox and has now been migrated here. All new messages will appear here. The old inbox will no longer be able to send or receive messages for this account.",
"DUPLICATE_INBOX_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. You wont be able to send/receive Instagram messages from this inbox anymore."
}, },
"TWITTER": { "TWITTER": {
"HELP": "To add your Twitter profile as a channel, you need to authenticate your Twitter Profile by clicking on 'Sign in with Twitter' ", "HELP": "To add your Twitter profile as a channel, you need to authenticate your Twitter Profile by clicking on 'Sign in with Twitter' ",
@@ -763,8 +753,7 @@
"EMAIL": "Имейл", "EMAIL": "Имейл",
"TELEGRAM": "Telegram", "TELEGRAM": "Telegram",
"LINE": "Line", "LINE": "Line",
"API": "API Channel", "API": "API Channel"
"INSTAGRAM": "Instagram"
} }
} }
} }
@@ -329,34 +329,11 @@
"HEADER_KNOW_MORE": "Know more", "HEADER_KNOW_MORE": "Know more",
"COPILOT": { "COPILOT": {
"SEND_MESSAGE": "Изпрати съобщение...", "SEND_MESSAGE": "Изпрати съобщение...",
"EMPTY_MESSAGE": "There was an error generating the response. Please try again.",
"LOADER": "Captain is thinking", "LOADER": "Captain is thinking",
"YOU": "You", "YOU": "You",
"USE": "Use this", "USE": "Use this",
"RESET": "Reset", "RESET": "Reset",
"SELECT_ASSISTANT": "Select Assistant", "SELECT_ASSISTANT": "Select Assistant"
"PROMPTS": {
"SUMMARIZE": {
"LABEL": "Summarize this conversation",
"CONTENT": "Summarize the key points discussed between the customer and the support agent, including the customer's concerns, questions, and the solutions or responses provided by the support agent"
},
"SUGGEST": {
"LABEL": "Suggest an answer",
"CONTENT": "Analyze the customer's inquiry, and draft a response that effectively addresses their concerns or questions. Ensure the reply is clear, concise, and provides helpful information."
},
"RATE": {
"LABEL": "Rate this conversation",
"CONTENT": "Review the conversation to see how well it meets the customer's needs. Share a rating out of 5 based on tone, clarity, and effectiveness."
}
}
},
"PLAYGROUND": {
"USER": "You",
"ASSISTANT": "Assistant",
"MESSAGE_PLACEHOLDER": "Напишете вашето съобщение...",
"HEADER": "Playground",
"DESCRIPTION": "Use this playground to send messages to your assistant and check if it responds accurately, quickly, and in the tone you expect.",
"CREDIT_NOTE": "Messages sent here will count toward your Captain credits."
}, },
"PAYWALL": { "PAYWALL": {
"TITLE": "Upgrade to use Captain AI", "TITLE": "Upgrade to use Captain AI",
@@ -396,45 +373,21 @@
"ERROR_MESSAGE": "There was an error creating the assistant, please try again." "ERROR_MESSAGE": "There was an error creating the assistant, please try again."
}, },
"FORM": { "FORM": {
"UPDATE": "Update",
"SECTIONS": {
"BASIC_INFO": "Basic Information",
"SYSTEM_MESSAGES": "System Messages",
"INSTRUCTIONS": "Instructions",
"FEATURES": "Features",
"TOOLS": "Tools "
},
"NAME": { "NAME": {
"LABEL": "Име", "LABEL": "Assistant Name",
"PLACEHOLDER": "Enter assistant name", "PLACEHOLDER": "Enter a name for the assistant",
"ERROR": "The name is required" "ERROR": "Please provide a name for the assistant"
}, },
"DESCRIPTION": { "DESCRIPTION": {
"LABEL": "Описание", "LABEL": "Assistant Description",
"PLACEHOLDER": "Enter assistant description", "PLACEHOLDER": "Describe how and where this assistant will be used",
"ERROR": "The description is required" "ERROR": "A description is required"
}, },
"PRODUCT_NAME": { "PRODUCT_NAME": {
"LABEL": "Product Name", "LABEL": "Product Name",
"PLACEHOLDER": "Enter product name", "PLACEHOLDER": "Enter the name of the product this assistant is designed for",
"ERROR": "The product name is required" "ERROR": "The product name is required"
}, },
"WELCOME_MESSAGE": {
"LABEL": "Welcome Message",
"PLACEHOLDER": "Enter welcome message"
},
"HANDOFF_MESSAGE": {
"LABEL": "Handoff Message",
"PLACEHOLDER": "Enter handoff message"
},
"RESOLUTION_MESSAGE": {
"LABEL": "Resolution Message",
"PLACEHOLDER": "Enter resolution message"
},
"INSTRUCTIONS": {
"LABEL": "Instructions",
"PLACEHOLDER": "Enter instructions for the assistant"
},
"FEATURES": { "FEATURES": {
"TITLE": "Features", "TITLE": "Features",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations", "ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
@@ -444,8 +397,7 @@
"EDIT": { "EDIT": {
"TITLE": "Update the assistant", "TITLE": "Update the assistant",
"SUCCESS_MESSAGE": "The assistant has been successfully updated", "SUCCESS_MESSAGE": "The assistant has been successfully updated",
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again."
"NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
@@ -83,22 +83,6 @@
"ACTION_PARAMETERS_REQUIRED": "Action parameters are required", "ACTION_PARAMETERS_REQUIRED": "Action parameters are required",
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
},
"ACTIONS": {
"ASSIGN_TEAM": "Assign a Team",
"ASSIGN_AGENT": "Assign an Agent",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Заглушаване на разговора",
"SNOOZE_CONVERSATION": "Snooze Conversation",
"RESOLVE_CONVERSATION": "Resolve Conversation",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event"
} }
} }
} }
@@ -387,8 +387,7 @@
"LABEL": "Име на фирма", "LABEL": "Име на фирма",
"PLACEHOLDER": "Wayne Enterprises" "PLACEHOLDER": "Wayne Enterprises"
}, },
"SUBMIT": "Изпращане", "SUBMIT": "Изпращане"
"CANCEL": "Отмени"
} }
}, },
"KEYBOARD_SHORTCUTS": { "KEYBOARD_SHORTCUTS": {
@@ -2,13 +2,23 @@
"AGENT_BOTS": { "AGENT_BOTS": {
"HEADER": "Bots", "HEADER": "Bots",
"LOADING_EDITOR": "S'està carregant l'editor...", "LOADING_EDITOR": "S'està carregant l'editor...",
"DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try. You can manage your bots from this page or create new ones using the 'Add Bot' button.", "DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.You can manage your bots from this page or create new ones using the 'Configure new bot' button.",
"LEARN_MORE": "Learn about agent bots", "LEARN_MORE": "Learn about agent bots",
"GLOBAL_BOT": "System bot", "CSML_BOT_EDITOR": {
"GLOBAL_BOT_BADGE": "Sistema", "NAME": {
"AVATAR": { "LABEL": "Nom del bot",
"SUCCESS_DELETE": "Bot avatar deleted successfully", "PLACEHOLDER": "Anomena el teu bot.",
"ERROR_DELETE": "Error deleting bot avatar, please try again" "ERROR": "El nom del bot és obligatori."
},
"DESCRIPTION": {
"LABEL": "Descripció del bot",
"PLACEHOLDER": "Què fa aquest bot?"
},
"BOT_CONFIG": {
"ERROR": "Introdueix la configuració del bot CSML més amunt.",
"API_ERROR": "La vostra configuració CSML no és vàlida. Arregla-ho i torna-ho a provar."
},
"SUBMIT": "Valida i desa"
}, },
"BOT_CONFIGURATION": { "BOT_CONFIGURATION": {
"TITLE": "Selecciona un bot d'agent", "TITLE": "Selecciona un bot d'agent",
@@ -22,7 +32,7 @@
"SELECT_PLACEHOLDER": "Selecciona el bot" "SELECT_PLACEHOLDER": "Selecciona el bot"
}, },
"ADD": { "ADD": {
"TITLE": "Add Bot", "TITLE": "Configura el nou bot",
"CANCEL_BUTTON_TEXT": "Cancel·la", "CANCEL_BUTTON_TEXT": "Cancel·la",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot afegit correctament.", "SUCCESS_MESSAGE": "Bot afegit correctament.",
@@ -30,22 +40,16 @@
} }
}, },
"LIST": { "LIST": {
"404": "No bots found. You can create a bot by clicking the 'Add Bot' button.", "404": "No s'han trobat bots. Pots crear un bot fent clic al botó \"Configura un bot nou\" ↗",
"LOADING": "S'estan obtenint bots...", "LOADING": "S'estan obtenint bots...",
"TABLE_HEADER": { "TYPE": "Tipus de bot"
"DETAILS": "Bot Details",
"URL": "URL del webhook"
}
}, },
"DELETE": { "DELETE": {
"BUTTON_TEXT": "Esborrar", "BUTTON_TEXT": "Esborrar",
"TITLE": "Suprimeix el bot", "TITLE": "Suprimeix el bot",
"CONFIRM": { "SUBMIT": "Esborrar",
"TITLE": "Confirma l'esborrat", "CANCEL_BUTTON_TEXT": "Cancel·la",
"MESSAGE": "Are you sure you want to delete {name}?", "DESCRIPTION": "Estàs segur que vols suprimir aquest bot? Aquesta acció és irreversible.",
"YES": "Si, esborra",
"NO": "No, segueix"
},
"API": { "API": {
"SUCCESS_MESSAGE": "S'ha esborrat el bot correctament.", "SUCCESS_MESSAGE": "S'ha esborrat el bot correctament.",
"ERROR_MESSAGE": "No s'ha pogut eliminar el bot. Torneu-ho a provar més endavant." "ERROR_MESSAGE": "No s'ha pogut eliminar el bot. Torneu-ho a provar més endavant."
@@ -53,44 +57,17 @@
}, },
"EDIT": { "EDIT": {
"BUTTON_TEXT": "Edita", "BUTTON_TEXT": "Edita",
"LOADING": "S'estan obtenint bots...",
"TITLE": "Edita el bot", "TITLE": "Edita el bot",
"CANCEL_BUTTON_TEXT": "Cancel·la",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot actualitzat correctament.", "SUCCESS_MESSAGE": "Bot actualitzat correctament.",
"ERROR_MESSAGE": "No s'ha pogut actualitzar el bot. Torneu-ho a provar." "ERROR_MESSAGE": "No s'ha pogut actualitzar el bot. Torneu-ho a provar."
} }
}, },
"FORM": {
"AVATAR": {
"LABEL": "Bot avatar"
},
"NAME": {
"LABEL": "Nom del bot",
"PLACEHOLDER": "Enter bot name",
"REQUIRED": "El nom del bot és obligatori"
},
"DESCRIPTION": {
"LABEL": "Descripció",
"PLACEHOLDER": "Què fa aquest bot?"
},
"WEBHOOK_URL": {
"LABEL": "URL del webhook",
"PLACEHOLDER": "https://example.com/webhook",
"REQUIRED": "Webhook URL is required"
},
"ERRORS": {
"NAME": "El nom del bot és obligatori",
"URL": "Webhook URL is required",
"VALID_URL": "Please enter a valid URL starting with http:// or https://"
},
"CANCEL": "Cancel·la",
"CREATE": "Create Bot",
"UPDATE": "Update Bot"
},
"WEBHOOK": {
"DESCRIPTION": "Configure a webhook bot to integrate with your custom services. The bot will receive and process events from conversations and can respond to them."
},
"TYPES": { "TYPES": {
"WEBHOOK": "Webhook bot" "WEBHOOK": "Webhook bot",
"CSML": "CSML bot"
} }
} }
} }
@@ -126,44 +126,6 @@
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
}, },
"NONE_OPTION": "Ningú", "NONE_OPTION": "Ningú"
"EVENTS": {
"CONVERSATION_CREATED": "Conversa Creada",
"CONVERSATION_UPDATED": "Conversa Actualitzada",
"MESSAGE_CREATED": "Message Created",
"CONVERSATION_OPENED": "Conversation Opened"
},
"ACTIONS": {
"ASSIGN_AGENT": "Assign to Agent",
"ASSIGN_TEAM": "Assign a Team",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"SEND_EMAIL_TO_TEAM": "Send an Email to Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Silencia la conversa",
"SNOOZE_CONVERSATION": "Posposa la conversa",
"RESOLVE_CONVERSATION": "Resol la conversa",
"SEND_WEBHOOK_EVENT": "Send Webhook Event",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "Canvia la prioritat",
"ADD_SLA": "Afegeix SLA"
},
"ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains",
"EMAIL": "Correu electrònic",
"INBOX": "Safata d'entrada",
"CONVERSATION_LANGUAGE": "Conversation Language",
"PHONE_NUMBER": "Número de telèfon",
"STATUS": "Estat",
"BROWSER_LANGUAGE": "Idioma del navegador",
"MAIL_SUBJECT": "Email Subject",
"COUNTRY_NAME": "País",
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Cessionari",
"TEAM_NAME": "Equip",
"PRIORITY": "Prioritat"
}
} }
} }
@@ -544,9 +544,6 @@
"WROTE": "va escriure", "WROTE": "va escriure",
"YOU": "Tu", "YOU": "Tu",
"SAVE": "Save note", "SAVE": "Save note",
"EXPAND": "Expandeix",
"COLLAPSE": "Collapse",
"NO_NOTES": "No notes, you can add notes from the contact details page.",
"EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above." "EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above."
} }
}, },
@@ -32,12 +32,10 @@
"LOADING_CONVERSATIONS": "S'estan carregant les converses", "LOADING_CONVERSATIONS": "S'estan carregant les converses",
"CANNOT_REPLY": "No pots respondre degut a", "CANNOT_REPLY": "No pots respondre degut a",
"24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores", "24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores",
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Aquesta conversa no està assignada a tu. Vols assignar-te-la?", "NOT_ASSIGNED_TO_YOU": "Aquesta conversa no està assignada a tu. Vols assignar-te-la?",
"ASSIGN_TO_ME": "Assigna'm", "ASSIGN_TO_ME": "Assigna'm",
"TWILIO_WHATSAPP_CAN_REPLY": "Només pots respondre a aquesta conversa mitjançant una plantilla de missatge a causa de", "TWILIO_WHATSAPP_CAN_REPLY": "Només pots respondre a aquesta conversa mitjançant una plantilla de missatge a causa de",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You wont be able to send messages from this conversation anymore.",
"REPLYING_TO": "Estas responent a:", "REPLYING_TO": "Estas responent a:",
"REMOVE_SELECTION": "Elimina la selecció", "REMOVE_SELECTION": "Elimina la selecció",
"DOWNLOAD": "Descarrega", "DOWNLOAD": "Descarrega",
@@ -295,7 +293,6 @@
"CONVERSATION_ACTIONS": "Accions de conversa", "CONVERSATION_ACTIONS": "Accions de conversa",
"CONVERSATION_LABELS": "Etiquetes de converses", "CONVERSATION_LABELS": "Etiquetes de converses",
"CONVERSATION_INFO": "Informació de la conversa", "CONVERSATION_INFO": "Informació de la conversa",
"CONTACT_NOTES": "Contact Notes",
"CONTACT_ATTRIBUTES": "Atributs de contacte", "CONTACT_ATTRIBUTES": "Atributs de contacte",
"PREVIOUS_CONVERSATION": "Converses prèvies", "PREVIOUS_CONVERSATION": "Converses prèvies",
"MACROS": "Macros", "MACROS": "Macros",
@@ -1,11 +1,5 @@
{ {
"GENERAL_SETTINGS": { "GENERAL_SETTINGS": {
"LIMIT_MESSAGES": {
"CONVERSATION": "You have exceeded the conversation limit. Hacker plan allows only 500 conversations.",
"INBOXES": "You have exceeded the inbox limit. Hacker plan only supports website live-chat. Additional inboxes like email, WhatsApp etc. require a paid plan.",
"AGENTS": "You have exceeded the agent limit. Hacker plan allows only 2 agents.",
"NON_ADMIN": "Please contact your administrator to upgrade the plan and continue using all features."
},
"TITLE": "Configuració del compte", "TITLE": "Configuració del compte",
"SUBMIT": "Actualització de la configuració", "SUBMIT": "Actualització de la configuració",
"BACK": "Enrere", "BACK": "Enrere",
@@ -14,26 +8,6 @@
"ERROR": "No s'ha pogut actualitzar la configuració, torna-ho a provar!", "ERROR": "No s'ha pogut actualitzar la configuració, torna-ho a provar!",
"SUCCESS": "La configuració del compte s'ha actualitzat correctament" "SUCCESS": "La configuració del compte s'ha actualitzat correctament"
}, },
"ACCOUNT_DELETE_SECTION": {
"TITLE": "Delete your Account",
"NOTE": "Once you delete your account, all your data will be deleted.",
"BUTTON_TEXT": "Delete Your Account",
"CONFIRM": {
"TITLE": "Delete Account",
"MESSAGE": "Deleting your Account is irreversible. Enter your account name below to confirm you want to permanently delete it.",
"BUTTON_TEXT": "Esborrar",
"DISMISS": "Cancel·la",
"PLACE_HOLDER": "Escriu {accountName} per confirmar"
},
"SUCCESS": "Account marked for deletion",
"FAILURE": "Could not delete account, try again!",
"SCHEDULED_DELETION": {
"TITLE": "Account Scheduled for Deletion",
"MESSAGE_MANUAL": "This account is scheduled for deletion on {deletionDate}. This was requested by an administrator. You can cancel the deletion before this date.",
"MESSAGE_INACTIVITY": "This account is scheduled for deletion on {deletionDate} due to account inactivity. You can cancel the deletion before this date.",
"CLEAR_BUTTON": "Cancel Scheduled Deletion"
}
},
"FORM": { "FORM": {
"ERROR": "Corregiu els errors del formulari", "ERROR": "Corregiu els errors del formulari",
"GENERAL_SECTION": { "GENERAL_SECTION": {
@@ -77,7 +51,6 @@
"UPDATE_CHATWOOT": "L'actualització {latestChatwootVersion} per Chatwoot està disponible. Si us plau, actualitza l'instancia.", "UPDATE_CHATWOOT": "L'actualització {latestChatwootVersion} per Chatwoot està disponible. Si us plau, actualitza l'instancia.",
"LEARN_MORE": "Aprèn més", "LEARN_MORE": "Aprèn més",
"PAYMENT_PENDING": "El teu pagament està pendent. Actualitzeu la vostra informació de pagament per continuar utilitzant Chatwoot", "PAYMENT_PENDING": "El teu pagament està pendent. Actualitzeu la vostra informació de pagament per continuar utilitzant Chatwoot",
"UPGRADE": "Upgrade to continue using Chatwoot",
"LIMITS_UPGRADE": "El teu compte ha superat els límits d'ús, actualitza el pla per continuar utilitzant Chatwoot", "LIMITS_UPGRADE": "El teu compte ha superat els límits d'ús, actualitza el pla per continuar utilitzant Chatwoot",
"OPEN_BILLING": "Obrir facturació" "OPEN_BILLING": "Obrir facturació"
}, },
@@ -696,8 +696,7 @@
"SLUG": { "SLUG": {
"LABEL": "Slug", "LABEL": "Slug",
"PLACEHOLDER": "user-guide", "PLACEHOLDER": "user-guide",
"ERROR": "El slug és obligatori", "ERROR": "El slug és obligatori"
"FORMAT_ERROR": "Please enter a valid slug, for eg: user-guide"
} }
}, },
"PORTAL_SETTINGS": { "PORTAL_SETTINGS": {
@@ -43,17 +43,7 @@
"INBOX_NAME": "Nom de la safata d'entrada", "INBOX_NAME": "Nom de la safata d'entrada",
"ADD_NAME": "Afegeix un nom per a la safata d'entrada", "ADD_NAME": "Afegeix un nom per a la safata d'entrada",
"PICK_NAME": "Tria un nom per a la teva safata d'entrada", "PICK_NAME": "Tria un nom per a la teva safata d'entrada",
"PICK_A_VALUE": "Tria un valor", "PICK_A_VALUE": "Tria un valor"
"CREATE_INBOX": "Crear safata d'entrada"
},
"INSTAGRAM": {
"CONTINUE_WITH_INSTAGRAM": "Continue with Instagram",
"CONNECT_YOUR_INSTAGRAM_PROFILE": "Connect your Instagram Profile",
"HELP": "To add your Instagram profile as a channel, you need to authenticate your Instagram Profile by clicking on 'Continue with Instagram' ",
"ERROR_MESSAGE": "There was an error connecting to Instagram, please try again",
"ERROR_AUTH": "There was an error connecting to Instagram, please try again",
"NEW_INBOX_SUGGESTION": "This Instagram account was previously linked to a different inbox and has now been migrated here. All new messages will appear here. The old inbox will no longer be able to send or receive messages for this account.",
"DUPLICATE_INBOX_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. You wont be able to send/receive Instagram messages from this inbox anymore."
}, },
"TWITTER": { "TWITTER": {
"HELP": "Per afegir el teu perfil de Twitter com a canal, has d'autentificar el vostre perfil de Twitter fent clic a 'Inicieu la sessió amb Twitter' ", "HELP": "Per afegir el teu perfil de Twitter com a canal, has d'autentificar el vostre perfil de Twitter fent clic a 'Inicieu la sessió amb Twitter' ",
@@ -763,8 +753,7 @@
"EMAIL": "Correu electrònic", "EMAIL": "Correu electrònic",
"TELEGRAM": "Telegram", "TELEGRAM": "Telegram",
"LINE": "Line", "LINE": "Line",
"API": "Canal de l'API", "API": "Canal de l'API"
"INSTAGRAM": "Instagram"
} }
} }
} }
@@ -329,34 +329,11 @@
"HEADER_KNOW_MORE": "Know more", "HEADER_KNOW_MORE": "Know more",
"COPILOT": { "COPILOT": {
"SEND_MESSAGE": "Envia missatge...", "SEND_MESSAGE": "Envia missatge...",
"EMPTY_MESSAGE": "There was an error generating the response. Please try again.",
"LOADER": "Captain is thinking", "LOADER": "Captain is thinking",
"YOU": "Tu", "YOU": "Tu",
"USE": "Use this", "USE": "Use this",
"RESET": "Reset", "RESET": "Reset",
"SELECT_ASSISTANT": "Select Assistant", "SELECT_ASSISTANT": "Select Assistant"
"PROMPTS": {
"SUMMARIZE": {
"LABEL": "Summarize this conversation",
"CONTENT": "Summarize the key points discussed between the customer and the support agent, including the customer's concerns, questions, and the solutions or responses provided by the support agent"
},
"SUGGEST": {
"LABEL": "Suggest an answer",
"CONTENT": "Analyze the customer's inquiry, and draft a response that effectively addresses their concerns or questions. Ensure the reply is clear, concise, and provides helpful information."
},
"RATE": {
"LABEL": "Rate this conversation",
"CONTENT": "Review the conversation to see how well it meets the customer's needs. Share a rating out of 5 based on tone, clarity, and effectiveness."
}
}
},
"PLAYGROUND": {
"USER": "Tu",
"ASSISTANT": "Assistant",
"MESSAGE_PLACEHOLDER": "Escriu el missatge...",
"HEADER": "Playground",
"DESCRIPTION": "Use this playground to send messages to your assistant and check if it responds accurately, quickly, and in the tone you expect.",
"CREDIT_NOTE": "Messages sent here will count toward your Captain credits."
}, },
"PAYWALL": { "PAYWALL": {
"TITLE": "Upgrade to use Captain AI", "TITLE": "Upgrade to use Captain AI",
@@ -396,45 +373,21 @@
"ERROR_MESSAGE": "There was an error creating the assistant, please try again." "ERROR_MESSAGE": "There was an error creating the assistant, please try again."
}, },
"FORM": { "FORM": {
"UPDATE": "Actualitza",
"SECTIONS": {
"BASIC_INFO": "Basic Information",
"SYSTEM_MESSAGES": "System Messages",
"INSTRUCTIONS": "Instructions",
"FEATURES": "Característiques",
"TOOLS": "Tools "
},
"NAME": { "NAME": {
"LABEL": "Nom", "LABEL": "Assistant Name",
"PLACEHOLDER": "Enter assistant name", "PLACEHOLDER": "Enter a name for the assistant",
"ERROR": "The name is required" "ERROR": "Please provide a name for the assistant"
}, },
"DESCRIPTION": { "DESCRIPTION": {
"LABEL": "Descripció", "LABEL": "Assistant Description",
"PLACEHOLDER": "Enter assistant description", "PLACEHOLDER": "Describe how and where this assistant will be used",
"ERROR": "The description is required" "ERROR": "A description is required"
}, },
"PRODUCT_NAME": { "PRODUCT_NAME": {
"LABEL": "Product Name", "LABEL": "Product Name",
"PLACEHOLDER": "Enter product name", "PLACEHOLDER": "Enter the name of the product this assistant is designed for",
"ERROR": "The product name is required" "ERROR": "The product name is required"
}, },
"WELCOME_MESSAGE": {
"LABEL": "Welcome Message",
"PLACEHOLDER": "Enter welcome message"
},
"HANDOFF_MESSAGE": {
"LABEL": "Handoff Message",
"PLACEHOLDER": "Enter handoff message"
},
"RESOLUTION_MESSAGE": {
"LABEL": "Resolution Message",
"PLACEHOLDER": "Enter resolution message"
},
"INSTRUCTIONS": {
"LABEL": "Instructions",
"PLACEHOLDER": "Enter instructions for the assistant"
},
"FEATURES": { "FEATURES": {
"TITLE": "Característiques", "TITLE": "Característiques",
"ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations", "ALLOW_CONVERSATION_FAQS": "Generate FAQs from resolved conversations",
@@ -444,8 +397,7 @@
"EDIT": { "EDIT": {
"TITLE": "Update the assistant", "TITLE": "Update the assistant",
"SUCCESS_MESSAGE": "The assistant has been successfully updated", "SUCCESS_MESSAGE": "The assistant has been successfully updated",
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again."
"NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
@@ -83,22 +83,6 @@
"ACTION_PARAMETERS_REQUIRED": "Action parameters are required", "ACTION_PARAMETERS_REQUIRED": "Action parameters are required",
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
},
"ACTIONS": {
"ASSIGN_TEAM": "Assign a Team",
"ASSIGN_AGENT": "Assign an Agent",
"ADD_LABEL": "Add a Label",
"REMOVE_LABEL": "Remove a Label",
"REMOVE_ASSIGNED_TEAM": "Remove Assigned Team",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Silencia la conversa",
"SNOOZE_CONVERSATION": "Posposa la conversa",
"RESOLVE_CONVERSATION": "Resol la conversa",
"SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message",
"CHANGE_PRIORITY": "Canvia la prioritat",
"ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event"
} }
} }
} }
@@ -387,8 +387,7 @@
"LABEL": "Nom de la companyia", "LABEL": "Nom de la companyia",
"PLACEHOLDER": "Wayne Enterprises" "PLACEHOLDER": "Wayne Enterprises"
}, },
"SUBMIT": "Envia", "SUBMIT": "Envia"
"CANCEL": "Cancel·la"
} }
}, },
"KEYBOARD_SHORTCUTS": { "KEYBOARD_SHORTCUTS": {
@@ -2,13 +2,23 @@
"AGENT_BOTS": { "AGENT_BOTS": {
"HEADER": "Bots", "HEADER": "Bots",
"LOADING_EDITOR": "Loading editor...", "LOADING_EDITOR": "Loading editor...",
"DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try. You can manage your bots from this page or create new ones using the 'Add Bot' button.", "DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try.You can manage your bots from this page or create new ones using the 'Configure new bot' button.",
"LEARN_MORE": "Learn about agent bots", "LEARN_MORE": "Learn about agent bots",
"GLOBAL_BOT": "System bot", "CSML_BOT_EDITOR": {
"GLOBAL_BOT_BADGE": "System", "NAME": {
"AVATAR": { "LABEL": "Bot name",
"SUCCESS_DELETE": "Bot avatar deleted successfully", "PLACEHOLDER": "Name your bot.",
"ERROR_DELETE": "Error deleting bot avatar, please try again" "ERROR": "Bot name is required."
},
"DESCRIPTION": {
"LABEL": "Bot description",
"PLACEHOLDER": "What does this bot do?"
},
"BOT_CONFIG": {
"ERROR": "Please enter your CSML bot configuration above.",
"API_ERROR": "Your CSML configuration is invalid. Please fix it and try again."
},
"SUBMIT": "Validate and save"
}, },
"BOT_CONFIGURATION": { "BOT_CONFIGURATION": {
"TITLE": "Select an agent bot", "TITLE": "Select an agent bot",
@@ -22,7 +32,7 @@
"SELECT_PLACEHOLDER": "Select bot" "SELECT_PLACEHOLDER": "Select bot"
}, },
"ADD": { "ADD": {
"TITLE": "Add Bot", "TITLE": "Configure new bot",
"CANCEL_BUTTON_TEXT": "Zrušit", "CANCEL_BUTTON_TEXT": "Zrušit",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot added successfully.", "SUCCESS_MESSAGE": "Bot added successfully.",
@@ -30,22 +40,16 @@
} }
}, },
"LIST": { "LIST": {
"404": "No bots found. You can create a bot by clicking the 'Add Bot' button.", "404": "No bots found. You can create a bot by clicking the 'Configure new bot' button",
"LOADING": "Fetching bots...", "LOADING": "Fetching bots...",
"TABLE_HEADER": { "TYPE": "Bot type"
"DETAILS": "Bot Details",
"URL": "URL webového háčku"
}
}, },
"DELETE": { "DELETE": {
"BUTTON_TEXT": "Vymazat", "BUTTON_TEXT": "Vymazat",
"TITLE": "Delete bot", "TITLE": "Delete bot",
"CONFIRM": { "SUBMIT": "Vymazat",
"TITLE": "Potvrdit odstranění", "CANCEL_BUTTON_TEXT": "Zrušit",
"MESSAGE": "Are you sure you want to delete {name}?", "DESCRIPTION": "Are you sure you want to delete this bot? This action is irreversible.",
"YES": "Ano, odstranit",
"NO": "Ne, zachovat"
},
"API": { "API": {
"SUCCESS_MESSAGE": "Bot deleted successfully.", "SUCCESS_MESSAGE": "Bot deleted successfully.",
"ERROR_MESSAGE": "Could not delete bot. Please try again." "ERROR_MESSAGE": "Could not delete bot. Please try again."
@@ -53,44 +57,17 @@
}, },
"EDIT": { "EDIT": {
"BUTTON_TEXT": "Upravit", "BUTTON_TEXT": "Upravit",
"LOADING": "Fetching bots...",
"TITLE": "Edit bot", "TITLE": "Edit bot",
"CANCEL_BUTTON_TEXT": "Zrušit",
"API": { "API": {
"SUCCESS_MESSAGE": "Bot updated successfully.", "SUCCESS_MESSAGE": "Bot updated successfully.",
"ERROR_MESSAGE": "Could not update bot. Please try again." "ERROR_MESSAGE": "Could not update bot. Please try again."
} }
}, },
"FORM": {
"AVATAR": {
"LABEL": "Bot avatar"
},
"NAME": {
"LABEL": "Bot name",
"PLACEHOLDER": "Enter bot name",
"REQUIRED": "Bot name is required"
},
"DESCRIPTION": {
"LABEL": "Description",
"PLACEHOLDER": "What does this bot do?"
},
"WEBHOOK_URL": {
"LABEL": "URL webového háčku",
"PLACEHOLDER": "https://example.com/webhook",
"REQUIRED": "Webhook URL is required"
},
"ERRORS": {
"NAME": "Bot name is required",
"URL": "Webhook URL is required",
"VALID_URL": "Please enter a valid URL starting with http:// or https://"
},
"CANCEL": "Zrušit",
"CREATE": "Create Bot",
"UPDATE": "Update Bot"
},
"WEBHOOK": {
"DESCRIPTION": "Configure a webhook bot to integrate with your custom services. The bot will receive and process events from conversations and can respond to them."
},
"TYPES": { "TYPES": {
"WEBHOOK": "Webhook bot" "WEBHOOK": "Webhook bot",
"CSML": "CSML bot"
} }
} }
} }
@@ -126,44 +126,6 @@
"ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required", "ATLEAST_ONE_CONDITION_REQUIRED": "At least one condition is required",
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
}, },
"NONE_OPTION": "Nic", "NONE_OPTION": "Nic"
"EVENTS": {
"CONVERSATION_CREATED": "Conversation Created",
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Zpráva vytvořena",
"CONVERSATION_OPENED": "Konverzace otevřena"
},
"ACTIONS": {
"ASSIGN_AGENT": "Přiřadit agentovi",
"ASSIGN_TEAM": "Přiřadit tým",
"ADD_LABEL": "Přidat štítek",
"REMOVE_LABEL": "Odebrat štítek",
"SEND_EMAIL_TO_TEAM": "Poslat e-mail týmu",
"SEND_EMAIL_TRANSCRIPT": "Send an Email Transcript",
"MUTE_CONVERSATION": "Ztlumit konverzaci",
"SNOOZE_CONVERSATION": "Snooze Conversation",
"RESOLVE_CONVERSATION": "Resolve Conversation",
"SEND_WEBHOOK_EVENT": "Send Webhook Event",
"SEND_ATTACHMENT": "Odeslat přílohu",
"SEND_MESSAGE": "Odeslat zprávu",
"CHANGE_PRIORITY": "Change Priority",
"ADD_SLA": "Add SLA"
},
"ATTRIBUTES": {
"MESSAGE_TYPE": "Typ zprávy",
"MESSAGE_CONTAINS": "Zpráva obsahuje",
"EMAIL": "E-mailová adresa",
"INBOX": "Inbox",
"CONVERSATION_LANGUAGE": "Jazyk konverzace",
"PHONE_NUMBER": "Telefonní číslo",
"STATUS": "Stav",
"BROWSER_LANGUAGE": "Browser Language",
"MAIL_SUBJECT": "Předmět e-mailu",
"COUNTRY_NAME": "Země",
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
"PRIORITY": "Priority"
}
} }
} }
@@ -544,9 +544,6 @@
"WROTE": "wrote", "WROTE": "wrote",
"YOU": "Vy", "YOU": "Vy",
"SAVE": "Save note", "SAVE": "Save note",
"EXPAND": "Expand",
"COLLAPSE": "Collapse",
"NO_NOTES": "No notes, you can add notes from the contact details page.",
"EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above." "EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above."
} }
}, },
@@ -32,12 +32,10 @@
"LOADING_CONVERSATIONS": "Načítání konverzací", "LOADING_CONVERSATIONS": "Načítání konverzací",
"CANNOT_REPLY": "Nemůžete odpovědět z důvodu", "CANNOT_REPLY": "Nemůžete odpovědět z důvodu",
"24_HOURS_WINDOW": "24 hodinové omezení okna", "24_HOURS_WINDOW": "24 hodinové omezení okna",
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Tato konverzace vám není přiřazena. Chcete si přiřadit tuto konverzaci?", "NOT_ASSIGNED_TO_YOU": "Tato konverzace vám není přiřazena. Chcete si přiřadit tuto konverzaci?",
"ASSIGN_TO_ME": "Přiřadit mi", "ASSIGN_TO_ME": "Přiřadit mi",
"TWILIO_WHATSAPP_CAN_REPLY": "Na tuto konverzaci můžete odpovědět pouze pomocí šablony zprávy z důvodu", "TWILIO_WHATSAPP_CAN_REPLY": "Na tuto konverzaci můžete odpovědět pouze pomocí šablony zprávy z důvodu",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hodinové omezení okna", "TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hodinové omezení okna",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You wont be able to send messages from this conversation anymore.",
"REPLYING_TO": "Odpovídáte uživateli:", "REPLYING_TO": "Odpovídáte uživateli:",
"REMOVE_SELECTION": "Odstranit výběr", "REMOVE_SELECTION": "Odstranit výběr",
"DOWNLOAD": "Stáhnout", "DOWNLOAD": "Stáhnout",
@@ -295,7 +293,6 @@
"CONVERSATION_ACTIONS": "Akce konverzace", "CONVERSATION_ACTIONS": "Akce konverzace",
"CONVERSATION_LABELS": "Štítky konverzace", "CONVERSATION_LABELS": "Štítky konverzace",
"CONVERSATION_INFO": "Informace o konverzaci", "CONVERSATION_INFO": "Informace o konverzaci",
"CONTACT_NOTES": "Contact Notes",
"CONTACT_ATTRIBUTES": "Atributy kontaktu", "CONTACT_ATTRIBUTES": "Atributy kontaktu",
"PREVIOUS_CONVERSATION": "Předchozí konverzace", "PREVIOUS_CONVERSATION": "Předchozí konverzace",
"MACROS": "Macros", "MACROS": "Macros",
@@ -1,11 +1,5 @@
{ {
"GENERAL_SETTINGS": { "GENERAL_SETTINGS": {
"LIMIT_MESSAGES": {
"CONVERSATION": "You have exceeded the conversation limit. Hacker plan allows only 500 conversations.",
"INBOXES": "You have exceeded the inbox limit. Hacker plan only supports website live-chat. Additional inboxes like email, WhatsApp etc. require a paid plan.",
"AGENTS": "You have exceeded the agent limit. Hacker plan allows only 2 agents.",
"NON_ADMIN": "Please contact your administrator to upgrade the plan and continue using all features."
},
"TITLE": "Nastavení účtu", "TITLE": "Nastavení účtu",
"SUBMIT": "Aktualizovat nastavení", "SUBMIT": "Aktualizovat nastavení",
"BACK": "Zpět", "BACK": "Zpět",
@@ -14,26 +8,6 @@
"ERROR": "Nelze aktualizovat nastavení, zkuste to znovu!", "ERROR": "Nelze aktualizovat nastavení, zkuste to znovu!",
"SUCCESS": "Nastavení účtu bylo úspěšně aktualizováno" "SUCCESS": "Nastavení účtu bylo úspěšně aktualizováno"
}, },
"ACCOUNT_DELETE_SECTION": {
"TITLE": "Odstranit účet",
"NOTE": "Once you delete your account, all your data will be deleted.",
"BUTTON_TEXT": "Odstranit účet",
"CONFIRM": {
"TITLE": "Odstranit účet",
"MESSAGE": "Deleting your Account is irreversible. Enter your account name below to confirm you want to permanently delete it.",
"BUTTON_TEXT": "Vymazat",
"DISMISS": "Zrušit",
"PLACE_HOLDER": "Please type {accountName} to confirm"
},
"SUCCESS": "Account marked for deletion",
"FAILURE": "Účet nelze odstranit, zkuste to znovu!",
"SCHEDULED_DELETION": {
"TITLE": "Account Scheduled for Deletion",
"MESSAGE_MANUAL": "This account is scheduled for deletion on {deletionDate}. This was requested by an administrator. You can cancel the deletion before this date.",
"MESSAGE_INACTIVITY": "This account is scheduled for deletion on {deletionDate} due to account inactivity. You can cancel the deletion before this date.",
"CLEAR_BUTTON": "Cancel Scheduled Deletion"
}
},
"FORM": { "FORM": {
"ERROR": "Opravte chyby formuláře", "ERROR": "Opravte chyby formuláře",
"GENERAL_SECTION": { "GENERAL_SECTION": {
@@ -77,7 +51,6 @@
"UPDATE_CHATWOOT": "Je dostupná aktualizace {latestChatwootVersion} pro Chatwoot. Aktualizujte prosím svou instanci.", "UPDATE_CHATWOOT": "Je dostupná aktualizace {latestChatwootVersion} pro Chatwoot. Aktualizujte prosím svou instanci.",
"LEARN_MORE": "Learn more", "LEARN_MORE": "Learn more",
"PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot", "PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot",
"UPGRADE": "Upgrade to continue using Chatwoot",
"LIMITS_UPGRADE": "Your account has exceeded the usage limits, please upgrade your plan to continue using Chatwoot", "LIMITS_UPGRADE": "Your account has exceeded the usage limits, please upgrade your plan to continue using Chatwoot",
"OPEN_BILLING": "Open billing" "OPEN_BILLING": "Open billing"
}, },
@@ -696,8 +696,7 @@
"SLUG": { "SLUG": {
"LABEL": "Slug", "LABEL": "Slug",
"PLACEHOLDER": "user-guide", "PLACEHOLDER": "user-guide",
"ERROR": "Slug is required", "ERROR": "Slug is required"
"FORMAT_ERROR": "Please enter a valid slug, for eg: user-guide"
} }
}, },
"PORTAL_SETTINGS": { "PORTAL_SETTINGS": {
@@ -43,17 +43,7 @@
"INBOX_NAME": "Název schránky", "INBOX_NAME": "Název schránky",
"ADD_NAME": "Zadejte název schránky", "ADD_NAME": "Zadejte název schránky",
"PICK_NAME": "Pick a Name for your Inbox", "PICK_NAME": "Pick a Name for your Inbox",
"PICK_A_VALUE": "Vyberte hodnotu", "PICK_A_VALUE": "Vyberte hodnotu"
"CREATE_INBOX": "Vytvořit doručenou poštu"
},
"INSTAGRAM": {
"CONTINUE_WITH_INSTAGRAM": "Pokračovat pomocí Instagramu",
"CONNECT_YOUR_INSTAGRAM_PROFILE": "Připojte svůj Instagram profil",
"HELP": "To add your Instagram profile as a channel, you need to authenticate your Instagram Profile by clicking on 'Continue with Instagram' ",
"ERROR_MESSAGE": "There was an error connecting to Instagram, please try again",
"ERROR_AUTH": "There was an error connecting to Instagram, please try again",
"NEW_INBOX_SUGGESTION": "This Instagram account was previously linked to a different inbox and has now been migrated here. All new messages will appear here. The old inbox will no longer be able to send or receive messages for this account.",
"DUPLICATE_INBOX_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. You wont be able to send/receive Instagram messages from this inbox anymore."
}, },
"TWITTER": { "TWITTER": {
"HELP": "Chcete-li přidat svůj Twitter profil jako kanál, musíte ověřit svůj Twitter profil kliknutím na tlačítko 'Přihlásit se přes Twitter' ", "HELP": "Chcete-li přidat svůj Twitter profil jako kanál, musíte ověřit svůj Twitter profil kliknutím na tlačítko 'Přihlásit se přes Twitter' ",
@@ -763,8 +753,7 @@
"EMAIL": "E-mailová adresa", "EMAIL": "E-mailová adresa",
"TELEGRAM": "Telegram", "TELEGRAM": "Telegram",
"LINE": "Line", "LINE": "Line",
"API": "API Channel", "API": "API Channel"
"INSTAGRAM": "Instagram"
} }
} }
} }

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