Compare commits
87
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce8f40debb | ||
|
|
5ca8aec7c3 | ||
|
|
4b8b33c4fd | ||
|
|
b7035de400 | ||
|
|
cec1a64d6f | ||
|
|
8b0caa6909 | ||
|
|
56160bfc63 | ||
|
|
abca836f0c | ||
|
|
64640092aa | ||
|
|
d478f6e42e | ||
|
|
ea34071553 | ||
|
|
ba8df900e3 | ||
|
|
fb86b1354a | ||
|
|
ab28802d5a | ||
|
|
f6ec3f3a72 | ||
|
|
9b75d9bd1b | ||
|
|
ec6c3b3571 | ||
|
|
48ba273730 | ||
|
|
d51148951f | ||
|
|
79bb3d0ef3 | ||
|
|
7418882a4f | ||
|
|
d38bd5139e | ||
|
|
fcc6973245 | ||
|
|
5491ca2470 | ||
|
|
2590c57893 | ||
|
|
f225e2fcd8 | ||
|
|
b626e0cbf1 | ||
|
|
72391f9c36 | ||
|
|
35f42e71f6 | ||
|
|
eb04882e5a | ||
|
|
f89d9a4401 | ||
|
|
e8ae73230d | ||
|
|
40c75941f5 | ||
|
|
e511527f32 | ||
|
|
d9b840f161 | ||
|
|
1fbdd68222 | ||
|
|
ef54f07d5b | ||
|
|
e771d99552 | ||
|
|
358a3924b8 | ||
|
|
6b87d6784e | ||
|
|
faaf67129e | ||
|
|
ce5a859a4f | ||
|
|
159c810117 | ||
|
|
ce400a36d7 | ||
|
|
d20de25da1 | ||
|
|
c31d693add | ||
|
|
94985bbc77 | ||
|
|
31497d9c63 | ||
|
|
f31d9a3e76 | ||
|
|
79082bea58 | ||
|
|
b9f68fc839 | ||
|
|
e10ef03490 | ||
|
|
a35c3e4c06 | ||
|
|
6f9f2c32c9 | ||
|
|
344e8d5016 | ||
|
|
3e27e28848 | ||
|
|
38af08534c | ||
|
|
f999112671 | ||
|
|
be37c5b80c | ||
|
|
3970149b06 | ||
|
|
9c33ce50b3 | ||
|
|
36a2412f42 | ||
|
|
3ba4340042 | ||
|
|
411048715d | ||
|
|
9a7a2486e4 | ||
|
|
39af360a18 | ||
|
|
ac1743514b | ||
|
|
c1df37b41d | ||
|
|
7c3b07c507 | ||
|
|
62203da528 | ||
|
|
029294e16b | ||
|
|
2c095bed86 | ||
|
|
7c6508dd04 | ||
|
|
cef23891af | ||
|
|
19a851c01d | ||
|
|
a22e11c810 | ||
|
|
efe6e64dcd | ||
|
|
d6f5c627eb | ||
|
|
51b4a8f9fc | ||
|
|
1268b2cf55 | ||
|
|
125ae5523a | ||
|
|
59f60936a7 | ||
|
|
216a2c0e73 | ||
|
|
708296a746 | ||
|
|
b9168664f7 | ||
|
|
01cd7b878d | ||
|
|
acb2816826 |
@@ -256,6 +256,8 @@ AZURE_APP_SECRET=
|
||||
## Change these values to fine tune performance
|
||||
# control the concurrency setting of sidekiq
|
||||
# SIDEKIQ_CONCURRENCY=10
|
||||
# Enable verbose logging each time a job is dequeued in Sidekiq
|
||||
# ENABLE_SIDEKIQ_DEQUEUE_LOGGER=false
|
||||
|
||||
|
||||
# AI powered features
|
||||
|
||||
@@ -21,6 +21,7 @@ gem 'telephone_number'
|
||||
gem 'time_diff'
|
||||
gem 'tzinfo-data'
|
||||
gem 'valid_email2'
|
||||
gem 'email-provider-info'
|
||||
# compress javascript config.assets.js_compressor
|
||||
gem 'uglifier'
|
||||
##-- used for single column multiple binary flags in notification settings/feature flagging --##
|
||||
@@ -158,7 +159,7 @@ gem 'working_hours'
|
||||
gem 'pg_search'
|
||||
|
||||
# Subscriptions, Billing
|
||||
gem 'stripe'
|
||||
gem 'stripe', '17.2.0.pre.alpha.2'
|
||||
|
||||
## - helper gems --##
|
||||
## to populate db with sample data
|
||||
|
||||
+6
-4
@@ -270,6 +270,7 @@ GEM
|
||||
concurrent-ruby (~> 1.0)
|
||||
http (>= 3.0)
|
||||
ruby2_keywords
|
||||
email-provider-info (0.0.1)
|
||||
email_reply_trimmer (0.1.13)
|
||||
erubi (1.13.0)
|
||||
et-orbi (1.2.11)
|
||||
@@ -594,7 +595,7 @@ GEM
|
||||
oj (3.16.10)
|
||||
bigdecimal (>= 3.0)
|
||||
ostruct (>= 0.2)
|
||||
omniauth (2.1.3)
|
||||
omniauth (2.1.4)
|
||||
hashie (>= 3.4.6)
|
||||
logger
|
||||
rack (>= 2.2.3)
|
||||
@@ -653,7 +654,7 @@ GEM
|
||||
rack (>= 2.0.0)
|
||||
rack-mini-profiler (3.2.0)
|
||||
rack (>= 1.2.0)
|
||||
rack-protection (4.1.1)
|
||||
rack-protection (4.2.1)
|
||||
base64 (>= 0.1.0)
|
||||
logger (>= 1.6.0)
|
||||
rack (>= 3.0.0, < 4)
|
||||
@@ -901,7 +902,7 @@ GEM
|
||||
squasher (0.7.2)
|
||||
stackprof (0.2.25)
|
||||
statsd-ruby (1.5.0)
|
||||
stripe (8.5.0)
|
||||
stripe (17.2.0.pre.alpha.2)
|
||||
telephone_number (1.4.20)
|
||||
test-prof (1.2.1)
|
||||
thor (1.4.0)
|
||||
@@ -1016,6 +1017,7 @@ DEPENDENCIES
|
||||
dotenv-rails (>= 3.0.0)
|
||||
down
|
||||
elastic-apm
|
||||
email-provider-info
|
||||
email_reply_trimmer
|
||||
facebook-messenger
|
||||
factory_bot_rails (>= 6.4.3)
|
||||
@@ -1108,7 +1110,7 @@ DEPENDENCIES
|
||||
spring-watcher-listen
|
||||
squasher
|
||||
stackprof
|
||||
stripe
|
||||
stripe (= 17.2.0.pre.alpha.2)
|
||||
telephone_number
|
||||
test-prof
|
||||
tidewave
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
"REDIS_OPENSSL_VERIFY_MODE":{
|
||||
"description": "OpenSSL verification mode for Redis connections. ref https://help.heroku.com/HC0F8CUS/redis-connection-issues",
|
||||
"value": "none"
|
||||
},
|
||||
"NODE_OPTIONS": {
|
||||
"description": "Increase V8 heap for Vite build to avoid OOM",
|
||||
"value": "--max-old-space-size=4096"
|
||||
}
|
||||
},
|
||||
"formation": {
|
||||
|
||||
@@ -23,7 +23,7 @@ class Messages::MessageBuilder
|
||||
process_emails
|
||||
# When the message has no quoted content, it will just be rendered as a regular message
|
||||
# The frontend is equipped to handle this case
|
||||
process_email_content if @account.feature_enabled?(:quoted_email_reply)
|
||||
process_email_content
|
||||
@message.save!
|
||||
@message
|
||||
end
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
class Api::V1::Accounts::BulkActionsController < Api::V1::Accounts::BaseController
|
||||
before_action :type_matches?
|
||||
|
||||
def create
|
||||
if type_matches?
|
||||
::BulkActionsJob.perform_later(
|
||||
account: @current_account,
|
||||
user: current_user,
|
||||
params: permitted_params
|
||||
)
|
||||
case normalized_type
|
||||
when 'Conversation'
|
||||
enqueue_conversation_job
|
||||
head :ok
|
||||
when 'Contact'
|
||||
check_authorization_for_contact_action
|
||||
enqueue_contact_job
|
||||
head :ok
|
||||
else
|
||||
render json: { success: false }, status: :unprocessable_entity
|
||||
@@ -16,11 +15,54 @@ class Api::V1::Accounts::BulkActionsController < Api::V1::Accounts::BaseControll
|
||||
|
||||
private
|
||||
|
||||
def type_matches?
|
||||
['Conversation'].include?(params[:type])
|
||||
def normalized_type
|
||||
params[:type].to_s.camelize
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.permit(:type, :snoozed_until, ids: [], fields: [:status, :assignee_id, :team_id], labels: [add: [], remove: []])
|
||||
def enqueue_conversation_job
|
||||
::BulkActionsJob.perform_later(
|
||||
account: @current_account,
|
||||
user: current_user,
|
||||
params: conversation_params
|
||||
)
|
||||
end
|
||||
|
||||
def enqueue_contact_job
|
||||
Contacts::BulkActionJob.perform_later(
|
||||
@current_account.id,
|
||||
current_user.id,
|
||||
contact_params
|
||||
)
|
||||
end
|
||||
|
||||
def delete_contact_action?
|
||||
params[:action_name] == 'delete'
|
||||
end
|
||||
|
||||
def check_authorization_for_contact_action
|
||||
authorize(Contact, :destroy?) if delete_contact_action?
|
||||
end
|
||||
|
||||
def conversation_params
|
||||
# TODO: Align conversation payloads with the `{ action_name, action_attributes }`
|
||||
# and then remove this method in favor of a common params method.
|
||||
base = params.permit(
|
||||
:snoozed_until,
|
||||
fields: [:status, :assignee_id, :team_id]
|
||||
)
|
||||
append_common_bulk_attributes(base)
|
||||
end
|
||||
|
||||
def contact_params
|
||||
# TODO: remove this method in favor of a common params method.
|
||||
# once legacy conversation payloads are migrated.
|
||||
append_common_bulk_attributes({})
|
||||
end
|
||||
|
||||
def append_common_bulk_attributes(base_params)
|
||||
# NOTE: Conversation payloads historically diverged per action. Going forward we
|
||||
# want all objects to share a common contract: `{ action_name, action_attributes }`
|
||||
common = params.permit(:type, :action_name, ids: [], labels: [add: [], remove: []])
|
||||
base_params.merge(common)
|
||||
end
|
||||
end
|
||||
|
||||
+38
-35
@@ -5,6 +5,7 @@ import { useToggle } from '@vueuse/core';
|
||||
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import ConfirmContactDeleteDialog from 'dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue';
|
||||
import Policy from 'dashboard/components/policy.vue';
|
||||
|
||||
defineProps({
|
||||
selectedContact: {
|
||||
@@ -24,42 +25,44 @@ const openConfirmDeleteContactDialog = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col items-start border-t border-n-strong px-6 py-5">
|
||||
<Button
|
||||
:label="t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT')"
|
||||
sm
|
||||
link
|
||||
slate
|
||||
class="hover:!no-underline text-n-slate-12"
|
||||
icon="i-lucide-chevron-down"
|
||||
trailing-icon
|
||||
@click="toggleDeleteSection()"
|
||||
/>
|
||||
<Policy :permissions="['administrator']">
|
||||
<div class="flex flex-col items-start border-t border-n-strong px-6 py-5">
|
||||
<Button
|
||||
:label="t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT')"
|
||||
sm
|
||||
link
|
||||
slate
|
||||
class="hover:!no-underline text-n-slate-12"
|
||||
icon="i-lucide-chevron-down"
|
||||
trailing-icon
|
||||
@click="toggleDeleteSection()"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="transition-all duration-300 ease-in-out grid w-full overflow-hidden"
|
||||
:class="
|
||||
showDeleteSection
|
||||
? 'grid-rows-[1fr] opacity-100 mt-2'
|
||||
: 'grid-rows-[0fr] opacity-0 mt-0'
|
||||
"
|
||||
>
|
||||
<div class="overflow-hidden min-h-0">
|
||||
<span class="inline-flex text-n-slate-11 text-sm items-center gap-1">
|
||||
{{ t('CONTACTS_LAYOUT.CARD.DELETE_CONTACT.MESSAGE') }}
|
||||
<Button
|
||||
:label="t('CONTACTS_LAYOUT.CARD.DELETE_CONTACT.BUTTON')"
|
||||
sm
|
||||
ruby
|
||||
link
|
||||
@click="openConfirmDeleteContactDialog()"
|
||||
/>
|
||||
</span>
|
||||
<div
|
||||
class="transition-all duration-300 ease-in-out grid w-full overflow-hidden"
|
||||
:class="
|
||||
showDeleteSection
|
||||
? 'grid-rows-[1fr] opacity-100 mt-2'
|
||||
: 'grid-rows-[0fr] opacity-0 mt-0'
|
||||
"
|
||||
>
|
||||
<div class="overflow-hidden min-h-0">
|
||||
<span class="inline-flex text-n-slate-11 text-sm items-center gap-1">
|
||||
{{ t('CONTACTS_LAYOUT.CARD.DELETE_CONTACT.MESSAGE') }}
|
||||
<Button
|
||||
:label="t('CONTACTS_LAYOUT.CARD.DELETE_CONTACT.BUTTON')"
|
||||
sm
|
||||
ruby
|
||||
link
|
||||
@click="openConfirmDeleteContactDialog()"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ConfirmContactDeleteDialog
|
||||
ref="confirmDeleteContactDialogRef"
|
||||
:selected-contact="selectedContact"
|
||||
/>
|
||||
<ConfirmContactDeleteDialog
|
||||
ref="confirmDeleteContactDialogRef"
|
||||
:selected-contact="selectedContact"
|
||||
/>
|
||||
</Policy>
|
||||
</template>
|
||||
|
||||
@@ -8,6 +8,7 @@ import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
|
||||
import Flag from 'dashboard/components-next/flag/Flag.vue';
|
||||
import ContactDeleteSection from 'dashboard/components-next/Contacts/ContactsCard/ContactDeleteSection.vue';
|
||||
import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue';
|
||||
import countries from 'shared/constants/countries';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -20,9 +21,17 @@ const props = defineProps({
|
||||
availabilityStatus: { type: String, default: null },
|
||||
isExpanded: { type: Boolean, default: false },
|
||||
isUpdating: { type: Boolean, default: false },
|
||||
selectable: { type: Boolean, default: false },
|
||||
isSelected: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['toggle', 'updateContact', 'showContact']);
|
||||
const emit = defineEmits([
|
||||
'toggle',
|
||||
'updateContact',
|
||||
'showContact',
|
||||
'select',
|
||||
'avatarHover',
|
||||
]);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -88,111 +97,148 @@ const onClickExpand = () => {
|
||||
};
|
||||
|
||||
const onClickViewDetails = () => emit('showContact', props.id);
|
||||
|
||||
const toggleSelect = checked => {
|
||||
emit('select', checked);
|
||||
};
|
||||
|
||||
const handleAvatarHover = isHovered => {
|
||||
emit('avatarHover', isHovered);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardLayout :key="id" layout="row">
|
||||
<div class="flex items-center justify-start flex-1 gap-4">
|
||||
<Avatar
|
||||
:name="name"
|
||||
:src="thumbnail"
|
||||
:size="48"
|
||||
:status="availabilityStatus"
|
||||
hide-offline-status
|
||||
rounded-full
|
||||
/>
|
||||
<div class="flex flex-col gap-0.5 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-1">
|
||||
<span class="text-base font-medium truncate text-n-slate-12">
|
||||
{{ name }}
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<span
|
||||
v-if="additionalAttributes?.companyName"
|
||||
class="i-ph-building-light size-4 text-n-slate-10 mb-0.5"
|
||||
/>
|
||||
<span
|
||||
v-if="additionalAttributes?.companyName"
|
||||
class="text-sm truncate text-n-slate-11"
|
||||
>
|
||||
{{ additionalAttributes.companyName }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center justify-start gap-x-3 gap-y-1">
|
||||
<div v-if="email" class="truncate max-w-72" :title="email">
|
||||
<span class="text-sm text-n-slate-11">
|
||||
{{ email }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="email" class="w-px h-3 truncate bg-n-slate-6" />
|
||||
<span v-if="phoneNumber" class="text-sm truncate text-n-slate-11">
|
||||
{{ phoneNumber }}
|
||||
</span>
|
||||
<div v-if="phoneNumber" class="w-px h-3 truncate bg-n-slate-6" />
|
||||
<span
|
||||
v-if="countryDetails"
|
||||
class="inline-flex items-center gap-2 text-sm truncate text-n-slate-11"
|
||||
<div class="relative">
|
||||
<CardLayout
|
||||
:key="id"
|
||||
layout="row"
|
||||
:class="{
|
||||
'outline-n-weak !bg-n-slate-3 dark:!bg-n-solid-3': isSelected,
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center justify-start flex-1 gap-4">
|
||||
<div
|
||||
class="relative"
|
||||
@mouseenter="handleAvatarHover(true)"
|
||||
@mouseleave="handleAvatarHover(false)"
|
||||
>
|
||||
<Avatar
|
||||
:name="name"
|
||||
:src="thumbnail"
|
||||
:size="48"
|
||||
:status="availabilityStatus"
|
||||
hide-offline-status
|
||||
rounded-full
|
||||
>
|
||||
<Flag :country="countryDetails.countryCode" class="size-3.5" />
|
||||
{{ formattedLocation }}
|
||||
</span>
|
||||
<div v-if="countryDetails" class="w-px h-3 truncate bg-n-slate-6" />
|
||||
<Button
|
||||
:label="t('CONTACTS_LAYOUT.CARD.VIEW_DETAILS')"
|
||||
variant="link"
|
||||
size="xs"
|
||||
@click="onClickViewDetails"
|
||||
/>
|
||||
<template v-if="selectable" #overlay="{ size }">
|
||||
<label
|
||||
class="flex items-center justify-center rounded-full cursor-pointer absolute inset-0 z-10 backdrop-blur-[2px] border border-n-weak"
|
||||
:style="{ width: `${size}px`, height: `${size}px` }"
|
||||
@click.stop
|
||||
>
|
||||
<Checkbox
|
||||
:model-value="isSelected"
|
||||
@change="event => toggleSelect(event.target.checked)"
|
||||
/>
|
||||
</label>
|
||||
</template>
|
||||
</Avatar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
icon="i-lucide-chevron-down"
|
||||
variant="ghost"
|
||||
color="slate"
|
||||
size="xs"
|
||||
:class="{ 'rotate-180': isExpanded }"
|
||||
@click="onClickExpand"
|
||||
/>
|
||||
|
||||
<template #after>
|
||||
<div
|
||||
class="transition-all duration-500 ease-in-out grid overflow-hidden"
|
||||
:class="
|
||||
isExpanded
|
||||
? 'grid-rows-[1fr] opacity-100'
|
||||
: 'grid-rows-[0fr] opacity-0'
|
||||
"
|
||||
>
|
||||
<div class="overflow-hidden">
|
||||
<div class="flex flex-col gap-6 p-6 border-t border-n-strong">
|
||||
<ContactsForm
|
||||
ref="contactsFormRef"
|
||||
:contact-data="contactData"
|
||||
@update="handleFormUpdate"
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
:label="
|
||||
t('CONTACTS_LAYOUT.CARD.EDIT_DETAILS_FORM.UPDATE_BUTTON')
|
||||
"
|
||||
size="sm"
|
||||
:is-loading="isUpdating"
|
||||
:disabled="isUpdating || isFormInvalid"
|
||||
@click="handleUpdateContact"
|
||||
<div class="flex flex-col gap-0.5 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-1">
|
||||
<span class="text-base font-medium truncate text-n-slate-12">
|
||||
{{ name }}
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<span
|
||||
v-if="additionalAttributes?.companyName"
|
||||
class="i-ph-building-light size-4 text-n-slate-10 mb-0.5"
|
||||
/>
|
||||
<span
|
||||
v-if="additionalAttributes?.companyName"
|
||||
class="text-sm truncate text-n-slate-11"
|
||||
>
|
||||
{{ additionalAttributes.companyName }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-start gap-x-3 gap-y-1"
|
||||
>
|
||||
<div v-if="email" class="truncate max-w-72" :title="email">
|
||||
<span class="text-sm text-n-slate-11">
|
||||
{{ email }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="email" class="w-px h-3 truncate bg-n-slate-6" />
|
||||
<span v-if="phoneNumber" class="text-sm truncate text-n-slate-11">
|
||||
{{ phoneNumber }}
|
||||
</span>
|
||||
<div v-if="phoneNumber" class="w-px h-3 truncate bg-n-slate-6" />
|
||||
<span
|
||||
v-if="countryDetails"
|
||||
class="inline-flex items-center gap-2 text-sm truncate text-n-slate-11"
|
||||
>
|
||||
<Flag :country="countryDetails.countryCode" class="size-3.5" />
|
||||
{{ formattedLocation }}
|
||||
</span>
|
||||
<div v-if="countryDetails" class="w-px h-3 truncate bg-n-slate-6" />
|
||||
<Button
|
||||
:label="t('CONTACTS_LAYOUT.CARD.VIEW_DETAILS')"
|
||||
variant="link"
|
||||
size="xs"
|
||||
@click="onClickViewDetails"
|
||||
/>
|
||||
</div>
|
||||
<ContactDeleteSection
|
||||
:selected-contact="{
|
||||
id: props.id,
|
||||
name: props.name,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CardLayout>
|
||||
|
||||
<Button
|
||||
icon="i-lucide-chevron-down"
|
||||
variant="ghost"
|
||||
color="slate"
|
||||
size="xs"
|
||||
:class="{ 'rotate-180': isExpanded }"
|
||||
@click="onClickExpand"
|
||||
/>
|
||||
|
||||
<template #after>
|
||||
<div
|
||||
class="transition-all duration-500 ease-in-out grid overflow-hidden"
|
||||
:class="
|
||||
isExpanded
|
||||
? 'grid-rows-[1fr] opacity-100'
|
||||
: 'grid-rows-[0fr] opacity-0'
|
||||
"
|
||||
>
|
||||
<div class="overflow-hidden">
|
||||
<div class="flex flex-col gap-6 p-6 border-t border-n-strong">
|
||||
<ContactsForm
|
||||
ref="contactsFormRef"
|
||||
:contact-data="contactData"
|
||||
@update="handleFormUpdate"
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
:label="
|
||||
t('CONTACTS_LAYOUT.CARD.EDIT_DETAILS_FORM.UPDATE_BUTTON')
|
||||
"
|
||||
size="sm"
|
||||
:is-loading="isUpdating"
|
||||
:disabled="isUpdating || isFormInvalid"
|
||||
@click="handleUpdateContact"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ContactDeleteSection
|
||||
:selected-contact="{
|
||||
id: props.id,
|
||||
name: props.name,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CardLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,6 +10,7 @@ import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import ContactLabels from 'dashboard/components-next/Contacts/ContactLabels/ContactLabels.vue';
|
||||
import ContactsForm from 'dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue';
|
||||
import ConfirmContactDeleteDialog from 'dashboard/components-next/Contacts/ContactsForm/ConfirmContactDeleteDialog.vue';
|
||||
import Policy from 'dashboard/components/policy.vue';
|
||||
|
||||
const props = defineProps({
|
||||
selectedContact: {
|
||||
@@ -174,27 +175,29 @@ const handleAvatarDelete = async () => {
|
||||
@click="updateContact"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col items-start w-full gap-4 pt-6 border-t border-n-strong"
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h6 class="text-base font-medium text-n-slate-12">
|
||||
{{ t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT') }}
|
||||
</h6>
|
||||
<span class="text-sm text-n-slate-11">
|
||||
{{ t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT_DESCRIPTION') }}
|
||||
</span>
|
||||
<Policy :permissions="['administrator']">
|
||||
<div
|
||||
class="flex flex-col items-start w-full gap-4 pt-6 border-t border-n-strong"
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h6 class="text-base font-medium text-n-slate-12">
|
||||
{{ t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT') }}
|
||||
</h6>
|
||||
<span class="text-sm text-n-slate-11">
|
||||
{{ t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT_DESCRIPTION') }}
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
:label="t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT')"
|
||||
color="ruby"
|
||||
@click="openConfirmDeleteContactDialog"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
:label="t('CONTACTS_LAYOUT.DETAILS.DELETE_CONTACT')"
|
||||
color="ruby"
|
||||
@click="openConfirmDeleteContactDialog"
|
||||
<ConfirmContactDeleteDialog
|
||||
ref="confirmDeleteContactDialogRef"
|
||||
:selected-contact="selectedContact"
|
||||
@go-to-contacts-list="emit('goToContactsList')"
|
||||
/>
|
||||
</div>
|
||||
<ConfirmContactDeleteDialog
|
||||
ref="confirmDeleteContactDialogRef"
|
||||
:selected-contact="selectedContact"
|
||||
@go-to-contacts-list="emit('goToContactsList')"
|
||||
/>
|
||||
</Policy>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,7 +10,15 @@ import {
|
||||
} from 'shared/helpers/CustomErrors';
|
||||
import ContactsCard from 'dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue';
|
||||
|
||||
defineProps({ contacts: { type: Array, required: true } });
|
||||
const props = defineProps({
|
||||
contacts: { type: Array, required: true },
|
||||
selectedContactIds: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['toggleContact']);
|
||||
|
||||
const { t } = useI18n();
|
||||
const store = useStore();
|
||||
@@ -20,6 +28,9 @@ const route = useRoute();
|
||||
const uiFlags = useMapGetter('contacts/getUIFlags');
|
||||
const isUpdating = computed(() => uiFlags.value.isUpdating);
|
||||
const expandedCardId = ref(null);
|
||||
const hoveredAvatarId = ref(null);
|
||||
|
||||
const selectedIdsSet = computed(() => new Set(props.selectedContactIds || []));
|
||||
|
||||
const updateContact = async updatedData => {
|
||||
try {
|
||||
@@ -58,25 +69,43 @@ const onClickViewDetails = async id => {
|
||||
const toggleExpanded = id => {
|
||||
expandedCardId.value = expandedCardId.value === id ? null : id;
|
||||
};
|
||||
|
||||
const isSelected = id => selectedIdsSet.value.has(id);
|
||||
|
||||
const shouldShowSelection = id => {
|
||||
return hoveredAvatarId.value === id || isSelected(id);
|
||||
};
|
||||
|
||||
const handleSelect = (id, value) => {
|
||||
emit('toggleContact', { id, value });
|
||||
};
|
||||
|
||||
const handleAvatarHover = (id, isHovered) => {
|
||||
hoveredAvatarId.value = isHovered ? id : null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-4 px-6 pt-4 pb-6">
|
||||
<ContactsCard
|
||||
v-for="contact in contacts"
|
||||
:id="contact.id"
|
||||
:key="contact.id"
|
||||
:name="contact.name"
|
||||
:email="contact.email"
|
||||
:thumbnail="contact.thumbnail"
|
||||
:phone-number="contact.phoneNumber"
|
||||
:additional-attributes="contact.additionalAttributes"
|
||||
:availability-status="contact.availabilityStatus"
|
||||
:is-expanded="expandedCardId === contact.id"
|
||||
:is-updating="isUpdating"
|
||||
@toggle="toggleExpanded(contact.id)"
|
||||
@update-contact="updateContact"
|
||||
@show-contact="onClickViewDetails"
|
||||
/>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div v-for="contact in contacts" :key="contact.id" class="relative">
|
||||
<ContactsCard
|
||||
:id="contact.id"
|
||||
:name="contact.name"
|
||||
:email="contact.email"
|
||||
:thumbnail="contact.thumbnail"
|
||||
:phone-number="contact.phoneNumber"
|
||||
:additional-attributes="contact.additionalAttributes"
|
||||
:availability-status="contact.availabilityStatus"
|
||||
:is-expanded="expandedCardId === contact.id"
|
||||
:is-updating="isUpdating"
|
||||
:selectable="shouldShowSelection(contact.id)"
|
||||
:is-selected="isSelected(contact.id)"
|
||||
@toggle="toggleExpanded(contact.id)"
|
||||
@update-contact="updateContact"
|
||||
@show-contact="onClickViewDetails"
|
||||
@select="value => handleSelect(contact.id, value)"
|
||||
@avatar-hover="value => handleAvatarHover(contact.id, value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup>
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import ButtonGroup from 'dashboard/components-next/buttonGroup/ButtonGroup.vue';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { computed } from 'vue';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
@@ -55,17 +56,17 @@ useKeyboardEvents(keyboardEvents);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col justify-center items-center absolute top-36 xl:top-24 ltr:right-2 rtl:left-2 bg-n-solid-2 border border-n-weak rounded-full gap-2 p-1"
|
||||
<ButtonGroup
|
||||
class="flex flex-col justify-center items-center absolute top-36 xl:top-24 ltr:right-2 rtl:left-2 bg-n-solid-2/90 backdrop-blur-lg border border-n-weak/50 rounded-full gap-1.5 p-1.5 shadow-sm transition-shadow duration-200 hover:shadow"
|
||||
>
|
||||
<Button
|
||||
v-tooltip.top="$t('CONVERSATION.SIDEBAR.CONTACT')"
|
||||
ghost
|
||||
slate
|
||||
sm
|
||||
class="!rounded-full"
|
||||
class="!rounded-full transition-all duration-[250ms] ease-out active:!scale-95 active:!brightness-105 active:duration-75"
|
||||
:class="{
|
||||
'bg-n-alpha-2': isContactSidebarOpen,
|
||||
'bg-n-alpha-2 active:shadow-sm': isContactSidebarOpen,
|
||||
}"
|
||||
icon="i-ph-user-bold"
|
||||
@click="handleConversationSidebarToggle"
|
||||
@@ -75,13 +76,14 @@ useKeyboardEvents(keyboardEvents);
|
||||
v-tooltip.bottom="$t('CONVERSATION.SIDEBAR.COPILOT')"
|
||||
ghost
|
||||
slate
|
||||
class="!rounded-full"
|
||||
:class="{
|
||||
'bg-n-alpha-2 !text-n-iris-9': isCopilotPanelOpen,
|
||||
}"
|
||||
sm
|
||||
class="!rounded-full transition-all duration-[250ms] ease-out active:!scale-95 active:duration-75"
|
||||
:class="{
|
||||
'bg-n-alpha-2 !text-n-iris-9 active:!brightness-105 active:shadow-sm':
|
||||
isCopilotPanelOpen,
|
||||
}"
|
||||
icon="i-woot-captain"
|
||||
@click="handleCopilotSidebarToggle"
|
||||
/>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
</template>
|
||||
|
||||
@@ -21,6 +21,10 @@ const props = defineProps({
|
||||
enableCannedResponses: { type: Boolean, default: true },
|
||||
enabledMenuOptions: { type: Array, default: () => [] },
|
||||
enableCaptainTools: { type: Boolean, default: false },
|
||||
signature: { type: String, default: '' },
|
||||
allowSignature: { type: Boolean, default: false },
|
||||
sendWithSignature: { type: Boolean, default: false },
|
||||
channelType: { type: String, default: '' },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
@@ -100,6 +104,10 @@ watch(
|
||||
:enable-canned-responses="enableCannedResponses"
|
||||
:enabled-menu-options="enabledMenuOptions"
|
||||
:enable-captain-tools="enableCaptainTools"
|
||||
:signature="signature"
|
||||
:allow-signature="allowSignature"
|
||||
:send-with-signature="sendWithSignature"
|
||||
:channel-type="channelType"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
|
||||
@@ -14,6 +14,10 @@ defineProps({
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
showBackdrop: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -25,14 +29,24 @@ defineProps({
|
||||
class="relative w-full max-w-[60rem] mx-auto overflow-hidden h-full max-h-[28rem]"
|
||||
>
|
||||
<div
|
||||
v-if="showBackdrop"
|
||||
class="w-full h-full space-y-4 overflow-y-hidden opacity-50 pointer-events-none"
|
||||
>
|
||||
<slot name="empty-state-item" />
|
||||
</div>
|
||||
<div
|
||||
class="absolute inset-x-0 bottom-0 flex flex-col items-center justify-end w-full h-full pb-20 bg-gradient-to-t from-n-background from-25% dark:from-n-background to-transparent"
|
||||
class="flex flex-col items-center justify-end w-full h-full pb-20"
|
||||
:class="{
|
||||
'absolute inset-x-0 bottom-0 bg-gradient-to-t from-n-background from-25% dark:from-n-background to-transparent':
|
||||
showBackdrop,
|
||||
}"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center gap-6">
|
||||
<div
|
||||
class="flex flex-col items-center justify-center gap-6"
|
||||
:class="{
|
||||
'mt-48': !showBackdrop,
|
||||
}"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center gap-3">
|
||||
<h2
|
||||
class="text-3xl font-medium text-center text-n-slate-12 font-interDisplay"
|
||||
@@ -40,6 +54,7 @@ defineProps({
|
||||
{{ title }}
|
||||
</h2>
|
||||
<p
|
||||
v-if="subtitle"
|
||||
class="max-w-xl text-base text-center text-n-slate-11 font-interDisplay tracking-[0.3px]"
|
||||
>
|
||||
{{ subtitle }}
|
||||
|
||||
+5
-2
@@ -14,6 +14,7 @@ import {
|
||||
} from 'dashboard/helper/portalHelper';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
|
||||
import ButtonGroup from 'dashboard/components-next/buttonGroup/ButtonGroup.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
|
||||
|
||||
@@ -140,11 +141,12 @@ const updateArticleStatus = async ({ value }) => {
|
||||
:disabled="!articleId"
|
||||
@click="previewArticle"
|
||||
/>
|
||||
<div class="flex items-center">
|
||||
<ButtonGroup class="flex items-center">
|
||||
<Button
|
||||
:label="t('HELP_CENTER.EDIT_ARTICLE_PAGE.HEADER.PUBLISH')"
|
||||
size="sm"
|
||||
class="ltr:rounded-r-none rtl:rounded-l-none"
|
||||
no-animation
|
||||
:is-loading="isArticlePublishing"
|
||||
:disabled="
|
||||
status === ARTICLE_STATUSES.PUBLISHED ||
|
||||
@@ -159,6 +161,7 @@ const updateArticleStatus = async ({ value }) => {
|
||||
icon="i-lucide-chevron-down"
|
||||
size="sm"
|
||||
:disabled="!articleId"
|
||||
no-animation
|
||||
class="ltr:rounded-l-none rtl:rounded-r-none"
|
||||
@click.stop="showArticleActionMenu = !showArticleActionMenu"
|
||||
/>
|
||||
@@ -170,7 +173,7 @@ const updateArticleStatus = async ({ value }) => {
|
||||
/>
|
||||
</OnClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -92,7 +92,6 @@ const setSignature = () => {
|
||||
|
||||
const toggleMessageSignature = () => {
|
||||
setSignatureFlagForInbox(props.channelType, !sendWithSignature.value);
|
||||
setSignature();
|
||||
};
|
||||
|
||||
// Added this watch to dynamically set signature on target inbox change.
|
||||
|
||||
+14
-8
@@ -199,16 +199,20 @@ const handleInboxAction = ({ value, action, ...rest }) => {
|
||||
state.attachedFiles = [];
|
||||
};
|
||||
|
||||
const removeTargetInbox = value => {
|
||||
v$.value.$reset();
|
||||
// Remove the signature from message content
|
||||
// Based on the Advance Editor (used in isEmailOrWebWidget) and Plain editor(all other inboxes except WhatsApp)
|
||||
if (props.sendWithSignature) {
|
||||
const signatureToRemove = inboxTypes.value.isEmailOrWebWidget
|
||||
? props.messageSignature
|
||||
: extractTextFromMarkdown(props.messageSignature);
|
||||
const removeSignatureFromMessage = () => {
|
||||
// Always remove the signature from message content when inbox/contact is removed
|
||||
// to ensure no leftover signature content remains
|
||||
const signatureToRemove = inboxTypes.value.isEmailOrWebWidget
|
||||
? props.messageSignature
|
||||
: extractTextFromMarkdown(props.messageSignature);
|
||||
if (signatureToRemove) {
|
||||
state.message = removeSignature(state.message, signatureToRemove);
|
||||
}
|
||||
};
|
||||
|
||||
const removeTargetInbox = value => {
|
||||
v$.value.$reset();
|
||||
removeSignatureFromMessage();
|
||||
emit('updateTargetInbox', value);
|
||||
state.attachedFiles = [];
|
||||
};
|
||||
@@ -216,6 +220,7 @@ const removeTargetInbox = value => {
|
||||
const clearSelectedContact = () => {
|
||||
emit('clearSelectedContact');
|
||||
state.attachedFiles = [];
|
||||
removeSignatureFromMessage();
|
||||
};
|
||||
|
||||
const onClickInsertEmoji = emoji => {
|
||||
@@ -354,6 +359,7 @@ const shouldShowMessageEditor = computed(() => {
|
||||
:is-email-or-web-widget-inbox="inboxTypes.isEmailOrWebWidget"
|
||||
:has-errors="validationStates.isMessageInvalid"
|
||||
:has-attachments="state.attachedFiles.length > 0"
|
||||
:channel-type="inboxChannelType"
|
||||
/>
|
||||
|
||||
<AttachmentPreviews
|
||||
|
||||
@@ -17,6 +17,7 @@ const props = defineProps({
|
||||
hasAttachments: { type: Boolean, default: false },
|
||||
sendWithSignature: { type: Boolean, default: false },
|
||||
messageSignature: { type: String, default: '' },
|
||||
channelType: { type: String, default: '' },
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -90,6 +91,10 @@ const replaceText = async message => {
|
||||
"
|
||||
enable-variables
|
||||
:show-character-count="false"
|
||||
:signature="messageSignature"
|
||||
allow-signature
|
||||
:send-with-signature="sendWithSignature"
|
||||
:channel-type="channelType"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
||||
@@ -36,6 +36,7 @@ const props = defineProps({
|
||||
icon: { type: [String, Object, Function], default: '' },
|
||||
trailingIcon: { type: Boolean, default: false },
|
||||
isLoading: { type: Boolean, default: false },
|
||||
noAnimation: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
@@ -179,12 +180,18 @@ const STYLE_CONFIG = {
|
||||
md: 'text-sm font-medium',
|
||||
lg: 'text-base',
|
||||
},
|
||||
clickAnimation: {
|
||||
xs: 'active:enabled:scale-[0.97]',
|
||||
sm: 'active:enabled:scale-[0.97]',
|
||||
md: 'active:enabled:scale-[0.98]',
|
||||
lg: 'active:enabled:scale-[0.98]',
|
||||
},
|
||||
justify: {
|
||||
start: 'justify-start',
|
||||
center: 'justify-center',
|
||||
end: 'justify-end',
|
||||
},
|
||||
base: 'inline-flex items-center min-w-0 gap-2 transition-all duration-200 ease-in-out border-0 rounded-lg outline-1 outline disabled:opacity-50',
|
||||
base: 'inline-flex items-center min-w-0 gap-2 transition-all duration-100 ease-out border-0 rounded-lg outline-1 outline disabled:opacity-50',
|
||||
};
|
||||
|
||||
const variantClasses = computed(() => {
|
||||
@@ -221,6 +228,12 @@ const linkButtonClasses = computed(() => {
|
||||
|
||||
return classes.join(' ');
|
||||
});
|
||||
|
||||
const animationClasses = computed(() => {
|
||||
return props.noAnimation
|
||||
? ''
|
||||
: STYLE_CONFIG.clickAnimation[computedSize.value];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -230,6 +243,7 @@ const linkButtonClasses = computed(() => {
|
||||
[STYLE_CONFIG.base]: true,
|
||||
[isLink ? linkButtonClasses : buttonClasses]: true,
|
||||
[STYLE_CONFIG.fontSize[computedSize]]: true,
|
||||
[animationClasses]: true,
|
||||
[STYLE_CONFIG.justify[computedJustify]]: true,
|
||||
'flex-row-reverse': trailingIcon && !isIconOnly,
|
||||
}"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
noAnimation: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
noAnimation
|
||||
? ''
|
||||
: 'has-[button:not(:disabled):active]:scale-[0.98] transition-transform duration-150 ease-out'
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -114,6 +114,7 @@ const handlePageChange = event => {
|
||||
<slot name="action" />
|
||||
</div>
|
||||
</div>
|
||||
<slot name="subHeader" />
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex-1 px-6 overflow-y-auto">
|
||||
|
||||
@@ -64,20 +64,23 @@ const bulkCheckboxState = computed({
|
||||
class="flex items-center gap-3 py-1 ltr:pl-3 rtl:pr-3 ltr:pr-4 rtl:pl-4 rounded-lg bg-n-solid-2 outline outline-1 outline-n-container shadow"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="flex items-center gap-1.5 min-w-0">
|
||||
<Checkbox
|
||||
v-model="bulkCheckboxState"
|
||||
:indeterminate="isIndeterminate"
|
||||
/>
|
||||
<span class="text-sm font-medium text-n-slate-12 tabular-nums">
|
||||
<span
|
||||
class="text-sm font-medium truncate text-n-slate-12 tabular-nums"
|
||||
>
|
||||
{{ selectAllLabel }}
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-sm text-n-slate-10 tabular-nums">
|
||||
<span class="text-sm text-n-slate-10 truncate tabular-nums">
|
||||
{{ selectedCountLabel }}
|
||||
</span>
|
||||
<div class="h-4 w-px bg-n-strong" />
|
||||
<slot name="secondary-actions" />
|
||||
</div>
|
||||
<div class="h-4 w-px bg-n-strong" />
|
||||
<div class="flex items-center gap-3">
|
||||
<slot name="actions" :selected-count="selectedCount">
|
||||
<Button
|
||||
|
||||
@@ -9,6 +9,7 @@ import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.v
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue';
|
||||
import Policy from 'dashboard/components/policy.vue';
|
||||
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
@@ -59,6 +60,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showActions: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['action', 'navigate', 'select', 'hover']);
|
||||
@@ -159,73 +164,116 @@ const handleDocumentableClick = () => {
|
||||
<span class="text-n-slate-11 text-sm line-clamp-5">
|
||||
{{ answer }}
|
||||
</span>
|
||||
<div v-if="!compact" class="items-center justify-between hidden lg:flex">
|
||||
<div class="inline-flex items-center">
|
||||
<span
|
||||
class="text-sm shrink-0 truncate text-n-slate-11 inline-flex items-center gap-1"
|
||||
>
|
||||
<i class="i-woot-captain" />
|
||||
{{ assistant?.name || '' }}
|
||||
</span>
|
||||
<div
|
||||
v-if="documentable"
|
||||
class="shrink-0 text-sm text-n-slate-11 inline-flex line-clamp-1 gap-1 ml-3"
|
||||
>
|
||||
<div
|
||||
v-if="!compact"
|
||||
class="flex items-start justify-between flex-col-reverse md:flex-row gap-3"
|
||||
>
|
||||
<Policy v-if="showActions" :permissions="['administrator']">
|
||||
<div class="flex items-center gap-2 sm:gap-5 w-full">
|
||||
<Button
|
||||
v-if="status === 'pending'"
|
||||
:label="$t('CAPTAIN.RESPONSES.OPTIONS.APPROVE')"
|
||||
icon="i-lucide-circle-check-big"
|
||||
sm
|
||||
link
|
||||
class="hover:!no-underline"
|
||||
@click="
|
||||
handleAssistantAction({ action: 'approve', value: 'approve' })
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
:label="$t('CAPTAIN.RESPONSES.OPTIONS.EDIT_RESPONSE')"
|
||||
icon="i-lucide-pencil-line"
|
||||
sm
|
||||
slate
|
||||
link
|
||||
class="hover:!no-underline"
|
||||
@click="
|
||||
handleAssistantAction({
|
||||
action: 'edit',
|
||||
value: 'edit',
|
||||
})
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
:label="$t('CAPTAIN.RESPONSES.OPTIONS.DELETE_RESPONSE')"
|
||||
icon="i-lucide-trash"
|
||||
sm
|
||||
ruby
|
||||
link
|
||||
class="hover:!no-underline"
|
||||
@click="
|
||||
handleAssistantAction({ action: 'delete', value: 'delete' })
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</Policy>
|
||||
<div
|
||||
class="flex items-center gap-3"
|
||||
:class="{ 'justify-between w-full': !showActions }"
|
||||
>
|
||||
<div class="inline-flex items-center gap-3 min-w-0">
|
||||
<span
|
||||
v-if="documentable.type === 'Captain::Document'"
|
||||
class="inline-flex items-center gap-1 truncate over"
|
||||
v-if="status === 'approved'"
|
||||
class="text-sm shrink-0 truncate text-n-slate-11 inline-flex items-center gap-1"
|
||||
>
|
||||
<i class="i-ph-files-light text-base" />
|
||||
<span class="max-w-96 truncate" :title="documentable.name">
|
||||
<Icon icon="i-woot-captain" class="size-3.5" />
|
||||
{{ assistant?.name || '' }}
|
||||
</span>
|
||||
<div
|
||||
v-if="documentable"
|
||||
class="text-sm text-n-slate-11 grid grid-cols-[auto_1fr] items-center gap-1 min-w-0"
|
||||
>
|
||||
<Icon
|
||||
v-if="documentable.type === 'Captain::Document'"
|
||||
icon="i-ph-files-light"
|
||||
class="size-3.5"
|
||||
/>
|
||||
<Icon
|
||||
v-else-if="documentable.type === 'User'"
|
||||
icon="i-ph-user-circle-plus"
|
||||
class="size-3.5"
|
||||
/>
|
||||
<Icon
|
||||
v-else-if="documentable.type === 'Conversation'"
|
||||
icon="i-ph-chat-circle-dots"
|
||||
class="size-3.5"
|
||||
/>
|
||||
<span
|
||||
v-if="documentable.type === 'Captain::Document'"
|
||||
class="truncate"
|
||||
:title="documentable.name"
|
||||
>
|
||||
{{ documentable.name }}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
v-if="documentable.type === 'User'"
|
||||
class="inline-flex items-center gap-1"
|
||||
>
|
||||
<i class="i-ph-user-circle-plus text-base" />
|
||||
<span
|
||||
class="max-w-96 truncate"
|
||||
v-else-if="documentable.type === 'User'"
|
||||
class="truncate"
|
||||
:title="documentable.available_name"
|
||||
>
|
||||
{{ documentable.available_name }}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
v-else-if="documentable.type === 'Conversation'"
|
||||
class="inline-flex items-center gap-1 group cursor-pointer"
|
||||
role="button"
|
||||
@click="handleDocumentableClick"
|
||||
>
|
||||
<i class="i-ph-chat-circle-dots text-base" />
|
||||
<span class="group-hover:underline">
|
||||
<span
|
||||
v-else-if="documentable.type === 'Conversation'"
|
||||
class="hover:underline truncate cursor-pointer"
|
||||
role="button"
|
||||
@click="handleDocumentableClick"
|
||||
>
|
||||
{{
|
||||
t(`CAPTAIN.RESPONSES.DOCUMENTABLE.CONVERSATION`, {
|
||||
id: documentable.display_id,
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
</span>
|
||||
<span v-else />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="status !== 'approved'"
|
||||
class="shrink-0 text-sm text-n-slate-11 line-clamp-1 inline-flex items-center gap-1 ml-3"
|
||||
class="shrink-0 text-sm text-n-slate-11 line-clamp-1 inline-flex items-center gap-1"
|
||||
>
|
||||
<i
|
||||
class="i-ph-stack text-base"
|
||||
:title="t('CAPTAIN.RESPONSES.STATUS.TITLE')"
|
||||
/>
|
||||
{{ t(`CAPTAIN.RESPONSES.STATUS.${status.toUpperCase()}`) }}
|
||||
<Icon icon="i-ph-calendar-dot" class="size-3.5" />
|
||||
{{ timestamp }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="shrink-0 text-sm text-n-slate-11 line-clamp-1 inline-flex items-center gap-1 ml-3"
|
||||
>
|
||||
<i class="i-ph-calendar-dot" />
|
||||
{{ timestamp }}
|
||||
</div>
|
||||
</div>
|
||||
</CardLayout>
|
||||
</template>
|
||||
|
||||
+47
-9
@@ -6,16 +6,39 @@ import ResponseCard from 'dashboard/components-next/captain/assistant/ResponseCa
|
||||
import FeatureSpotlight from 'dashboard/components-next/feature-spotlight/FeatureSpotlight.vue';
|
||||
import { responsesList } from 'dashboard/components-next/captain/pageComponents/emptyStates/captainEmptyStateContent.js';
|
||||
|
||||
const emit = defineEmits(['click']);
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
variant: {
|
||||
type: String,
|
||||
default: 'approved',
|
||||
validator: value => ['approved', 'pending'].includes(value),
|
||||
},
|
||||
hasActiveFilters: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click', 'clearFilters']);
|
||||
|
||||
const isApproved = computed(() => props.variant === 'approved');
|
||||
const isPending = computed(() => props.variant === 'pending');
|
||||
|
||||
const { isOnChatwootCloud } = useAccount();
|
||||
|
||||
const onClick = () => {
|
||||
emit('click');
|
||||
};
|
||||
|
||||
const onClearFilters = () => {
|
||||
emit('clearFilters');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FeatureSpotlight
|
||||
v-if="isApproved"
|
||||
:title="$t('CAPTAIN.RESPONSES.EMPTY_STATE.FEATURE_SPOTLIGHT.TITLE')"
|
||||
:note="$t('CAPTAIN.RESPONSES.EMPTY_STATE.FEATURE_SPOTLIGHT.NOTE')"
|
||||
fallback-thumbnail="/assets/images/dashboard/captain/faqs-light.svg"
|
||||
@@ -25,11 +48,16 @@ const onClick = () => {
|
||||
class="mb-8"
|
||||
/>
|
||||
<EmptyStateLayout
|
||||
:title="$t('CAPTAIN.RESPONSES.EMPTY_STATE.TITLE')"
|
||||
:subtitle="$t('CAPTAIN.RESPONSES.EMPTY_STATE.SUBTITLE')"
|
||||
:title="
|
||||
isPending
|
||||
? $t('CAPTAIN.RESPONSES.EMPTY_STATE.NO_PENDING_TITLE')
|
||||
: $t('CAPTAIN.RESPONSES.EMPTY_STATE.TITLE')
|
||||
"
|
||||
:subtitle="isApproved ? $t('CAPTAIN.RESPONSES.EMPTY_STATE.SUBTITLE') : ''"
|
||||
:action-perms="['administrator']"
|
||||
:show-backdrop="isApproved"
|
||||
>
|
||||
<template #empty-state-item>
|
||||
<template v-if="isApproved" #empty-state-item>
|
||||
<div class="grid grid-cols-1 gap-4 p-px overflow-hidden">
|
||||
<ResponseCard
|
||||
v-for="(response, index) in responsesList.slice(0, 5)"
|
||||
@@ -45,11 +73,21 @@ const onClick = () => {
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<Button
|
||||
:label="$t('CAPTAIN.RESPONSES.ADD_NEW')"
|
||||
icon="i-lucide-plus"
|
||||
@click="onClick"
|
||||
/>
|
||||
<div class="flex flex-col items-center gap-3">
|
||||
<Button
|
||||
v-if="isApproved"
|
||||
:label="$t('CAPTAIN.RESPONSES.ADD_NEW')"
|
||||
icon="i-lucide-plus"
|
||||
@click="onClick"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="isPending && hasActiveFilters"
|
||||
:label="$t('CAPTAIN.RESPONSES.EMPTY_STATE.CLEAR_SEARCH')"
|
||||
variant="link"
|
||||
size="sm"
|
||||
@click="onClearFilters"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</EmptyStateLayout>
|
||||
</template>
|
||||
|
||||
@@ -96,6 +96,7 @@ watch(
|
||||
:label="selectedLabel"
|
||||
trailing-icon
|
||||
:disabled="disabled"
|
||||
no-animation
|
||||
class="justify-between w-full !px-3 !py-2.5 text-n-slate-12 font-normal group-hover/combobox:border-n-slate-6 focus:outline-n-brand"
|
||||
:class="{
|
||||
focused: open,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import ButtonGroup from 'dashboard/components-next/buttonGroup/ButtonGroup.vue';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useMapGetter } from 'dashboard/composables/store';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
@@ -53,14 +54,17 @@ const toggleSidebar = () => {
|
||||
v-if="showCopilotLauncher"
|
||||
class="fixed bottom-4 ltr:right-4 rtl:left-4 z-50"
|
||||
>
|
||||
<div class="rounded-full bg-n-alpha-2 p-1">
|
||||
<ButtonGroup
|
||||
class="rounded-full bg-n-alpha-2 backdrop-blur-lg p-1 shadow hover:shadow-md"
|
||||
>
|
||||
<Button
|
||||
icon="i-woot-captain"
|
||||
class="!rounded-full !bg-n-solid-3 dark:!bg-n-alpha-2 !text-n-slate-12 text-xl"
|
||||
no-animation
|
||||
class="!rounded-full !bg-n-solid-3 dark:!bg-n-alpha-2 !text-n-slate-12 text-xl transition-all duration-200 ease-out hover:brightness-110"
|
||||
lg
|
||||
@click="toggleSidebar"
|
||||
/>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<template v-else />
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<script setup>
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-n-slate-12 max-w-80 flex flex-col gap-2.5">
|
||||
<div class="p-3 bg-n-alpha-2 rounded-xl">
|
||||
<span
|
||||
v-dompurify-html="message.content"
|
||||
class="prose prose-bubble font-medium text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<Button label="Call us" slate class="!text-n-blue-text w-full" />
|
||||
<Button
|
||||
label="Visit our website"
|
||||
slate
|
||||
class="!text-n-blue-text w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,32 @@
|
||||
<script setup>
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-n-alpha-2 divide-y divide-n-strong text-n-slate-12 rounded-xl max-w-80"
|
||||
>
|
||||
<div class="px-3 py-2.5">
|
||||
<img :src="message.image_url" class="max-h-44 rounded-lg w-full" />
|
||||
<div class="pt-2.5 flex flex-col gap-2">
|
||||
<h6 class="font-semibold">{{ message.title }}</h6>
|
||||
<span
|
||||
v-dompurify-html="message.content"
|
||||
class="prose prose-bubble text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-3 flex items-center justify-center">
|
||||
<Button label="Call us to order" link class="hover:!no-underline" />
|
||||
</div>
|
||||
<div class="p-3 flex items-center justify-center">
|
||||
<Button label="Visit our store" link class="hover:!no-underline" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script setup>
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-n-alpha-2 divide-y divide-n-strong text-n-slate-12 rounded-xl max-w-80"
|
||||
>
|
||||
<div class="p-3">
|
||||
<span
|
||||
v-dompurify-html="message.content"
|
||||
class="prose prose-bubble font-medium text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div class="p-3 flex items-center justify-center">
|
||||
<Button label="See options" link class="hover:!no-underline" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-n-alpha-2 text-n-slate-12 rounded-xl flex flex-col gap-2.5 p-3 max-w-80"
|
||||
>
|
||||
<img :src="message.image_url" class="max-h-44 rounded-lg w-full" />
|
||||
<span
|
||||
v-dompurify-html="message.content"
|
||||
class="prose prose-bubble font-medium text-sm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,68 @@
|
||||
<script setup>
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-n-alpha-2 divide-y divide-n-strong text-n-slate-12 rounded-xl max-w-80"
|
||||
>
|
||||
<div class="p-3">
|
||||
<span
|
||||
v-dompurify-html="message.content"
|
||||
class="prose prose-bubble font-medium text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div class="p-3 flex items-center justify-center">
|
||||
<Button label="No, that will be all" link class="hover:!no-underline">
|
||||
<template #icon>
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
class="stroke-n-blue-text"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M.667 6.654 5.315.667v3.326c7.968 0 8.878 6.46 8.656 10.007l-.005-.027c-.334-1.79-.474-4.658-8.65-4.658v3.327z"
|
||||
stroke-width="1.333"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="p-3 flex items-center justify-center">
|
||||
<Button
|
||||
label="I want to talk to an agents"
|
||||
link
|
||||
class="hover:!no-underline"
|
||||
>
|
||||
<template #icon>
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
class="stroke-n-blue-text"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M.667 6.654 5.315.667v3.326c7.968 0 8.878 6.46 8.656 10.007l-.005-.027c-.334-1.79-.474-4.658-8.65-4.658v3.327z"
|
||||
stroke-width="1.333"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-n-alpha-2 text-n-slate-12 rounded-xl p-3 max-w-80">
|
||||
<span v-dompurify-html="message.content" class="prose prose-bubble" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -47,6 +47,7 @@ const isReel = computed(() => {
|
||||
'max-w-48': isReel,
|
||||
'max-w-full': !isReel,
|
||||
}"
|
||||
@click.stop
|
||||
@error="handleError"
|
||||
/>
|
||||
</div>
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<script setup>
|
||||
import CallToAction from '../../bubbles/Template/CallToAction.vue';
|
||||
|
||||
const message = {
|
||||
content:
|
||||
'We have super cool products going live! Pre-order and customize products. Contact us for more details',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story
|
||||
title="Components/Message Bubbles/Template/CallToAction"
|
||||
:layout="{ type: 'grid', width: '600px' }"
|
||||
>
|
||||
<Variant title="Call To Action">
|
||||
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
|
||||
<CallToAction :message="message" />
|
||||
</div>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script setup>
|
||||
import Card from '../../bubbles/Template/Card.vue';
|
||||
|
||||
const message = {
|
||||
title: 'Two in one cake (1 pound)',
|
||||
content: 'Customize your order for special occasions',
|
||||
image_url:
|
||||
'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=500&h=300&fit=crop',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story
|
||||
title="Components/Message Bubbles/Template/Card"
|
||||
:layout="{ type: 'grid', width: '600px' }"
|
||||
>
|
||||
<Variant title="Card">
|
||||
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
|
||||
<Card :message="message" />
|
||||
</div>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<script setup>
|
||||
import ListPicker from '../../bubbles/Template/ListPicker.vue';
|
||||
|
||||
const message = {
|
||||
content: `Hey there! Thanks for reaching out to us. Could you let us know
|
||||
what you need to help us better assist you? `,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story
|
||||
title="Components/Message Bubbles/Template/ListPicker"
|
||||
:layout="{ type: 'grid', width: '600px' }"
|
||||
>
|
||||
<Variant title="ListPicker">
|
||||
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
|
||||
<ListPicker :message="message" />
|
||||
</div>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script setup>
|
||||
import Media from '../../bubbles/Template/Media.vue';
|
||||
|
||||
const message = {
|
||||
content:
|
||||
'Welcome to our Diwali sale! Get flat 50% off on select items. Hurry now!',
|
||||
image_url:
|
||||
'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=500&h=300&fit=crop',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story
|
||||
title="Components/Message Bubbles/Template/Media"
|
||||
:layout="{ type: 'grid', width: '600px' }"
|
||||
>
|
||||
<Variant title="Image Media">
|
||||
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
|
||||
<Media :message="message" />
|
||||
</div>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<script setup>
|
||||
import QuickReply from '../../bubbles/Template/QuickReply.vue';
|
||||
|
||||
const message = {
|
||||
content: `Hey there! Thanks for reaching out to us. Could you let us know
|
||||
what you need to help us better assist you?`,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story
|
||||
title="Components/Message Bubbles/Template/QuickReply"
|
||||
:layout="{ type: 'grid', width: '600px' }"
|
||||
>
|
||||
<Variant title="Quick Replies">
|
||||
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
|
||||
<QuickReply :message="message" />
|
||||
</div>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
import Text from '../../bubbles/Template/Text.vue';
|
||||
|
||||
const message = {
|
||||
content: 'Hello John, how may we assist you?',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story
|
||||
title="Components/Message Bubbles/Template/Text"
|
||||
:layout="{ type: 'grid', width: '600px' }"
|
||||
>
|
||||
<Variant title="Default Text">
|
||||
<div class="p-4 bg-n-background rounded-lg w-full min-w-4xl">
|
||||
<Text :message="message" />
|
||||
</div>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
@@ -2,6 +2,7 @@
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import ButtonGroup from 'dashboard/components-next/buttonGroup/ButtonGroup.vue';
|
||||
|
||||
defineProps({
|
||||
isMobileSidebarOpen: {
|
||||
@@ -45,14 +46,17 @@ const toggleSidebar = () => {
|
||||
},
|
||||
]"
|
||||
>
|
||||
<div class="rounded-full bg-n-alpha-2 p-1">
|
||||
<ButtonGroup
|
||||
class="rounded-full bg-n-alpha-2 backdrop-blur-lg p-1 shadow hover:shadow-md"
|
||||
>
|
||||
<Button
|
||||
icon="i-lucide-menu"
|
||||
class="!rounded-full !bg-n-solid-3 dark:!bg-n-alpha-2 !text-n-slate-12 text-xl"
|
||||
no-animation
|
||||
class="!rounded-full !bg-n-solid-3 dark:!bg-n-alpha-2 !text-n-slate-12 text-xl transition-all duration-200 ease-out hover:brightness-110"
|
||||
lg
|
||||
@click="toggleSidebar"
|
||||
/>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<template v-else />
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref, onMounted, nextTick } from 'vue';
|
||||
import { useResizeObserver } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
initialActiveTab: {
|
||||
type: Number,
|
||||
@@ -22,6 +24,32 @@ const emit = defineEmits(['tabChanged']);
|
||||
|
||||
const activeTab = computed(() => props.initialActiveTab);
|
||||
|
||||
const tabRefs = ref([]);
|
||||
const indicatorStyle = ref({});
|
||||
const enableTransition = ref(false);
|
||||
|
||||
const activeElement = computed(() => tabRefs.value[activeTab.value]);
|
||||
|
||||
const updateIndicator = () => {
|
||||
if (!activeElement.value) return;
|
||||
|
||||
indicatorStyle.value = {
|
||||
left: `${activeElement.value.offsetLeft}px`,
|
||||
width: `${activeElement.value.offsetWidth}px`,
|
||||
};
|
||||
};
|
||||
|
||||
useResizeObserver(activeElement, () => {
|
||||
if (enableTransition.value || !activeElement.value) updateIndicator();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
updateIndicator();
|
||||
nextTick(() => {
|
||||
enableTransition.value = true;
|
||||
});
|
||||
});
|
||||
|
||||
const selectTab = index => {
|
||||
emit('tabChanged', props.tabs[index]);
|
||||
};
|
||||
@@ -37,20 +65,30 @@ const showDivider = index => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center h-8 rounded-lg bg-n-alpha-1 w-fit">
|
||||
<div
|
||||
class="relative flex items-center h-8 rounded-lg bg-n-alpha-1 w-fit transition-all duration-200 ease-out has-[button:active]:scale-[1.01]"
|
||||
>
|
||||
<div
|
||||
class="absolute rounded-lg bg-n-solid-active shadow-sm pointer-events-none h-8 outline-1 outline outline-n-container inset-y-0"
|
||||
:class="{ 'transition-all duration-300 ease-out': enableTransition }"
|
||||
:style="indicatorStyle"
|
||||
/>
|
||||
|
||||
<template v-for="(tab, index) in tabs" :key="index">
|
||||
<button
|
||||
class="relative px-4 truncate py-1.5 text-sm border-0 outline-1 outline rounded-lg transition-colors duration-300 ease-in-out hover:text-n-brand"
|
||||
:ref="el => (tabRefs[index] = el)"
|
||||
class="relative z-10 px-4 truncate py-1.5 text-sm border-0 outline-1 outline-transparent rounded-lg transition-all duration-200 ease-out hover:text-n-brand active:scale-[1.02]"
|
||||
:class="[
|
||||
activeTab === index
|
||||
? 'text-n-blue-text bg-n-solid-active outline-n-container dark:outline-transparent'
|
||||
: 'text-n-slate-10 outline-transparent h-8',
|
||||
? 'text-n-blue-text scale-100'
|
||||
: 'text-n-slate-10 scale-[0.98]',
|
||||
]"
|
||||
@click="selectTab(index)"
|
||||
>
|
||||
{{ tab.label }} {{ tab.count ? `(${tab.count})` : '' }}
|
||||
</button>
|
||||
<div
|
||||
v-if="index < tabs.length - 1"
|
||||
class="w-px h-3.5 rounded my-auto transition-colors duration-300 ease-in-out"
|
||||
:class="
|
||||
showDivider(index)
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
CMD_RESOLVE_CONVERSATION,
|
||||
} from 'dashboard/helper/commandbar/events';
|
||||
|
||||
import ButtonGroup from 'dashboard/components-next/buttonGroup/ButtonGroup.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const store = useStore();
|
||||
@@ -133,7 +134,7 @@ useEmitter(CMD_RESOLVE_CONVERSATION, onCmdResolveConversation);
|
||||
|
||||
<template>
|
||||
<div class="relative flex items-center justify-end resolve-actions">
|
||||
<div
|
||||
<ButtonGroup
|
||||
class="rounded-lg shadow outline-1 outline flex-shrink-0"
|
||||
:class="!showOpenButton ? 'outline-n-container' : 'outline-transparent'"
|
||||
>
|
||||
@@ -142,6 +143,7 @@ useEmitter(CMD_RESOLVE_CONVERSATION, onCmdResolveConversation);
|
||||
:label="t('CONVERSATION.HEADER.RESOLVE_ACTION')"
|
||||
size="sm"
|
||||
color="slate"
|
||||
no-animation
|
||||
class="ltr:rounded-r-none rtl:rounded-l-none !outline-0"
|
||||
:is-loading="isLoading"
|
||||
@click="onCmdResolveConversation"
|
||||
@@ -151,6 +153,7 @@ useEmitter(CMD_RESOLVE_CONVERSATION, onCmdResolveConversation);
|
||||
:label="t('CONVERSATION.HEADER.REOPEN_ACTION')"
|
||||
size="sm"
|
||||
color="slate"
|
||||
no-animation
|
||||
class="ltr:rounded-r-none rtl:rounded-l-none !outline-0"
|
||||
:is-loading="isLoading"
|
||||
@click="onCmdOpenConversation"
|
||||
@@ -160,6 +163,7 @@ useEmitter(CMD_RESOLVE_CONVERSATION, onCmdResolveConversation);
|
||||
:label="t('CONVERSATION.HEADER.OPEN_ACTION')"
|
||||
size="sm"
|
||||
color="slate"
|
||||
no-animation
|
||||
:is-loading="isLoading"
|
||||
@click="onCmdOpenConversation"
|
||||
/>
|
||||
@@ -169,12 +173,13 @@ useEmitter(CMD_RESOLVE_CONVERSATION, onCmdResolveConversation);
|
||||
icon="i-lucide-chevron-down"
|
||||
:disabled="isLoading"
|
||||
size="sm"
|
||||
no-animation
|
||||
class="ltr:rounded-l-none rtl:rounded-r-none !outline-0"
|
||||
color="slate"
|
||||
trailing-icon
|
||||
@click="openDropdown"
|
||||
/>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
<div
|
||||
v-if="showActionsDropdown"
|
||||
v-on-clickaway="closeDropdown"
|
||||
|
||||
@@ -302,7 +302,16 @@ function isBodyEmpty(content) {
|
||||
}
|
||||
|
||||
function handleEmptyBodyWithSignature() {
|
||||
const { schema, tr } = state;
|
||||
const { schema, tr, doc } = state;
|
||||
|
||||
const isEmptyParagraph = node =>
|
||||
node && node.type === schema.nodes.paragraph && node.content.size === 0;
|
||||
|
||||
// Check if empty paragraph already exists to prevent duplicates when toggling signatures
|
||||
if (isEmptyParagraph(doc.firstChild)) {
|
||||
focusEditorInputField('start');
|
||||
return;
|
||||
}
|
||||
|
||||
// create a paragraph node and
|
||||
// start a transaction to append it at the end
|
||||
|
||||
@@ -59,7 +59,7 @@ const translateValue = computed(() => {
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="flex items-center w-auto h-8 p-1 transition-all border rounded-full bg-n-alpha-2 group relative duration-300 ease-in-out z-0"
|
||||
class="flex items-center w-auto h-8 p-1 transition-all border rounded-full bg-n-alpha-2 group relative duration-300 ease-in-out z-0 active:scale-[0.995] active:duration-75"
|
||||
:disabled="disabled"
|
||||
:class="{
|
||||
'cursor-not-allowed': disabled,
|
||||
|
||||
+92
-159
@@ -1,100 +1,129 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useMapGetter } from 'dashboard/composables/store';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
emits: ['update', 'close', 'assign'],
|
||||
data() {
|
||||
return {
|
||||
query: '',
|
||||
selectedLabels: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({ labels: 'labels/getLabels' }),
|
||||
filteredLabels() {
|
||||
return this.labels.filter(label =>
|
||||
label.title.toLowerCase().includes(this.query.toLowerCase())
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isLabelSelected(label) {
|
||||
return this.selectedLabels.includes(label);
|
||||
},
|
||||
assignLabels(key) {
|
||||
this.$emit('update', key);
|
||||
},
|
||||
onClose() {
|
||||
this.$emit('close');
|
||||
},
|
||||
},
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
import Input from 'dashboard/components-next/input/Input.vue';
|
||||
|
||||
const emit = defineEmits(['close', 'assign']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const labels = useMapGetter('labels/getLabels');
|
||||
|
||||
const query = ref('');
|
||||
const selectedLabels = ref([]);
|
||||
|
||||
const filteredLabels = computed(() => {
|
||||
if (!query.value) return labels.value;
|
||||
return labels.value.filter(label =>
|
||||
label.title.toLowerCase().includes(query.value.toLowerCase())
|
||||
);
|
||||
});
|
||||
|
||||
const hasLabels = computed(() => labels.value.length > 0);
|
||||
const hasFilteredLabels = computed(() => filteredLabels.value.length > 0);
|
||||
|
||||
const isLabelSelected = label => {
|
||||
return selectedLabels.value.includes(label);
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const handleAssign = () => {
|
||||
if (selectedLabels.value.length > 0) {
|
||||
emit('assign', selectedLabels.value);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-on-clickaway="onClose" class="labels-container">
|
||||
<div
|
||||
v-on-click-outside="onClose"
|
||||
class="absolute ltr:right-2 rtl:left-2 top-12 origin-top-right z-20 w-60 bg-n-alpha-3 backdrop-blur-[100px] border-n-weak rounded-lg border border-solid shadow-md"
|
||||
role="dialog"
|
||||
aria-labelledby="label-dialog-title"
|
||||
>
|
||||
<div class="triangle">
|
||||
<svg height="12" viewBox="0 0 24 12" width="24">
|
||||
<path d="M20 12l-8-8-12 12" fill-rule="evenodd" stroke-width="1px" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex items-center justify-between header">
|
||||
<span>{{ $t('BULK_ACTION.LABELS.ASSIGN_LABELS') }}</span>
|
||||
<div class="flex items-center justify-between p-2.5">
|
||||
<span class="text-sm font-medium">{{
|
||||
t('BULK_ACTION.LABELS.ASSIGN_LABELS')
|
||||
}}</span>
|
||||
<NextButton ghost xs slate icon="i-lucide-x" @click="onClose" />
|
||||
</div>
|
||||
<div class="labels-list">
|
||||
<header class="labels-list__header">
|
||||
<div
|
||||
class="flex items-center justify-between h-8 gap-2 label-list-search"
|
||||
>
|
||||
<fluent-icon icon="search" class="search-icon" size="16" />
|
||||
<input
|
||||
v-model="query"
|
||||
type="search"
|
||||
:placeholder="$t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')"
|
||||
class="reset-base !outline-0 !text-sm label--search_input"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col max-h-60 min-h-0">
|
||||
<header class="py-2 px-2.5">
|
||||
<Input
|
||||
v-model="query"
|
||||
type="search"
|
||||
:placeholder="t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')"
|
||||
icon-left="i-lucide-search"
|
||||
size="sm"
|
||||
class="w-full"
|
||||
:aria-label="t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')"
|
||||
/>
|
||||
</header>
|
||||
<ul class="labels-list__body">
|
||||
<ul
|
||||
v-if="hasLabels"
|
||||
class="flex-1 overflow-y-auto m-0 list-none"
|
||||
role="listbox"
|
||||
:aria-label="t('BULK_ACTION.LABELS.ASSIGN_LABELS')"
|
||||
>
|
||||
<li v-if="!hasFilteredLabels" class="p-2 text-center">
|
||||
<span class="text-sm text-n-slate-11">{{
|
||||
t('BULK_ACTION.LABELS.NO_LABELS_FOUND')
|
||||
}}</span>
|
||||
</li>
|
||||
<li
|
||||
v-for="label in filteredLabels"
|
||||
:key="label.id"
|
||||
class="label__list-item"
|
||||
class="my-1 mx-0 py-0 px-2.5"
|
||||
role="option"
|
||||
:aria-selected="isLabelSelected(label.title)"
|
||||
>
|
||||
<label
|
||||
class="item"
|
||||
:class="{ 'label-selected': isLabelSelected(label.title) }"
|
||||
class="items-center rounded-md cursor-pointer flex py-1 px-2.5 hover:bg-n-slate-3 dark:hover:bg-n-solid-3 has-[:checked]:bg-n-slate-2"
|
||||
>
|
||||
<input
|
||||
v-model="selectedLabels"
|
||||
type="checkbox"
|
||||
:value="label.title"
|
||||
class="label-checkbox"
|
||||
class="my-0 ltr:mr-2.5 rtl:ml-2.5"
|
||||
:aria-label="label.title"
|
||||
/>
|
||||
<span
|
||||
class="overflow-hidden label-title whitespace-nowrap text-ellipsis"
|
||||
class="overflow-hidden flex-grow w-full text-sm whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ label.title }}
|
||||
</span>
|
||||
<span
|
||||
class="label-pill"
|
||||
class="rounded-md h-3 w-3 flex-shrink-0 border border-solid border-n-weak"
|
||||
:style="{ backgroundColor: label.color }"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<footer class="labels-list__footer">
|
||||
<div v-else class="p-2 text-center">
|
||||
<span class="text-sm text-n-slate-11">{{
|
||||
t('CONTACTS_BULK_ACTIONS.NO_LABELS_FOUND')
|
||||
}}</span>
|
||||
</div>
|
||||
<footer class="p-2">
|
||||
<NextButton
|
||||
sm
|
||||
type="submit"
|
||||
:label="$t('BULK_ACTION.LABELS.ASSIGN_SELECTED_LABELS')"
|
||||
class="w-full"
|
||||
:label="t('BULK_ACTION.LABELS.ASSIGN_SELECTED_LABELS')"
|
||||
:disabled="!selectedLabels.length"
|
||||
@click="$emit('assign', selectedLabels)"
|
||||
@click="handleAssign"
|
||||
/>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -102,107 +131,11 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.labels-list {
|
||||
@apply flex flex-col max-h-[15rem] min-h-[auto];
|
||||
.triangle {
|
||||
@apply block z-10 absolute text-left -top-3 ltr:right-[--triangle-position] rtl:left-[--triangle-position];
|
||||
|
||||
.labels-list__header {
|
||||
@apply bg-n-alpha-3 backdrop-blur-[100px] py-0 px-2.5;
|
||||
svg path {
|
||||
@apply fill-n-alpha-3 backdrop-blur-[100px] stroke-n-weak;
|
||||
}
|
||||
|
||||
.labels-list__body {
|
||||
@apply flex-1 overflow-y-auto py-2.5 mx-0;
|
||||
}
|
||||
|
||||
.labels-list__footer {
|
||||
@apply p-2;
|
||||
|
||||
button {
|
||||
@apply w-full;
|
||||
|
||||
.button__content {
|
||||
@apply text-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-list-search {
|
||||
@apply bg-n-alpha-black2 py-0 px-2.5 border border-solid border-n-strong rounded-md;
|
||||
|
||||
.search-icon {
|
||||
@apply text-n-slate-10;
|
||||
}
|
||||
|
||||
.label--search_input {
|
||||
@apply border-0 text-xs m-0 dark:bg-transparent bg-transparent h-[unset] w-full;
|
||||
}
|
||||
}
|
||||
|
||||
.labels-container {
|
||||
@apply absolute ltr:right-2 rtl:left-2 top-12 origin-top-right w-auto z-20 max-w-[15rem] min-w-[15rem] bg-n-alpha-3 backdrop-blur-[100px] border-n-weak rounded-lg border border-solid shadow-md;
|
||||
|
||||
.header {
|
||||
@apply p-2.5;
|
||||
|
||||
span {
|
||||
@apply text-sm font-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
@apply max-h-[15rem] overflow-y-auto;
|
||||
|
||||
.label__list-container {
|
||||
@apply h-full;
|
||||
}
|
||||
}
|
||||
|
||||
.triangle {
|
||||
@apply block z-10 absolute text-left -top-3 ltr:right-[--triangle-position] rtl:left-[--triangle-position];
|
||||
|
||||
svg path {
|
||||
@apply fill-n-alpha-3 backdrop-blur-[100px] stroke-n-weak;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply m-0 list-none;
|
||||
}
|
||||
|
||||
.labels-placeholder {
|
||||
@apply p-2;
|
||||
}
|
||||
|
||||
.label__list-item {
|
||||
@apply my-1 mx-0 py-0 px-2.5;
|
||||
|
||||
.item {
|
||||
@apply items-center rounded-md cursor-pointer flex py-1 px-2.5 hover:bg-n-slate-3 dark:hover:bg-n-solid-3;
|
||||
|
||||
&.label-selected {
|
||||
@apply bg-n-slate-2;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-sm;
|
||||
}
|
||||
|
||||
.label-checkbox {
|
||||
@apply my-0 ltr:mr-2.5 rtl:ml-2.5;
|
||||
}
|
||||
|
||||
.label-title {
|
||||
@apply flex-grow w-full;
|
||||
}
|
||||
|
||||
.label-pill {
|
||||
@apply rounded-md h-3 w-3 flex-shrink-0 border border-solid border-n-weak;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-container {
|
||||
@apply bg-n-alpha-3 backdrop-blur-[100px] py-0 px-2.5 sticky top-0 z-20;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "በአሁኑ ጊዜ ንቁ እውቂያዎች የሉም 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "All"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Edit",
|
||||
"DELETE_RESPONSE": "Delete"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "إضافة وسم",
|
||||
"NO_LABELS_FOUND": "لم يتم العثور على تسميات لـ",
|
||||
"NO_LABELS_FOUND": "لم يتم العثور على تصنيفات",
|
||||
"ASSIGN_SELECTED_LABELS": "تعيين التسميات المحددة",
|
||||
"ASSIGN_SUCCESFUL": "تم تعيين التسميات بنجاح.",
|
||||
"ASSIGN_FAILED": "فشل في تعيين التسميات ، الرجاء المحاولة مرة أخرى."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "تعيين التسميات",
|
||||
"ASSIGN_LABELS_SUCCESS": "تم تعيين التسميات بنجاح.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "الكل"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "تعديل",
|
||||
"DELETE_RESPONSE": "حذف"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "All"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Edit",
|
||||
"DELETE_RESPONSE": "Delete"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "Няма намерени етикети",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Всички"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Редактирай",
|
||||
"DELETE_RESPONSE": "Изтрий"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "All"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Edit",
|
||||
"DELETE_RESPONSE": "Delete"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assignar etiquetes",
|
||||
"NO_LABELS_FOUND": "No s'han trobat etiquetes per a",
|
||||
"NO_LABELS_FOUND": "No s'han trobat etiquetes",
|
||||
"ASSIGN_SELECTED_LABELS": "Assigna les etiquetes seleccionades",
|
||||
"ASSIGN_SUCCESFUL": "L'etiqueta s'ha assignat correctament.",
|
||||
"ASSIGN_FAILED": "No s'han pogut assignar les etiquetes. Torna-ho a provar."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "L'etiqueta s'ha assignat correctament.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Totes"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Edita",
|
||||
"DELETE_RESPONSE": "Esborrar"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Vše"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Upravit",
|
||||
"DELETE_RESPONSE": "Vymazat"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "Ingen etiketter fundet for",
|
||||
"NO_LABELS_FOUND": "Ingen labels fundet",
|
||||
"ASSIGN_SELECTED_LABELS": "Tildel valgte etiketter",
|
||||
"ASSIGN_SUCCESFUL": "Etiketter tildelt med succes.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Tildel Etiketter",
|
||||
"ASSIGN_LABELS_SUCCESS": "Etiketter tildelt med succes.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Alle"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Rediger",
|
||||
"DELETE_RESPONSE": "Slet"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Labels zuweisen",
|
||||
"NO_LABELS_FOUND": "Keine Labels gefunden für",
|
||||
"NO_LABELS_FOUND": "Keine Labels gefunden",
|
||||
"ASSIGN_SELECTED_LABELS": "Ausgewählte Labels zuweisen",
|
||||
"ASSIGN_SUCCESFUL": "Labels erfolgreich zugewiesen.",
|
||||
"ASSIGN_FAILED": "Labels konnten nicht zugewiesen werden. Bitte versuchen Sie es erneut."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "Im Moment sind keine Kontakte aktiv 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Labels zuweisen",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels erfolgreich zugewiesen.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "Wir konnten die Suche nicht abschließen. Bitte versuch es erneut."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Alle"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Bearbeiten",
|
||||
"DELETE_RESPONSE": "Löschen"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "Δεν βρέθηκαν πρότυπα για",
|
||||
"NO_LABELS_FOUND": "Δεν βρέθηκαν ετικέτες",
|
||||
"ASSIGN_SELECTED_LABELS": "Ανάθεση επιλεγμένων ετικετών",
|
||||
"ASSIGN_SUCCESFUL": "Επιτυχής ανάθεση ετικετών.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Ανάθεση Ετικετών",
|
||||
"ASSIGN_LABELS_SUCCESS": "Επιτυχής ανάθεση ετικετών.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Όλες"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Επεξεργασία",
|
||||
"DELETE_RESPONSE": "Διαγραφή"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -572,6 +572,27 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})",
|
||||
"DELETE_CONTACTS": "Delete",
|
||||
"DELETE_SUCCESS": "Contacts deleted successfully.",
|
||||
"DELETE_FAILED": "Failed to delete contacts.",
|
||||
"DELETE_DIALOG": {
|
||||
"TITLE": "Delete selected contacts",
|
||||
"SINGULAR_TITLE": "Delete selected contact",
|
||||
"DESCRIPTION": "This will permanently delete {count} selected contacts. This action cannot be undone.",
|
||||
"SINGULAR_DESCRIPTION": "This will permanently delete the selected contact. This action cannot be undone.",
|
||||
"CONFIRM_MULTIPLE": "Delete contacts",
|
||||
"CONFIRM_SINGLE": "Delete contact"
|
||||
}
|
||||
},
|
||||
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
|
||||
@@ -861,6 +861,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -900,6 +901,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "All"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -931,13 +936,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Edit",
|
||||
"DELETE_RESPONSE": "Delete"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Asignar etiqueta",
|
||||
"NO_LABELS_FOUND": "No se encontraron etiquetas para",
|
||||
"NO_LABELS_FOUND": "No se encontraron etiquetas",
|
||||
"ASSIGN_SELECTED_LABELS": "Asignar etiquetas seleccionadas",
|
||||
"ASSIGN_SUCCESFUL": "Etiquetas asignadas correctamente.",
|
||||
"ASSIGN_FAILED": "Error al asignar etiquetas, inténtalo de nuevo."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No hay contactos activos por el momento 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Asignar etiquetas",
|
||||
"ASSIGN_LABELS_SUCCESS": "Etiquetas asignadas correctamente.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "No pudimos completar la búsqueda. Por favor, inténtalo de nuevo."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "Preguntas frecuentes",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Todos"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Editar",
|
||||
"DELETE_RESPONSE": "Eliminar"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "All"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Edit",
|
||||
"DELETE_RESPONSE": "Delete"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "برچسب اختصاص دهید",
|
||||
"NO_LABELS_FOUND": "هیچ برچسبی یافت نشد برای",
|
||||
"NO_LABELS_FOUND": "هیچ برچسبی یافت نشد",
|
||||
"ASSIGN_SELECTED_LABELS": "اختصاص برچسبهای انتخاب شده",
|
||||
"ASSIGN_SUCCESFUL": "برچسبها با موفقیت اختصاص یافتند.",
|
||||
"ASSIGN_FAILED": "اختصاص برچسب به صورت موفق انجام نشد، لطفا دوباره امتحان کنید."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "اختصاص برچسبها",
|
||||
"ASSIGN_LABELS_SUCCESS": "برچسبها با موفقیت اختصاص یافتند.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "همه"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "ویرایش",
|
||||
"DELETE_RESPONSE": "حذف"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Kaikki"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Muokkaa",
|
||||
"DELETE_RESPONSE": "Poista"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assigner une étiquette",
|
||||
"NO_LABELS_FOUND": "Aucune étiquette trouvée pour",
|
||||
"NO_LABELS_FOUND": "Aucune étiquette trouvée",
|
||||
"ASSIGN_SELECTED_LABELS": "Assigner les étiquettes sélectionnées",
|
||||
"ASSIGN_SUCCESFUL": "Étiquettes attribuées avec succès.",
|
||||
"ASSIGN_FAILED": "Impossible d'attribuer les étiquettes, veuillez réessayer."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "Aucun contact n'est actif pour le moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assigner des étiquettes",
|
||||
"ASSIGN_LABELS_SUCCESS": "Étiquettes attribuées avec succès.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "Tous"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Modifier",
|
||||
"DELETE_RESPONSE": "Supprimer"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "לא נצאו תוויות עבור",
|
||||
"NO_LABELS_FOUND": "לא נמצאו תוויות",
|
||||
"ASSIGN_SELECTED_LABELS": "שייך תוויות נבחרות",
|
||||
"ASSIGN_SUCCESFUL": "תוויות שוייכו בהצלחה.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "הקצה תוויות",
|
||||
"ASSIGN_LABELS_SUCCESS": "תוויות שוייכו בהצלחה.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "הכל"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "ערוך",
|
||||
"DELETE_RESPONSE": "מחק"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
@@ -863,6 +863,7 @@
|
||||
},
|
||||
"RESPONSES": {
|
||||
"HEADER": "FAQs",
|
||||
"PENDING_FAQS": "Pending FAQs",
|
||||
"ADD_NEW": "Create new FAQ",
|
||||
"DOCUMENTABLE": {
|
||||
"CONVERSATION": "Conversation #{id}"
|
||||
@@ -902,6 +903,10 @@
|
||||
"APPROVED": "Approved",
|
||||
"ALL": "All"
|
||||
},
|
||||
"PENDING_BANNER": {
|
||||
"TITLE": "Captain has found some FAQs your customers were looking for.",
|
||||
"ACTION": "Click here to review"
|
||||
},
|
||||
"FORM_DESCRIPTION": "Add a question and its corresponding answer to the knowledge base and select the assistant it should be associated with.",
|
||||
"CREATE": {
|
||||
"TITLE": "Add an FAQ",
|
||||
@@ -932,13 +937,15 @@
|
||||
"APPROVE_SUCCESS_MESSAGE": "The FAQ was marked as approved"
|
||||
},
|
||||
"OPTIONS": {
|
||||
"APPROVE": "Mark as approved",
|
||||
"EDIT_RESPONSE": "Edit FAQ",
|
||||
"DELETE_RESPONSE": "Delete FAQ"
|
||||
"APPROVE": "Approve",
|
||||
"EDIT_RESPONSE": "Edit",
|
||||
"DELETE_RESPONSE": "Delete"
|
||||
},
|
||||
"EMPTY_STATE": {
|
||||
"TITLE": "No FAQs Found",
|
||||
"NO_PENDING_TITLE": "There are no more pending FAQs to review",
|
||||
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
|
||||
"CLEAR_SEARCH": "Clear active filters",
|
||||
"FEATURE_SPOTLIGHT": {
|
||||
"TITLE": "Captain FAQ",
|
||||
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found for",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
|
||||
@@ -571,6 +571,16 @@
|
||||
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||
}
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
"CLEAR_SELECTION": "Clear selection",
|
||||
"SELECT_ALL": "Select all ({count})"
|
||||
},
|
||||
"COMPOSE_NEW_CONVERSATION": {
|
||||
"CONTACT_SEARCH": {
|
||||
"ERROR_MESSAGE": "We couldn’t complete the search. Please try again."
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user