Merge branch 'chatwoot:develop' into chatwoot/develop

This commit is contained in:
Gabriel Jablonski 2025-07-16 09:20:38 -03:00 committed by GitHub
commit e1a4fdf73b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
406 changed files with 9005 additions and 443 deletions

View File

@ -1,62 +0,0 @@
version: '2'
plugins:
rubocop:
enabled: false
channel: rubocop-0-73
eslint:
enabled: false
csslint:
enabled: true
scss-lint:
enabled: true
brakeman:
enabled: false
checks:
similar-code:
enabled: false
method-count:
enabled: true
config:
threshold: 32
file-lines:
enabled: true
config:
threshold: 300
method-lines:
config:
threshold: 50
exclude_patterns:
- 'spec/'
- '**/specs/**/**'
- '**/spec/**/**'
- 'db/*'
- 'bin/**/*'
- 'db/**/*'
- 'config/**/*'
- 'public/**/*'
- 'vendor/**/*'
- 'node_modules/**/*'
- 'lib/tasks/auto_annotate_models.rake'
- 'app/test-matchers.js'
- 'docs/*'
- '**/*.md'
- '**/*.yml'
- 'app/javascript/dashboard/i18n/locale'
- '**/*.stories.js'
- 'stories/'
- 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js'
- 'app/javascript/shared/constants/countries.js'
- 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js'
- 'app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js'
- 'app/javascript/dashboard/routes/dashboard/settings/automation/constants.js'
- 'app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js'
- 'app/javascript/dashboard/routes/dashboard/settings/reports/constants.js'
- 'app/javascript/dashboard/store/captain/storeFactory.js'
- 'app/javascript/dashboard/i18n/index.js'
- 'app/javascript/widget/i18n/index.js'
- 'app/javascript/survey/i18n/index.js'
- 'app/javascript/shared/constants/locales.js'
- 'app/javascript/dashboard/helper/specs/macrosFixtures.js'
- 'app/javascript/dashboard/routes/dashboard/settings/macros/constants.js'
- '**/fixtures/**'
- '**/*/fixtures.js'

28
.github/workflows/auto-assign-pr.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Auto-assign PR to Author
on:
pull_request:
types: [opened]
jobs:
auto-assign:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Auto-assign PR to author
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
const pull_number = context.payload.pull_request.number;
const author = context.payload.pull_request.user.login;
await github.rest.issues.addAssignees({
owner,
repo,
issue_number: pull_number,
assignees: [author]
});
console.log(`Assigned PR #${pull_number} to ${author}`);

7
.qlty/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*
!configs
!configs/**
!hooks
!hooks/**
!qlty.toml
!.gitignore

View File

@ -0,0 +1,2 @@
ignored:
- DL3008

View File

@ -0,0 +1 @@
source-path=SCRIPTDIR

View File

@ -0,0 +1,8 @@
rules:
document-start: disable
quoted-strings:
required: only-when-needed
extra-allowed: ["{|}"]
key-duplicates: {}
octal-values:
forbid-implicit-octal: true

84
.qlty/qlty.toml Normal file
View File

@ -0,0 +1,84 @@
# This file was automatically generated by `qlty init`.
# You can modify it to suit your needs.
# We recommend you to commit this file to your repository.
#
# This configuration is used by both Qlty CLI and Qlty Cloud.
#
# Qlty CLI -- Code quality toolkit for developers
# Qlty Cloud -- Fully automated Code Health Platform
#
# Try Qlty Cloud: https://qlty.sh
#
# For a guide to configuration, visit https://qlty.sh/d/config
# Or for a full reference, visit https://qlty.sh/d/qlty-toml
config_version = "0"
exclude_patterns = [
"*_min.*",
"*-min.*",
"*.min.*",
"**/.yarn/**",
"**/*.d.ts",
"**/assets/**",
"**/bower_components/**",
"**/build/**",
"**/cache/**",
"**/config/**",
"**/db/**",
"**/deps/**",
"**/dist/**",
"**/extern/**",
"**/external/**",
"**/generated/**",
"**/Godeps/**",
"**/gradlew/**",
"**/mvnw/**",
"**/node_modules/**",
"**/protos/**",
"**/seed/**",
"**/target/**",
"**/templates/**",
"**/testdata/**",
"**/vendor/**", "spec/", "**/specs/**/**", "**/spec/**/**", "db/*", "bin/**/*", "db/**/*", "config/**/*", "public/**/*", "vendor/**/*", "node_modules/**/*", "lib/tasks/auto_annotate_models.rake", "app/test-matchers.js", "docs/*", "**/*.md", "**/*.yml", "app/javascript/dashboard/i18n/locale", "**/*.stories.js", "stories/", "app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js", "app/javascript/shared/constants/countries.js", "app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js", "app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js", "app/javascript/dashboard/routes/dashboard/settings/automation/constants.js", "app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js", "app/javascript/dashboard/routes/dashboard/settings/reports/constants.js", "app/javascript/dashboard/store/captain/storeFactory.js", "app/javascript/dashboard/i18n/index.js", "app/javascript/widget/i18n/index.js", "app/javascript/survey/i18n/index.js", "app/javascript/shared/constants/locales.js", "app/javascript/dashboard/helper/specs/macrosFixtures.js", "app/javascript/dashboard/routes/dashboard/settings/macros/constants.js", "**/fixtures/**", "**/*/fixtures.js",
]
test_patterns = [
"**/test/**",
"**/spec/**",
"**/*.test.*",
"**/*.spec.*",
"**/*_test.*",
"**/*_spec.*",
"**/test_*.*",
"**/spec_*.*",
]
[smells]
mode = "comment"
[smells.boolean_logic]
threshold = 4
[smells.file_complexity]
threshold = 66
enabled = true
[smells.return_statements]
threshold = 4
[smells.nested_control_flow]
threshold = 4
[smells.function_parameters]
threshold = 4
[smells.function_complexity]
threshold = 5
[smells.duplication]
enabled = true
threshold = 20
[[source]]
name = "default"
default = true

View File

@ -283,7 +283,7 @@ Rails/RedundantActiveRecordAllMethod:
Enabled: false Enabled: false
Layout/TrailingEmptyLines: Layout/TrailingEmptyLines:
Enabled: false Enabled: true
Style/SafeNavigationChainLength: Style/SafeNavigationChainLength:
Enabled: false Enabled: false

View File

@ -29,6 +29,6 @@ class Api::V1::Accounts::CampaignsController < Api::V1::Accounts::BaseController
def campaign_params def campaign_params
params.require(:campaign).permit(:title, :description, :message, :enabled, :trigger_only_during_business_hours, :inbox_id, :sender_id, params.require(:campaign).permit(:title, :description, :message, :enabled, :trigger_only_during_business_hours, :inbox_id, :sender_id,
:scheduled_at, audience: [:type, :id], trigger_rules: {}) :scheduled_at, audience: [:type, :id], trigger_rules: {}, template_params: {})
end end
end end

View File

@ -0,0 +1,64 @@
class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts::BaseController
before_action :validate_feature_enabled!
# POST /api/v1/accounts/:account_id/whatsapp/authorization
# Handles the embedded signup callback data from the Facebook SDK
def create
validate_embedded_signup_params!
channel = process_embedded_signup
render_success_response(channel.inbox)
rescue StandardError => e
render_error_response(e)
end
private
def process_embedded_signup
service = Whatsapp::EmbeddedSignupService.new(
account: Current.account,
code: params[:code],
business_id: params[:business_id],
waba_id: params[:waba_id],
phone_number_id: params[:phone_number_id]
)
service.perform
end
def render_success_response(inbox)
render json: {
success: true,
id: inbox.id,
name: inbox.name,
channel_type: 'whatsapp'
}
end
def render_error_response(error)
Rails.logger.error "[WHATSAPP AUTHORIZATION] Embedded signup error: #{error.message}"
Rails.logger.error error.backtrace.join("\n")
render json: {
success: false,
error: error.message
}, status: :unprocessable_entity
end
def validate_feature_enabled!
return if Current.account.feature_whatsapp_embedded_signup?
render json: {
success: false,
error: 'WhatsApp embedded signup is not enabled for this account'
}, status: :forbidden
end
def validate_embedded_signup_params!
missing_params = []
missing_params << 'code' if params[:code].blank?
missing_params << 'business_id' if params[:business_id].blank?
missing_params << 'waba_id' if params[:waba_id].blank?
return if missing_params.empty?
raise ArgumentError, "Required parameters are missing: #{missing_params.join(', ')}"
end
end

View File

@ -67,6 +67,8 @@ class DashboardController < ActionController::Base
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''), FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''),
INSTAGRAM_APP_ID: GlobalConfigService.load('INSTAGRAM_APP_ID', ''), INSTAGRAM_APP_ID: GlobalConfigService.load('INSTAGRAM_APP_ID', ''),
FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v17.0'), FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v17.0'),
WHATSAPP_APP_ID: GlobalConfigService.load('WHATSAPP_APP_ID', ''),
WHATSAPP_CONFIGURATION_ID: GlobalConfigService.load('WHATSAPP_CONFIGURATION_ID', ''),
IS_ENTERPRISE: ChatwootApp.enterprise?, IS_ENTERPRISE: ChatwootApp.enterprise?,
AZURE_APP_ID: GlobalConfigService.load('AZURE_APP_ID', ''), AZURE_APP_ID: GlobalConfigService.load('AZURE_APP_ID', ''),
GIT_SHA: GIT_HASH GIT_SHA: GIT_HASH

View File

@ -39,8 +39,10 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController
'email' => ['MAILER_INBOUND_EMAIL_DOMAIN'], 'email' => ['MAILER_INBOUND_EMAIL_DOMAIN'],
'linear' => %w[LINEAR_CLIENT_ID LINEAR_CLIENT_SECRET], 'linear' => %w[LINEAR_CLIENT_ID LINEAR_CLIENT_SECRET],
'slack' => %w[SLACK_CLIENT_ID SLACK_CLIENT_SECRET], 'slack' => %w[SLACK_CLIENT_ID SLACK_CLIENT_SECRET],
'instagram' => %w[INSTAGRAM_APP_ID INSTAGRAM_APP_SECRET INSTAGRAM_VERIFY_TOKEN INSTAGRAM_API_VERSION ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT],
'whatsapp_embedded' => %w[WHATSAPP_APP_ID WHATSAPP_APP_SECRET WHATSAPP_CONFIGURATION_ID WHATSAPP_API_VERSION],
'notion' => %w[NOTION_CLIENT_ID NOTION_CLIENT_SECRET], 'notion' => %w[NOTION_CLIENT_ID NOTION_CLIENT_SECRET],
'instagram' => %w[INSTAGRAM_APP_ID INSTAGRAM_APP_SECRET INSTAGRAM_VERIFY_TOKEN INSTAGRAM_API_VERSION ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT] 'google' => %w[GOOGLE_OAUTH_CLIENT_ID GOOGLE_OAUTH_CLIENT_SECRET GOOGLE_OAUTH_REDIRECT_URI]
} }
@allowed_configs = mapping.fetch(@config, %w[ENABLE_ACCOUNT_SIGNUP FIREBASE_PROJECT_ID FIREBASE_CREDENTIALS]) @allowed_configs = mapping.fetch(@config, %w[ENABLE_ACCOUNT_SIGNUP FIREBASE_PROJECT_ID FIREBASE_CREDENTIALS])

View File

@ -7,8 +7,9 @@
class SuperAdmin::ApplicationController < Administrate::ApplicationController class SuperAdmin::ApplicationController < Administrate::ApplicationController
include ActionView::Helpers::TagHelper include ActionView::Helpers::TagHelper
include ActionView::Context include ActionView::Context
include SuperAdmin::NavigationHelper
helper_method :render_vue_component helper_method :render_vue_component, :settings_open?, :settings_pages
# authenticiation done via devise : SuperAdmin Model # authenticiation done via devise : SuperAdmin Model
before_action :authenticate_super_admin! before_action :authenticate_super_admin!

View File

@ -1,5 +1,7 @@
# TODO: Move this values to features.yml itself # TODO: Move this values to features.yml itself
# No need to replicate the same values in two places # No need to replicate the same values in two places
# ------- Premium Features ------- #
captain: captain:
name: 'Captain' name: 'Captain'
description: 'Enable AI-powered conversations with your customers.' description: 'Enable AI-powered conversations with your customers.'
@ -32,6 +34,15 @@ disable_branding:
enabled: <%= (ChatwootHub.pricing_plan != 'community') %> enabled: <%= (ChatwootHub.pricing_plan != 'community') %>
icon: 'icon-sailbot-fill' icon: 'icon-sailbot-fill'
enterprise: true enterprise: true
# ------- Product Features ------- #
help_center:
name: 'Help Center'
description: 'Allow agents to create help center articles and publish them in a portal.'
enabled: true
icon: 'icon-book-2-line'
# ------- Communication Channels ------- #
live_chat: live_chat:
name: 'Live Chat' name: 'Live Chat'
description: 'Improve your customer experience using a live chat on your website.' description: 'Improve your customer experience using a live chat on your website.'
@ -42,6 +53,12 @@ email:
description: 'Manage your email customer interactions from Chatwoot.' description: 'Manage your email customer interactions from Chatwoot.'
enabled: true enabled: true
icon: 'icon-mail-send-fill' icon: 'icon-mail-send-fill'
config_key: 'email'
sms:
name: 'SMS'
description: 'Manage your SMS customer interactions from Chatwoot.'
enabled: true
icon: 'icon-message-line'
messenger: messenger:
name: 'Messenger' name: 'Messenger'
description: 'Stay connected with your customers on Facebook & Instagram.' description: 'Stay connected with your customers on Facebook & Instagram.'
@ -69,22 +86,22 @@ line:
description: 'Manage your Line customer interactions from Chatwoot.' description: 'Manage your Line customer interactions from Chatwoot.'
enabled: true enabled: true
icon: 'icon-line-line' icon: 'icon-line-line'
sms:
name: 'SMS' # ------- OAuth & Authentication ------- #
description: 'Manage your SMS customer interactions from Chatwoot.' google:
name: 'Google'
description: 'Configuration for setting up Google OAuth Integration'
enabled: true enabled: true
icon: 'icon-message-line' icon: 'icon-google'
help_center: config_key: 'google'
name: 'Help Center'
description: 'Allow agents to create help center articles and publish them in a portal.'
enabled: true
icon: 'icon-book-2-line'
microsoft: microsoft:
name: 'Microsoft' name: 'Microsoft'
description: 'Configuration for setting up Microsoft Email' description: 'Configuration for setting up Microsoft Email'
enabled: true enabled: true
icon: 'icon-microsoft' icon: 'icon-microsoft'
config_key: 'microsoft' config_key: 'microsoft'
# ------- Third-party Integrations ------- #
linear: linear:
name: 'Linear' name: 'Linear'
description: 'Configuration for setting up Linear Integration' description: 'Configuration for setting up Linear Integration'
@ -103,6 +120,12 @@ slack:
enabled: true enabled: true
icon: 'icon-slack' icon: 'icon-slack'
config_key: 'slack' config_key: 'slack'
whatsapp_embedded:
name: 'WhatsApp Embedded'
description: 'Configuration for setting up WhatsApp Embedded Integration'
enabled: true
icon: 'icon-whatsapp-line'
config_key: 'whatsapp_embedded'
shopify: shopify:
name: 'Shopify' name: 'Shopify'
description: 'Configuration for setting up Shopify Integration' description: 'Configuration for setting up Shopify Integration'

View File

@ -1,6 +1,6 @@
module SuperAdmin::FeaturesHelper module SuperAdmin::FeaturesHelper
def self.available_features def self.available_features
YAML.load(ERB.new(Rails.root.join('enterprise/app/helpers/super_admin/features.yml').read).result).with_indifferent_access YAML.load(ERB.new(Rails.root.join('app/helpers/super_admin/features.yml').read).result).with_indifferent_access
end end
def self.plan_details def self.plan_details

View File

@ -0,0 +1,16 @@
module SuperAdmin::NavigationHelper
def settings_open?
params[:controller].in? %w[super_admin/settings super_admin/app_configs]
end
def settings_pages
features = SuperAdmin::FeaturesHelper.available_features.select do |_feature, attrs|
attrs['config_key'].present? && attrs['enabled']
end
# Add general at the beginning
general_feature = [['general', { 'config_key' => 'general', 'name' => 'General' }]]
general_feature + features.to_a
end
end

View File

@ -0,0 +1,14 @@
/* global axios */
import ApiClient from '../ApiClient';
class WhatsappChannel extends ApiClient {
constructor() {
super('whatsapp', { accountScoped: true });
}
createEmbeddedSignup(params) {
return axios.post(`${this.baseUrl()}/whatsapp/authorization`, params);
}
}
export default new WhatsappChannel();

View File

@ -0,0 +1,37 @@
<script setup>
import { ONE_OFF_CAMPAIGN_EMPTY_STATE_CONTENT } from './CampaignEmptyStateContent';
import EmptyStateLayout from 'dashboard/components-next/EmptyStateLayout.vue';
import CampaignCard from 'dashboard/components-next/Campaigns/CampaignCard/CampaignCard.vue';
defineProps({
title: {
type: String,
default: '',
},
subtitle: {
type: String,
default: '',
},
});
</script>
<template>
<EmptyStateLayout :title="title" :subtitle="subtitle">
<template #empty-state-item>
<div class="flex flex-col gap-4 p-px">
<CampaignCard
v-for="campaign in ONE_OFF_CAMPAIGN_EMPTY_STATE_CONTENT"
:key="campaign.id"
:title="campaign.title"
:message="campaign.message"
:is-enabled="campaign.enabled"
:status="campaign.campaign_status"
:sender="campaign.sender"
:inbox="campaign.inbox"
:scheduled-at="campaign.scheduled_at"
/>
</div>
</template>
</EmptyStateLayout>
</template>

View File

@ -0,0 +1,48 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useStore } from 'dashboard/composables/store';
import { useAlert, useTrack } from 'dashboard/composables';
import { CAMPAIGN_TYPES } from 'shared/constants/campaign.js';
import { CAMPAIGNS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events.js';
import WhatsAppCampaignForm from 'dashboard/components-next/Campaigns/Pages/CampaignPage/WhatsAppCampaign/WhatsAppCampaignForm.vue';
const emit = defineEmits(['close']);
const store = useStore();
const { t } = useI18n();
const addCampaign = async campaignDetails => {
try {
await store.dispatch('campaigns/create', campaignDetails);
useTrack(CAMPAIGNS_EVENTS.CREATE_CAMPAIGN, {
type: CAMPAIGN_TYPES.ONE_OFF,
});
useAlert(t('CAMPAIGN.WHATSAPP.CREATE.FORM.API.SUCCESS_MESSAGE'));
} catch (error) {
const errorMessage =
error?.response?.message ||
t('CAMPAIGN.WHATSAPP.CREATE.FORM.API.ERROR_MESSAGE');
useAlert(errorMessage);
}
};
const handleSubmit = campaignDetails => {
addCampaign(campaignDetails);
};
const handleClose = () => emit('close');
</script>
<template>
<div
class="w-[25rem] z-50 min-w-0 absolute top-10 ltr:right-0 rtl:left-0 bg-n-alpha-3 backdrop-blur-[100px] p-6 rounded-xl border border-n-weak shadow-md flex flex-col gap-6"
>
<h3 class="text-base font-medium text-n-slate-12">
{{ t(`CAMPAIGN.WHATSAPP.CREATE.TITLE`) }}
</h3>
<WhatsAppCampaignForm @submit="handleSubmit" @cancel="handleClose" />
</div>
</template>

View File

@ -0,0 +1,357 @@
<script setup>
import { reactive, computed, watch, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useVuelidate } from '@vuelidate/core';
import { required, minLength } from '@vuelidate/validators';
import { useMapGetter } from 'dashboard/composables/store';
import Input from 'dashboard/components-next/input/Input.vue';
import Button from 'dashboard/components-next/button/Button.vue';
import ComboBox from 'dashboard/components-next/combobox/ComboBox.vue';
import TagMultiSelectComboBox from 'dashboard/components-next/combobox/TagMultiSelectComboBox.vue';
const emit = defineEmits(['submit', 'cancel']);
const { t } = useI18n();
const formState = {
uiFlags: useMapGetter('campaigns/getUIFlags'),
labels: useMapGetter('labels/getLabels'),
inboxes: useMapGetter('inboxes/getWhatsAppInboxes'),
getWhatsAppTemplates: useMapGetter('inboxes/getWhatsAppTemplates'),
};
const initialState = {
title: '',
inboxId: null,
templateId: null,
scheduledAt: null,
selectedAudience: [],
};
const state = reactive({ ...initialState });
const processedParams = ref({});
const rules = {
title: { required, minLength: minLength(1) },
inboxId: { required },
templateId: { required },
scheduledAt: { required },
selectedAudience: { required },
};
const v$ = useVuelidate(rules, state);
const isCreating = computed(() => formState.uiFlags.value.isCreating);
const currentDateTime = computed(() => {
// Added to disable the scheduled at field from being set to the current time
const now = new Date();
const localTime = new Date(now.getTime() - now.getTimezoneOffset() * 60000);
return localTime.toISOString().slice(0, 16);
});
const mapToOptions = (items, valueKey, labelKey) =>
items?.map(item => ({
value: item[valueKey],
label: item[labelKey],
})) ?? [];
const audienceList = computed(() =>
mapToOptions(formState.labels.value, 'id', 'title')
);
const inboxOptions = computed(() =>
mapToOptions(formState.inboxes.value, 'id', 'name')
);
const templateOptions = computed(() => {
if (!state.inboxId) return [];
const templates = formState.getWhatsAppTemplates.value(state.inboxId);
return templates.map(template => {
// Create a more user-friendly label from template name
const friendlyName = template.name
.replace(/_/g, ' ')
.replace(/\b\w/g, l => l.toUpperCase());
return {
value: template.id,
label: `${friendlyName} (${template.language || 'en'})`,
template: template,
};
});
});
const selectedTemplate = computed(() => {
if (!state.templateId) return null;
return templateOptions.value.find(option => option.value === state.templateId)
?.template;
});
const templateString = computed(() => {
if (!selectedTemplate.value) return '';
try {
return (
selectedTemplate.value.components?.find(
component => component.type === 'BODY'
)?.text || ''
);
} catch (error) {
return '';
}
});
const processedString = computed(() => {
if (!templateString.value) return '';
return templateString.value.replace(/{{([^}]+)}}/g, (match, variable) => {
return processedParams.value[variable] || `{{${variable}}}`;
});
});
const getErrorMessage = (field, errorKey) => {
const baseKey = 'CAMPAIGN.WHATSAPP.CREATE.FORM';
return v$.value[field].$error ? t(`${baseKey}.${errorKey}.ERROR`) : '';
};
const formErrors = computed(() => ({
title: getErrorMessage('title', 'TITLE'),
inbox: getErrorMessage('inboxId', 'INBOX'),
template: getErrorMessage('templateId', 'TEMPLATE'),
scheduledAt: getErrorMessage('scheduledAt', 'SCHEDULED_AT'),
audience: getErrorMessage('selectedAudience', 'AUDIENCE'),
}));
const hasRequiredTemplateParams = computed(() => {
const params = Object.values(processedParams.value);
return params.length === 0 || params.every(param => param.trim() !== '');
});
const isSubmitDisabled = computed(
() => v$.value.$invalid || !hasRequiredTemplateParams.value
);
const formatToUTCString = localDateTime =>
localDateTime ? new Date(localDateTime).toISOString() : null;
const resetState = () => {
Object.assign(state, initialState);
processedParams.value = {};
v$.value.$reset();
};
const handleCancel = () => emit('cancel');
const generateVariables = () => {
const matchedVariables = templateString.value.match(/{{([^}]+)}}/g);
if (!matchedVariables) {
processedParams.value = {};
return;
}
const finalVars = matchedVariables.map(match => match.replace(/{{|}}/g, ''));
processedParams.value = finalVars.reduce((acc, variable) => {
acc[variable] = processedParams.value[variable] || '';
return acc;
}, {});
};
const prepareCampaignDetails = () => {
// Find the selected template to get its content
const currentTemplate = selectedTemplate.value;
// Extract template content - this should be the template message body
const templateContent = templateString.value;
// Prepare template_params object with the same structure as used in contacts
const templateParams = {
name: currentTemplate?.name || '',
namespace: currentTemplate?.namespace || '',
category: currentTemplate?.category || 'UTILITY',
language: currentTemplate?.language || 'en_US',
processed_params: processedParams.value,
};
return {
title: state.title,
message: templateContent,
template_params: templateParams,
inbox_id: state.inboxId,
scheduled_at: formatToUTCString(state.scheduledAt),
audience: state.selectedAudience?.map(id => ({
id,
type: 'Label',
})),
};
};
const handleSubmit = async () => {
const isFormValid = await v$.value.$validate();
if (!isFormValid) return;
emit('submit', prepareCampaignDetails());
resetState();
handleCancel();
};
// Reset template selection when inbox changes
watch(
() => state.inboxId,
() => {
state.templateId = null;
processedParams.value = {};
}
);
// Generate variables when template changes
watch(
() => state.templateId,
() => {
generateVariables();
}
);
</script>
<template>
<form class="flex flex-col gap-4" @submit.prevent="handleSubmit">
<Input
v-model="state.title"
:label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.TITLE.LABEL')"
:placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.TITLE.PLACEHOLDER')"
:message="formErrors.title"
:message-type="formErrors.title ? 'error' : 'info'"
/>
<div class="flex flex-col gap-1">
<label for="inbox" class="mb-0.5 text-sm font-medium text-n-slate-12">
{{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.INBOX.LABEL') }}
</label>
<ComboBox
id="inbox"
v-model="state.inboxId"
:options="inboxOptions"
:has-error="!!formErrors.inbox"
:placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.INBOX.PLACEHOLDER')"
:message="formErrors.inbox"
class="[&>div>button]:bg-n-alpha-black2 [&>div>button:not(.focused)]:dark:outline-n-weak [&>div>button:not(.focused)]:hover:!outline-n-slate-6"
/>
</div>
<div class="flex flex-col gap-1">
<label for="template" class="mb-0.5 text-sm font-medium text-n-slate-12">
{{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.LABEL') }}
</label>
<ComboBox
id="template"
v-model="state.templateId"
:options="templateOptions"
:has-error="!!formErrors.template"
:placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.PLACEHOLDER')"
:message="formErrors.template"
class="[&>div>button]:bg-n-alpha-black2 [&>div>button:not(.focused)]:dark:outline-n-weak [&>div>button:not(.focused)]:hover:!outline-n-slate-6"
/>
<p class="mt-1 text-xs text-n-slate-11">
{{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.INFO') }}
</p>
</div>
<!-- Template Preview -->
<div
v-if="selectedTemplate"
class="flex flex-col gap-4 p-4 rounded-lg bg-n-alpha-black2"
>
<div class="flex justify-between items-center">
<h3 class="text-sm font-medium text-n-slate-12">
{{ selectedTemplate.name }}
</h3>
<span class="text-xs text-n-slate-11">
{{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.LANGUAGE') }}:
{{ selectedTemplate.language || 'en' }}
</span>
</div>
<div class="flex flex-col gap-2">
<div class="rounded-md bg-n-alpha-black3">
<div class="text-sm whitespace-pre-wrap text-n-slate-12">
{{ processedString }}
</div>
</div>
</div>
<div class="text-xs text-n-slate-11">
{{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.CATEGORY') }}:
{{ selectedTemplate.category || 'UTILITY' }}
</div>
</div>
<!-- Template Variables -->
<div
v-if="Object.keys(processedParams).length > 0"
class="flex flex-col gap-3"
>
<label class="text-sm font-medium text-n-slate-12">
{{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.VARIABLES_LABEL') }}
</label>
<div class="flex flex-col gap-2">
<div
v-for="(value, key) in processedParams"
:key="key"
class="flex gap-2 items-center"
>
<Input
v-model="processedParams[key]"
type="text"
class="flex-1"
:placeholder="
t('CAMPAIGN.WHATSAPP.CREATE.FORM.TEMPLATE.VARIABLE_PLACEHOLDER', {
variable: key,
})
"
/>
</div>
</div>
</div>
<div class="flex flex-col gap-1">
<label for="audience" class="mb-0.5 text-sm font-medium text-n-slate-12">
{{ t('CAMPAIGN.WHATSAPP.CREATE.FORM.AUDIENCE.LABEL') }}
</label>
<TagMultiSelectComboBox
v-model="state.selectedAudience"
:options="audienceList"
:label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.AUDIENCE.LABEL')"
:placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.AUDIENCE.PLACEHOLDER')"
:has-error="!!formErrors.audience"
:message="formErrors.audience"
class="[&>div>button]:bg-n-alpha-black2"
/>
</div>
<Input
v-model="state.scheduledAt"
:label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.SCHEDULED_AT.LABEL')"
type="datetime-local"
:min="currentDateTime"
:placeholder="t('CAMPAIGN.WHATSAPP.CREATE.FORM.SCHEDULED_AT.PLACEHOLDER')"
:message="formErrors.scheduledAt"
:message-type="formErrors.scheduledAt ? 'error' : 'info'"
/>
<div class="flex gap-3 justify-between items-center w-full">
<Button
variant="faded"
color="slate"
type="button"
:label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.BUTTONS.CANCEL')"
class="w-full bg-n-alpha-2 text-n-blue-text hover:bg-n-alpha-3"
@click="handleCancel"
/>
<Button
:label="t('CAMPAIGN.WHATSAPP.CREATE.FORM.BUTTONS.CREATE')"
class="w-full"
type="submit"
:is-loading="isCreating"
:disabled="isCreating || isSubmitDisabled"
/>
</div>
</form>
</template>

View File

@ -218,10 +218,13 @@ const resetForm = () => {
Object.assign(state, defaultState); Object.assign(state, defaultState);
}; };
watch(() => props.contactData, prepareStateBasedOnProps, { watch(
immediate: true, () => props.contactData?.id,
deep: true, id => {
}); if (id) prepareStateBasedOnProps();
},
{ immediate: true }
);
// Expose state to parent component for avatar upload // Expose state to parent component for avatar upload
defineExpose({ defineExpose({

View File

@ -15,8 +15,8 @@ const emit = defineEmits(['click']);
const { t } = useI18n(); const { t } = useI18n();
const onClick = event => { const onClick = (item, index) => {
emit('click', event); emit('click', item, index);
}; };
</script> </script>
@ -24,22 +24,25 @@ const onClick = event => {
<nav :aria-label="t('BREADCRUMB.ARIA_LABEL')" class="flex items-center h-8"> <nav :aria-label="t('BREADCRUMB.ARIA_LABEL')" class="flex items-center h-8">
<ol class="flex items-center mb-0"> <ol class="flex items-center mb-0">
<li v-for="(item, index) in items" :key="index" class="flex items-center"> <li v-for="(item, index) in items" :key="index" class="flex items-center">
<Icon
v-if="index > 0"
icon="i-lucide-chevron-right"
class="flex-shrink-0 mx-2 size-4 text-n-slate-11 dark:text-n-slate-11"
/>
<!-- Render as button for all except the last item -->
<button <button
v-if="index === 0" v-if="index !== items.length - 1"
class="inline-flex items-center justify-center min-w-0 gap-2 p-0 text-sm font-medium transition-all duration-200 ease-in-out border-0 rounded-lg text-n-slate-11 hover:text-n-slate-12 outline-transparent max-w-56" class="inline-flex items-center justify-center min-w-0 gap-2 p-0 text-sm font-medium transition-all duration-200 ease-in-out border-0 rounded-lg text-n-slate-11 hover:text-n-slate-12 outline-transparent max-w-56"
@click="onClick" @click="onClick(item, index)"
> >
<span class="min-w-0 truncate">{{ item.label }}</span> <span class="min-w-0 truncate">{{ item.label }}</span>
</button> </button>
<template v-else>
<Icon <!-- The last breadcrumb item is plain text -->
icon="i-lucide-chevron-right" <span v-else class="text-sm truncate text-n-slate-12 max-w-56">
class="flex-shrink-0 mx-2 size-4 text-n-slate-11 dark:text-n-slate-11" {{ item.emoji ? item.emoji : '' }} {{ item.label }}
/> </span>
<span class="text-sm truncate text-n-slate-12 max-w-56">
{{ item.emoji ? item.emoji : '' }} {{ item.label }}
</span>
</template>
</li> </li>
</ol> </ol>
</nav> </nav>

View File

@ -0,0 +1,91 @@
<script setup>
import { useRouter } from 'vue-router';
import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue';
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
import Breadcrumb from 'dashboard/components-next/breadcrumb/Breadcrumb.vue';
defineProps({
isFetching: {
type: Boolean,
default: false,
},
isEmpty: {
type: Boolean,
default: false,
},
currentPage: {
type: Number,
default: 1,
},
totalCount: {
type: Number,
default: 100,
},
itemsPerPage: {
type: Number,
default: 25,
},
showPaginationFooter: {
type: Boolean,
default: false,
},
breadcrumbItems: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(['update:currentPage']);
const router = useRouter();
const handlePageChange = event => {
emit('update:currentPage', event);
};
const handleBreadcrumbClick = item => {
router.push({
name: item.routeName,
});
};
</script>
<template>
<section
class="my-4 px-10 flex flex-col w-full h-screen overflow-y-auto bg-n-background"
>
<div class="max-w-[60rem] mx-auto flex flex-col w-full h-full">
<header class="mb-7 sticky top-0 z-10 bg-n-background">
<Breadcrumb :items="breadcrumbItems" @click="handleBreadcrumbClick" />
</header>
<main class="flex gap-16 w-full flex-1 pb-16">
<section
v-if="$slots.body || $slots.emptyState || isFetching"
class="flex flex-col w-full"
>
<div
v-if="isFetching"
class="flex items-center justify-center py-10 text-n-slate-11"
>
<Spinner />
</div>
<div v-else-if="isEmpty">
<slot name="emptyState" />
</div>
<slot v-else name="body" />
</section>
<section v-if="$slots.controls" class="flex w-full">
<slot name="controls" />
</section>
</main>
<footer v-if="showPaginationFooter" class="sticky bottom-0 z-10 pb-4">
<PaginationFooter
:current-page="currentPage"
:total-items="totalCount"
:items-per-page="itemsPerPage"
@update:current-page="handlePageChange"
/>
</footer>
</div>
</section>
</template>

View File

@ -55,6 +55,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
showMenu: {
type: Boolean,
default: true,
},
}); });
const emit = defineEmits(['action', 'navigate', 'select', 'hover']); const emit = defineEmits(['action', 'navigate', 'select', 'hover']);
@ -130,7 +134,7 @@ const handleDocumentableClick = () => {
<span class="text-base text-n-slate-12 line-clamp-1"> <span class="text-base text-n-slate-12 line-clamp-1">
{{ question }} {{ question }}
</span> </span>
<div v-if="!compact" class="flex items-center gap-2"> <div v-if="!compact && showMenu" class="flex items-center gap-2">
<Policy <Policy
v-on-clickaway="() => toggleDropdown(false)" v-on-clickaway="() => toggleDropdown(false)"
:permissions="['administrator']" :permissions="['administrator']"

View File

@ -0,0 +1,151 @@
<script setup>
import { reactive, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useVuelidate } from '@vuelidate/core';
import { required, minLength } from '@vuelidate/validators';
import Input from 'dashboard/components-next/input/Input.vue';
import Button from 'dashboard/components-next/button/Button.vue';
import Editor from 'dashboard/components-next/Editor/Editor.vue';
const props = defineProps({
assistant: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(['submit']);
const { t } = useI18n();
const initialState = {
name: '',
description: '',
productName: '',
features: {
conversationFaqs: false,
memories: false,
},
};
const state = reactive({ ...initialState });
const validationRules = {
name: { required, minLength: minLength(1) },
description: { required, minLength: minLength(1) },
productName: { required, minLength: minLength(1) },
};
const v$ = useVuelidate(validationRules, state);
const getErrorMessage = field => {
return v$.value[field].$error ? v$.value[field].$errors[0].$message : '';
};
const formErrors = computed(() => ({
name: getErrorMessage('name'),
description: getErrorMessage('description'),
productName: getErrorMessage('productName'),
}));
const updateStateFromAssistant = assistant => {
const { config = {} } = assistant;
state.name = assistant.name;
state.description = assistant.description;
state.productName = config.product_name;
state.features = {
conversationFaqs: config.feature_faq || false,
memories: config.feature_memory || false,
};
};
const handleBasicInfoUpdate = async () => {
const result = await Promise.all([
v$.value.name.$validate(),
v$.value.description.$validate(),
v$.value.productName.$validate(),
]).then(results => results.every(Boolean));
if (!result) return;
const payload = {
name: state.name,
description: state.description,
config: {
...props.assistant.config,
product_name: state.productName,
feature_faq: state.features.conversationFaqs,
feature_memory: state.features.memories,
},
};
emit('submit', payload);
};
watch(
() => props.assistant,
newAssistant => {
if (newAssistant) updateStateFromAssistant(newAssistant);
},
{ immediate: true }
);
</script>
<template>
<div class="flex flex-col gap-6">
<Input
v-model="state.name"
:label="t('CAPTAIN.ASSISTANTS.FORM.NAME.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.NAME.PLACEHOLDER')"
:message="formErrors.name"
:message-type="formErrors.name ? 'error' : 'info'"
/>
<Input
v-model="state.productName"
:label="t('CAPTAIN.ASSISTANTS.FORM.PRODUCT_NAME.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.PRODUCT_NAME.PLACEHOLDER')"
:message="formErrors.productName"
:message-type="formErrors.productName ? 'error' : 'info'"
/>
<Editor
v-model="state.description"
:label="t('CAPTAIN.ASSISTANTS.FORM.DESCRIPTION.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.DESCRIPTION.PLACEHOLDER')"
:message="formErrors.description"
:message-type="formErrors.description ? 'error' : 'info'"
/>
<div class="flex flex-col gap-2">
<label class="text-sm font-medium text-n-slate-12">
{{ t('CAPTAIN.ASSISTANTS.FORM.FEATURES.TITLE') }}
</label>
<div class="flex flex-col gap-2">
<label class="flex items-center gap-2">
<input
v-model="state.features.conversationFaqs"
type="checkbox"
class="form-checkbox"
/>
{{ t('CAPTAIN.ASSISTANTS.FORM.FEATURES.ALLOW_CONVERSATION_FAQS') }}
</label>
<label class="flex items-center gap-2">
<input
v-model="state.features.memories"
type="checkbox"
class="form-checkbox"
/>
{{ t('CAPTAIN.ASSISTANTS.FORM.FEATURES.ALLOW_MEMORIES') }}
</label>
</div>
</div>
<div>
<Button
:label="t('CAPTAIN.ASSISTANTS.FORM.UPDATE')"
@click="handleBasicInfoUpdate"
/>
</div>
</div>
</template>

View File

@ -0,0 +1,43 @@
<script setup>
import { useRouter } from 'vue-router';
import Button from 'dashboard/components-next/button/Button.vue';
defineProps({
controlItem: {
type: Object,
default: () => ({}),
},
});
const router = useRouter();
const onClick = name => {
router.push({ name });
};
</script>
<template>
<div
:key="controlItem.name"
class="pt-3 ltr:pl-4 rtl:pr-4 ltr:pr-2 rtl:pl-2 pb-5 gap-2 flex flex-col w-full shadow outline-1 outline outline-n-container rounded-2xl bg-n-solid-2 cursor-pointer"
@click="onClick(controlItem.routeName)"
>
<div class="flex items-center justify-between w-full gap-1 h-8">
<span class="text-sm font-medium text-n-slate-12 line-clamp-1">
{{ controlItem.name }}
</span>
<div class="flex items-center gap-2">
<Button
icon="i-lucide-chevron-right"
slate
ghost
xs
@click="onClick(controlItem.routeName)"
/>
</div>
</div>
<span class="text-n-slate-11 text-sm leading-[21px] line-clamp-5">
{{ controlItem.description }}
</span>
</div>
</template>

View File

@ -0,0 +1,125 @@
<script setup>
import { reactive, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useVuelidate } from '@vuelidate/core';
import { minLength } from '@vuelidate/validators';
import Button from 'dashboard/components-next/button/Button.vue';
import Editor from 'dashboard/components-next/Editor/Editor.vue';
const props = defineProps({
assistant: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(['submit']);
const { t } = useI18n();
const initialState = {
handoffMessage: '',
resolutionMessage: '',
temperature: 1,
};
const state = reactive({ ...initialState });
const validationRules = {
handoffMessage: { minLength: minLength(1) },
resolutionMessage: { minLength: minLength(1) },
};
const v$ = useVuelidate(validationRules, state);
const getErrorMessage = field => {
return v$.value[field].$error ? v$.value[field].$errors[0].$message : '';
};
const formErrors = computed(() => ({
handoffMessage: getErrorMessage('handoffMessage'),
resolutionMessage: getErrorMessage('resolutionMessage'),
}));
const updateStateFromAssistant = assistant => {
const { config = {} } = assistant;
state.handoffMessage = config.handoff_message;
state.resolutionMessage = config.resolution_message;
state.temperature = config.temperature || 1;
};
const handleSystemMessagesUpdate = async () => {
const result = await Promise.all([
v$.value.handoffMessage.$validate(),
v$.value.resolutionMessage.$validate(),
]).then(results => results.every(Boolean));
if (!result) return;
const payload = {
config: {
...props.assistant.config,
handoff_message: state.handoffMessage,
resolution_message: state.resolutionMessage,
temperature: state.temperature || 1,
},
};
emit('submit', payload);
};
watch(
() => props.assistant,
newAssistant => {
if (newAssistant) updateStateFromAssistant(newAssistant);
},
{ immediate: true }
);
</script>
<template>
<div class="flex flex-col gap-6">
<Editor
v-model="state.handoffMessage"
:label="t('CAPTAIN.ASSISTANTS.FORM.HANDOFF_MESSAGE.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.HANDOFF_MESSAGE.PLACEHOLDER')"
:message="formErrors.handoffMessage"
:message-type="formErrors.handoffMessage ? 'error' : 'info'"
/>
<Editor
v-model="state.resolutionMessage"
:label="t('CAPTAIN.ASSISTANTS.FORM.RESOLUTION_MESSAGE.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.RESOLUTION_MESSAGE.PLACEHOLDER')"
:message="formErrors.resolutionMessage"
:message-type="formErrors.resolutionMessage ? 'error' : 'info'"
/>
<div class="flex flex-col gap-2">
<label class="text-sm font-medium text-n-slate-12">
{{ t('CAPTAIN.ASSISTANTS.FORM.TEMPERATURE.LABEL') }}
</label>
<div class="flex items-center gap-4">
<input
v-model="state.temperature"
type="range"
min="0"
max="1"
step="0.1"
class="w-full"
/>
<span class="text-sm text-n-slate-12">{{ state.temperature }}</span>
</div>
<p class="text-sm text-n-slate-11 italic">
{{ t('CAPTAIN.ASSISTANTS.FORM.TEMPERATURE.DESCRIPTION') }}
</p>
</div>
<div>
<Button
:label="t('CAPTAIN.ASSISTANTS.FORM.UPDATE')"
@click="handleSystemMessagesUpdate"
/>
</div>
</div>
</template>

View File

@ -0,0 +1,28 @@
<script setup>
import SettingsHeader from './SettingsHeader.vue';
</script>
<template>
<Story
title="Captain/PageComponents/SettingsHeader"
:layout="{ type: 'grid', width: '800px' }"
>
<Variant title="Default">
<div class="px-6 py-4 bg-n-background">
<SettingsHeader
heading="General Settings"
description="Configure general preferences for your workspace."
/>
</div>
</Variant>
<Variant title="Long Description">
<div class="px-6 py-4 bg-n-background">
<SettingsHeader
heading="Integrations"
description="Manage and configure third-party integrations such as Slack, Zapier, and Webhooks to enhance your workflow."
/>
</div>
</Variant>
</Story>
</template>

View File

@ -0,0 +1,19 @@
<script setup>
defineProps({
heading: {
type: String,
required: true,
},
description: {
type: String,
default: '',
},
});
</script>
<template>
<header class="flex flex-col items-start gap-2">
<h2 class="text-n-slate-12 text-base font-medium">{{ heading }}</h2>
<p class="text-n-slate-11 text-sm">{{ description }}</p>
</header>
</template>

View File

@ -331,6 +331,11 @@ const menuItems = computed(() => {
label: t('SIDEBAR.SMS'), label: t('SIDEBAR.SMS'),
to: accountScopedRoute('campaigns_sms_index'), to: accountScopedRoute('campaigns_sms_index'),
}, },
{
name: 'WhatsApp',
label: t('SIDEBAR.WHATSAPP'),
to: accountScopedRoute('campaigns_whatsapp_index'),
},
], ],
}, },
{ {

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, watch, computed } from 'vue'; import { ref, watch, computed, nextTick } from 'vue';
import { useKeyboardNavigableList } from 'dashboard/composables/useKeyboardNavigableList'; import { useKeyboardNavigableList } from 'dashboard/composables/useKeyboardNavigableList';
const props = defineProps({ const props = defineProps({
@ -19,19 +19,16 @@ const mentionsListContainerRef = ref(null);
const selectedIndex = ref(0); const selectedIndex = ref(0);
const adjustScroll = () => { const adjustScroll = () => {
const container = mentionsListContainerRef.value; nextTick(() => {
const item = container.querySelector(`#mention-item-${selectedIndex.value}`); const container = mentionsListContainerRef.value;
if (item) { if (!container) return;
const itemTop = item.offsetTop; const selectedElement = container.querySelector(
const itemBottom = itemTop + item.offsetHeight; `#mention-item-${selectedIndex.value}`
const containerTop = container.scrollTop; );
const containerBottom = containerTop + container.offsetHeight; if (selectedElement) {
if (itemTop < containerTop) { selectedElement.scrollIntoView({ block: 'nearest', behavior: 'auto' });
container.scrollTop = itemTop;
} else if (itemBottom + 34 > containerBottom) {
container.scrollTop = itemBottom - container.offsetHeight + 34;
} }
} });
}; };
const onSelect = () => { const onSelect = () => {

View File

@ -4,6 +4,7 @@ export const FEATURE_FLAGS = {
AUTO_RESOLVE_CONVERSATIONS: 'auto_resolve_conversations', AUTO_RESOLVE_CONVERSATIONS: 'auto_resolve_conversations',
AUTOMATIONS: 'automations', AUTOMATIONS: 'automations',
CAMPAIGNS: 'campaigns', CAMPAIGNS: 'campaigns',
WHATSAPP_CAMPAIGNS: 'whatsapp_campaign',
CANNED_RESPONSES: 'canned_responses', CANNED_RESPONSES: 'canned_responses',
CRM: 'crm', CRM: 'crm',
CUSTOM_ATTRIBUTES: 'custom_attributes', CUSTOM_ATTRIBUTES: 'custom_attributes',
@ -36,6 +37,8 @@ export const FEATURE_FLAGS = {
REPORT_V4: 'report_v4', REPORT_V4: 'report_v4',
CHANNEL_INSTAGRAM: 'channel_instagram', CHANNEL_INSTAGRAM: 'channel_instagram',
CONTACT_CHATWOOT_SUPPORT_TEAM: 'contact_chatwoot_support_team', CONTACT_CHATWOOT_SUPPORT_TEAM: 'contact_chatwoot_support_team',
WHATSAPP_EMBEDDED_SIGNUP: 'whatsapp_embedded_signup',
CAPTAIN_V2: 'captain_integration_v2',
}; };
export const PREMIUM_FEATURES = [ export const PREMIUM_FEATURES = [
@ -44,4 +47,5 @@ export const PREMIUM_FEATURES = [
FEATURE_FLAGS.CUSTOM_ROLES, FEATURE_FLAGS.CUSTOM_ROLES,
FEATURE_FLAGS.AUDIT_LOGS, FEATURE_FLAGS.AUDIT_LOGS,
FEATURE_FLAGS.HELP_CENTER, FEATURE_FLAGS.HELP_CENTER,
FEATURE_FLAGS.CAPTAIN_V2,
]; ];

View File

@ -5,6 +5,18 @@
"LOADING": "Fetching custom attributes", "LOADING": "Fetching custom attributes",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Conversation",
"CONTACT": "Contact"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Text",
"NUMBER": "Number",
"LINK": "Link",
"DATE": "Date",
"LIST": "List",
"CHECKBOX": "Checkbox"
},
"ADD": { "ADD": {
"TITLE": "Add Custom Attribute", "TITLE": "Add Custom Attribute",
"SUBMIT": "Create", "SUBMIT": "Create",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Open conversation" "OPEN_CONVERSATION": "Open conversation"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "None",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Press enter to select", "ENTER_TO_SELECT": "Press enter to select",
"ENTER_TO_REMOVE": "Press enter to remove", "ENTER_TO_REMOVE": "Press enter to remove",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Select one", "SELECT_ONE": "Select one",
"SELECT": "Select" "SELECT": "Select"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Change Priority", "CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "None",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "جلب الصفات المخصصة", "LOADING": "جلب الصفات المخصصة",
"DESCRIPTION": "سمة مخصصة تتتبع تفاصيل إضافية حول جهات الاتصال أو المحادثات الخاصة بك - مثل خطة الاشتراك أو تاريخ الشراء الأول. يمكنك إضافة أنواع مختلفة من السمات المخصصة، مثل النص أو القوائم أو الأرقام، لالتقاط المعلومات المحددة التي تحتاجها.", "DESCRIPTION": "سمة مخصصة تتتبع تفاصيل إضافية حول جهات الاتصال أو المحادثات الخاصة بك - مثل خطة الاشتراك أو تاريخ الشراء الأول. يمكنك إضافة أنواع مختلفة من السمات المخصصة، مثل النص أو القوائم أو الأرقام، لالتقاط المعلومات المحددة التي تحتاجها.",
"LEARN_MORE": "تعرف على المزيد حول السمات المخصصة", "LEARN_MORE": "تعرف على المزيد حول السمات المخصصة",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "المحادثات",
"CONTACT": "جهات الاتصال"
},
"ATTRIBUTE_TYPES": {
"TEXT": "النص",
"NUMBER": "العدد",
"LINK": "الرابط",
"DATE": "Date",
"LIST": "القائمة",
"CHECKBOX": "خانة الاختيار"
},
"ADD": { "ADD": {
"TITLE": "إضافة صفة خاصة", "TITLE": "إضافة صفة خاصة",
"SUBMIT": "إنشاء", "SUBMIT": "إنشاء",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "فتح المحادثة" "OPEN_CONVERSATION": "فتح المحادثة"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "لا شيء",
"LOW": "منخفضة",
"MEDIUM": "متوسطة",
"HIGH": "عالية",
"URGENT": "عاجل"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "اضغط على زر الإدخال للاختيار", "ENTER_TO_SELECT": "اضغط على زر الإدخال للاختيار",
"ENTER_TO_REMOVE": "اضغط على زر الإدخال للحذف", "ENTER_TO_REMOVE": "اضغط على زر الإدخال للحذف",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "اختر واحدا", "SELECT_ONE": "اختر واحدا",
"SELECT": "اختر" "SELECT": "اختر"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "تغيير الأولوية", "CHANGE_PRIORITY": "تغيير الأولوية",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "لا شيء",
"LOW": "منخفضة",
"MEDIUM": "متوسطة",
"HIGH": "عالية",
"URGENT": "عاجل"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "Fetching custom attributes", "LOADING": "Fetching custom attributes",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Conversation",
"CONTACT": "Contact"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Text",
"NUMBER": "Number",
"LINK": "Link",
"DATE": "Date",
"LIST": "List",
"CHECKBOX": "Checkbox"
},
"ADD": { "ADD": {
"TITLE": "Add Custom Attribute", "TITLE": "Add Custom Attribute",
"SUBMIT": "Create", "SUBMIT": "Create",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Open conversation" "OPEN_CONVERSATION": "Open conversation"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "None",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Press enter to select", "ENTER_TO_SELECT": "Press enter to select",
"ENTER_TO_REMOVE": "Press enter to remove", "ENTER_TO_REMOVE": "Press enter to remove",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Select one", "SELECT_ONE": "Select one",
"SELECT": "Select" "SELECT": "Select"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Change Priority", "CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "None",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "Fetching custom attributes", "LOADING": "Fetching custom attributes",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Разговор",
"CONTACT": "Contact"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Text",
"NUMBER": "Number",
"LINK": "Link",
"DATE": "Date",
"LIST": "List",
"CHECKBOX": "Checkbox"
},
"ADD": { "ADD": {
"TITLE": "Add Custom Attribute", "TITLE": "Add Custom Attribute",
"SUBMIT": "Създаване", "SUBMIT": "Създаване",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Open conversation" "OPEN_CONVERSATION": "Open conversation"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "None",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Press enter to select", "ENTER_TO_SELECT": "Press enter to select",
"ENTER_TO_REMOVE": "Press enter to remove", "ENTER_TO_REMOVE": "Press enter to remove",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Select one", "SELECT_ONE": "Select one",
"SELECT": "Select" "SELECT": "Select"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Change Priority", "CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "None",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "S'estan recollint atributs personalitzats", "LOADING": "S'estan recollint atributs personalitzats",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Conversa",
"CONTACT": "Contacte"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Llista",
"NUMBER": "Número",
"LINK": "Enllaç",
"DATE": "Date",
"LIST": "Llista",
"CHECKBOX": "Casella de selecció"
},
"ADD": { "ADD": {
"TITLE": "Afegir atribut personalitzat", "TITLE": "Afegir atribut personalitzat",
"SUBMIT": "Crear", "SUBMIT": "Crear",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Afegeix SLA", "ADD_SLA": "Afegeix SLA",
"OPEN_CONVERSATION": "Obrir conversa" "OPEN_CONVERSATION": "Obrir conversa"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "Ningú",
"LOW": "Baixa",
"MEDIUM": "Mitjana",
"HIGH": "Alta",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Presiona retorn (tecla enter) per seleccionar", "ENTER_TO_SELECT": "Presiona retorn (tecla enter) per seleccionar",
"ENTER_TO_REMOVE": "Presiona retorn (tecla enter) per eliminar", "ENTER_TO_REMOVE": "Presiona retorn (tecla enter) per eliminar",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Selecciona un", "SELECT_ONE": "Selecciona un",
"SELECT": "Selecciona" "SELECT": "Selecciona"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Canvia la prioritat", "CHANGE_PRIORITY": "Canvia la prioritat",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "Ningú",
"LOW": "Baixa",
"MEDIUM": "Mitjana",
"HIGH": "Alta",
"URGENT": "Urgent"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "Fetching custom attributes", "LOADING": "Fetching custom attributes",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Conversation",
"CONTACT": "Contact"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Text",
"NUMBER": "Number",
"LINK": "Link",
"DATE": "Date",
"LIST": "List",
"CHECKBOX": "Checkbox"
},
"ADD": { "ADD": {
"TITLE": "Add Custom Attribute", "TITLE": "Add Custom Attribute",
"SUBMIT": "Create", "SUBMIT": "Create",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Open conversation" "OPEN_CONVERSATION": "Open conversation"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "Nic",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Typ zprávy", "MESSAGE_TYPE": "Typ zprávy",
"MESSAGE_CONTAINS": "Zpráva obsahuje", "MESSAGE_CONTAINS": "Zpráva obsahuje",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Stiskněte Enter pro vybrání", "ENTER_TO_SELECT": "Stiskněte Enter pro vybrání",
"ENTER_TO_REMOVE": "Stiskněte Enter pro odebrání", "ENTER_TO_REMOVE": "Stiskněte Enter pro odebrání",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Vyberte jeden", "SELECT_ONE": "Vyberte jeden",
"SELECT": "Select" "SELECT": "Select"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Change Priority", "CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Přidat soukromou poznámku", "ADD_PRIVATE_NOTE": "Přidat soukromou poznámku",
"SEND_WEBHOOK_EVENT": "Poslat událost webhook" "SEND_WEBHOOK_EVENT": "Poslat událost webhook"
},
"PRIORITY_TYPES": {
"NONE": "Nic",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "Henter brugerdefinerede attributter", "LOADING": "Henter brugerdefinerede attributter",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Samtale",
"CONTACT": "Kontakt"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Tekst",
"NUMBER": "Nummer",
"LINK": "Link",
"DATE": "Date",
"LIST": "Liste",
"CHECKBOX": "Afkrydsningsfelt"
},
"ADD": { "ADD": {
"TITLE": "Tilføj Tilpasset Attribut", "TITLE": "Tilføj Tilpasset Attribut",
"SUBMIT": "Opret", "SUBMIT": "Opret",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Åbn samtale" "OPEN_CONVERSATION": "Åbn samtale"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "Ingen",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Tryk enter for at vælge", "ENTER_TO_SELECT": "Tryk enter for at vælge",
"ENTER_TO_REMOVE": "Tryk enter for at fjerne", "ENTER_TO_REMOVE": "Tryk enter for at fjerne",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Vælg en", "SELECT_ONE": "Vælg en",
"SELECT": "Vælg" "SELECT": "Vælg"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Change Priority", "CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "Ingen",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "Benutzerdefinierte Attribute abrufen", "LOADING": "Benutzerdefinierte Attribute abrufen",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Konversation",
"CONTACT": "Kontakt"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Text",
"NUMBER": "Nummer",
"LINK": "Link",
"DATE": "Date",
"LIST": "Liste",
"CHECKBOX": "Checkbox"
},
"ADD": { "ADD": {
"TITLE": "Benutzerdefiniertes Attribut hinzufügen", "TITLE": "Benutzerdefiniertes Attribut hinzufügen",
"SUBMIT": "Erstellen", "SUBMIT": "Erstellen",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Unterhaltung öffnen" "OPEN_CONVERSATION": "Unterhaltung öffnen"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "Keine",
"LOW": "Niedrig",
"MEDIUM": "Mittel",
"HIGH": "Hoch",
"URGENT": "Dringend"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Drücken Sie zur Auswahl die Eingabetaste", "ENTER_TO_SELECT": "Drücken Sie zur Auswahl die Eingabetaste",
"ENTER_TO_REMOVE": "Drücken Sie zum Entfernen die Eingabetaste", "ENTER_TO_REMOVE": "Drücken Sie zum Entfernen die Eingabetaste",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Eines wählen", "SELECT_ONE": "Eines wählen",
"SELECT": "Auswählen" "SELECT": "Auswählen"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Priorität ändern", "CHANGE_PRIORITY": "Priorität ändern",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "Keine",
"LOW": "Niedrig",
"MEDIUM": "Mittel",
"HIGH": "Hoch",
"URGENT": "Dringend"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "Λήψη προσαρμοσμένων ιδιοτήτων", "LOADING": "Λήψη προσαρμοσμένων ιδιοτήτων",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Συνομιλία",
"CONTACT": "Επαφές"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Κείμενο",
"NUMBER": "Αριθμός",
"LINK": "Σύνδεσμος",
"DATE": "Date",
"LIST": "Λίστα",
"CHECKBOX": "Checkbox"
},
"ADD": { "ADD": {
"TITLE": "Προσθήκη προσαρμοσμένης ιδιότητας", "TITLE": "Προσθήκη προσαρμοσμένης ιδιότητας",
"SUBMIT": "Δημιουργία", "SUBMIT": "Δημιουργία",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Άνοιγμα συνομιλίας" "OPEN_CONVERSATION": "Άνοιγμα συνομιλίας"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "Κανένα",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Πάτησε enter για επιλογή", "ENTER_TO_SELECT": "Πάτησε enter για επιλογή",
"ENTER_TO_REMOVE": "Πάτησε enter για αφαίρεση", "ENTER_TO_REMOVE": "Πάτησε enter για αφαίρεση",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Επιλέξτε ένα", "SELECT_ONE": "Επιλέξτε ένα",
"SELECT": "Επιλογή" "SELECT": "Επιλογή"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Change Priority", "CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "Κανένα",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
} }
} }
} }

View File

@ -146,6 +146,7 @@
"SEND_WEBHOOK_EVENT": "Send Webhook Event", "SEND_WEBHOOK_EVENT": "Send Webhook Event",
"SEND_ATTACHMENT": "Send Attachment", "SEND_ATTACHMENT": "Send Attachment",
"SEND_MESSAGE": "Send a Message", "SEND_MESSAGE": "Send a Message",
"ADD_PRIVATE_NOTE": "Add a Private Note",
"CHANGE_PRIORITY": "Change Priority", "CHANGE_PRIORITY": "Change Priority",
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Open conversation" "OPEN_CONVERSATION": "Open conversation"

View File

@ -137,6 +137,70 @@
} }
} }
}, },
"WHATSAPP": {
"HEADER_TITLE": "WhatsApp campaigns",
"NEW_CAMPAIGN": "Create campaign",
"EMPTY_STATE": {
"TITLE": "No WhatsApp campaigns are available",
"SUBTITLE": "Launch a WhatsApp campaign to reach your customers directly. Send offers or make announcements with ease. Click 'Create campaign' to get started."
},
"CARD": {
"STATUS": {
"COMPLETED": "Completed",
"SCHEDULED": "Scheduled"
},
"CAMPAIGN_DETAILS": {
"SENT_FROM": "Sent from",
"ON": "on"
}
},
"CREATE": {
"TITLE": "Create WhatsApp campaign",
"CANCEL_BUTTON_TEXT": "Cancel",
"CREATE_BUTTON_TEXT": "Create",
"FORM": {
"TITLE": {
"LABEL": "Title",
"PLACEHOLDER": "Please enter the title of campaign",
"ERROR": "Title is required"
},
"INBOX": {
"LABEL": "Select Inbox",
"PLACEHOLDER": "Select Inbox",
"ERROR": "Inbox is required"
},
"TEMPLATE": {
"LABEL": "WhatsApp Template",
"PLACEHOLDER": "Select a template",
"INFO": "Select a template to use for this campaign.",
"ERROR": "Template is required",
"PREVIEW_TITLE": "Process {templateName}",
"LANGUAGE": "Language",
"CATEGORY": "Category",
"VARIABLES_LABEL": "Variables",
"VARIABLE_PLACEHOLDER": "Enter value for {variable}"
},
"AUDIENCE": {
"LABEL": "Audience",
"PLACEHOLDER": "Select the customer labels",
"ERROR": "Audience is required"
},
"SCHEDULED_AT": {
"LABEL": "Scheduled time",
"PLACEHOLDER": "Please select the time",
"ERROR": "Scheduled time is required"
},
"BUTTONS": {
"CREATE": "Create",
"CANCEL": "Cancel"
},
"API": {
"SUCCESS_MESSAGE": "WhatsApp campaign created successfully",
"ERROR_MESSAGE": "There was an error. Please try again."
}
}
}
},
"CONFIRM_DELETE": { "CONFIRM_DELETE": {
"TITLE": "Are you sure to delete?", "TITLE": "Are you sure to delete?",
"DESCRIPTION": "The delete action is permanent and cannot be reversed.", "DESCRIPTION": "The delete action is permanent and cannot be reversed.",

View File

@ -222,10 +222,17 @@
"DESC": "Start supporting your customers via WhatsApp.", "DESC": "Start supporting your customers via WhatsApp.",
"PROVIDERS": { "PROVIDERS": {
"LABEL": "API Provider", "LABEL": "API Provider",
"WHATSAPP_EMBEDDED": "WhatsApp Business",
"TWILIO": "Twilio", "TWILIO": "Twilio",
"WHATSAPP_CLOUD": "WhatsApp Cloud", "WHATSAPP_CLOUD": "WhatsApp Cloud",
"WHATSAPP_CLOUD_DESC": "Quick setup through Meta",
"TWILIO_DESC": "Connect via Twilio credentials",
"360_DIALOG": "360Dialog" "360_DIALOG": "360Dialog"
}, },
"SELECT_PROVIDER": {
"TITLE": "Select your API provider",
"DESCRIPTION": "Choose your WhatsApp provider. You can connect directly through Meta which requires no setup, or connect through Twilio using your account credentials."
},
"INBOX_NAME": { "INBOX_NAME": {
"LABEL": "Inbox Name", "LABEL": "Inbox Name",
"PLACEHOLDER": "Please enter an inbox name", "PLACEHOLDER": "Please enter an inbox name",
@ -264,6 +271,28 @@
"WEBHOOK_VERIFICATION_TOKEN": "Webhook Verification Token" "WEBHOOK_VERIFICATION_TOKEN": "Webhook Verification Token"
}, },
"SUBMIT_BUTTON": "Create WhatsApp Channel", "SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
"TITLE": "Quick Setup with Meta",
"DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
"SECURE_AUTH": "Secure OAuth based authentication",
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
"WAITING_FOR_BUSINESS_INFO": "Please complete business setup in the Meta window...",
"PROCESSING": "Setting up your WhatsApp Business Account",
"LOADING_SDK": "Loading Facebook SDK...",
"CANCELLED": "WhatsApp Signup was cancelled",
"SUCCESS_TITLE": "WhatsApp Business Account Connected!",
"WAITING_FOR_AUTH": "Waiting for authentication...",
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
"SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
},
"API": { "API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel" "ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
} }

View File

@ -478,6 +478,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -319,6 +319,7 @@
"CSAT": "CSAT", "CSAT": "CSAT",
"LIVE_CHAT": "Live Chat", "LIVE_CHAT": "Live Chat",
"SMS": "SMS", "SMS": "SMS",
"WHATSAPP": "WhatsApp",
"CAMPAIGNS": "Campaigns", "CAMPAIGNS": "Campaigns",
"ONGOING": "Ongoing", "ONGOING": "Ongoing",
"ONE_OFF": "One off", "ONE_OFF": "One off",

View File

@ -5,6 +5,18 @@
"LOADING": "Obtener atributos personalizados", "LOADING": "Obtener atributos personalizados",
"DESCRIPTION": "Un atributo personalizado rastrea detalles adicionales sobre tus contactos o conversaciones, como el plan de suscripción o la fecha de su primera compra. Puede agregar diferentes tipos de atributos personalizados, como textos, listas o números, para capturar la información específica que necesita.", "DESCRIPTION": "Un atributo personalizado rastrea detalles adicionales sobre tus contactos o conversaciones, como el plan de suscripción o la fecha de su primera compra. Puede agregar diferentes tipos de atributos personalizados, como textos, listas o números, para capturar la información específica que necesita.",
"LEARN_MORE": "Aprende más sobre los atributos personalizados", "LEARN_MORE": "Aprende más sobre los atributos personalizados",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Conversación",
"CONTACT": "Contacto"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Texto",
"NUMBER": "Número",
"LINK": "Enlace",
"DATE": "Date",
"LIST": "Lista",
"CHECKBOX": "Casilla"
},
"ADD": { "ADD": {
"TITLE": "Añadir atributo personalizado", "TITLE": "Añadir atributo personalizado",
"SUBMIT": "Crear", "SUBMIT": "Crear",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Añadir SLA", "ADD_SLA": "Añadir SLA",
"OPEN_CONVERSATION": "Abrir conversación" "OPEN_CONVERSATION": "Abrir conversación"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "Ninguna",
"LOW": "Baja",
"MEDIUM": "Media",
"HIGH": "Alta",
"URGENT": "Urgente"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Tipo de mensaje", "MESSAGE_TYPE": "Tipo de mensaje",
"MESSAGE_CONTAINS": "El mensaje contiene", "MESSAGE_CONTAINS": "El mensaje contiene",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Pulse Enter para seleccionar", "ENTER_TO_SELECT": "Pulse Enter para seleccionar",
"ENTER_TO_REMOVE": "Presione Enter para eliminar", "ENTER_TO_REMOVE": "Presione Enter para eliminar",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Seleccione uno", "SELECT_ONE": "Seleccione uno",
"SELECT": "Seleccionar" "SELECT": "Seleccionar"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Cambiar prioridad", "CHANGE_PRIORITY": "Cambiar prioridad",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Enviar Evento de Webhook" "SEND_WEBHOOK_EVENT": "Enviar Evento de Webhook"
},
"PRIORITY_TYPES": {
"NONE": "Ninguna",
"LOW": "Baja",
"MEDIUM": "Media",
"HIGH": "Alta",
"URGENT": "Urgente"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "واکشی ویژگی‌های سفارشی", "LOADING": "واکشی ویژگی‌های سفارشی",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "گفتگو",
"CONTACT": "مخاطب"
},
"ATTRIBUTE_TYPES": {
"TEXT": "متن",
"NUMBER": "شماره",
"LINK": "پیوند",
"DATE": "Date",
"LIST": "فهرست",
"CHECKBOX": "چک باکس"
},
"ADD": { "ADD": {
"TITLE": "اضافه کردن ویژگی سفارشی", "TITLE": "اضافه کردن ویژگی سفارشی",
"SUBMIT": "ايجاد كردن", "SUBMIT": "ايجاد كردن",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "اضافه کردن SLA", "ADD_SLA": "اضافه کردن SLA",
"OPEN_CONVERSATION": "باز کردن گفتگو" "OPEN_CONVERSATION": "باز کردن گفتگو"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "هیچکدام",
"LOW": "پایین",
"MEDIUM": "متوسط",
"HIGH": "بالا",
"URGENT": "فوری"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "برای انتخاب Enter را فشار دهید", "ENTER_TO_SELECT": "برای انتخاب Enter را فشار دهید",
"ENTER_TO_REMOVE": "برای حذف دکمه enter را فشار دهید", "ENTER_TO_REMOVE": "برای حذف دکمه enter را فشار دهید",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "یکی را انتخاب کن", "SELECT_ONE": "یکی را انتخاب کن",
"SELECT": "انتخاب کنید" "SELECT": "انتخاب کنید"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "تغییر اولویت", "CHANGE_PRIORITY": "تغییر اولویت",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "هیچکدام",
"LOW": "پایین",
"MEDIUM": "متوسط",
"HIGH": "بالا",
"URGENT": "فوری"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "Fetching custom attributes", "LOADING": "Fetching custom attributes",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Conversation",
"CONTACT": "Contact"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Text",
"NUMBER": "Number",
"LINK": "Link",
"DATE": "Date",
"LIST": "List",
"CHECKBOX": "Checkbox"
},
"ADD": { "ADD": {
"TITLE": "Add Custom Attribute", "TITLE": "Add Custom Attribute",
"SUBMIT": "Luo", "SUBMIT": "Luo",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Open conversation" "OPEN_CONVERSATION": "Open conversation"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "None",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Message Type", "MESSAGE_TYPE": "Message Type",
"MESSAGE_CONTAINS": "Message Contains", "MESSAGE_CONTAINS": "Message Contains",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Paina enter valitaksesi", "ENTER_TO_SELECT": "Paina enter valitaksesi",
"ENTER_TO_REMOVE": "Paina enter poistaaksesi", "ENTER_TO_REMOVE": "Paina enter poistaaksesi",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Valitse yksi", "SELECT_ONE": "Valitse yksi",
"SELECT": "Select" "SELECT": "Select"
} }

View File

@ -481,6 +481,37 @@
"ERROR_MESSAGE": "There was an error updating the assistant, please try again.", "ERROR_MESSAGE": "There was an error updating the assistant, please try again.",
"NOT_FOUND": "Could not find the assistant. Please try again." "NOT_FOUND": "Could not find the assistant. Please try again."
}, },
"SETTINGS": {
"BREADCRUMB": {
"ASSISTANT": "Assistant"
},
"BASIC_SETTINGS": {
"TITLE": "Basic settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
"TITLE": "System settings",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
"TITLE": "The Fun Stuff",
"DESCRIPTION": "Add more control to the assistant. (a bit more visual like a story : Query guardrail → scenarios → output) Nudges user to actually utilise these.",
"OPTIONS": {
"GUARDRAILS": {
"TITLE": "Guardrails",
"DESCRIPTION": "Keeps things on track—only the kinds of questions you want your assistant to answer, nothing off-limits or off-topic."
},
"SCENARIOS": {
"TITLE": "Scenarios",
"DESCRIPTION": "Give your assistant some context—like “what to do when a user is stuck,” or “how to act during a refund request.”"
},
"RESPONSE_GUIDELINES": {
"TITLE": "Response guidelines",
"DESCRIPTION": "The vibe and structure of your assistants replies—clear and friendly? Short and snappy? Detailed and formal?"
}
}
}
},
"OPTIONS": { "OPTIONS": {
"EDIT_ASSISTANT": "Edit Assistant", "EDIT_ASSISTANT": "Edit Assistant",
"DELETE_ASSISTANT": "Delete Assistant", "DELETE_ASSISTANT": "Delete Assistant",

View File

@ -99,6 +99,13 @@
"CHANGE_PRIORITY": "Change Priority", "CHANGE_PRIORITY": "Change Priority",
"ADD_PRIVATE_NOTE": "Add a Private Note", "ADD_PRIVATE_NOTE": "Add a Private Note",
"SEND_WEBHOOK_EVENT": "Send Webhook Event" "SEND_WEBHOOK_EVENT": "Send Webhook Event"
},
"PRIORITY_TYPES": {
"NONE": "None",
"LOW": "Low",
"MEDIUM": "Medium",
"HIGH": "High",
"URGENT": "Urgent"
} }
} }
} }

View File

@ -5,6 +5,18 @@
"LOADING": "Récupération des attributs personnalisés", "LOADING": "Récupération des attributs personnalisés",
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.", "DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes", "LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
"CONVERSATION": "Conversation",
"CONTACT": "Contact"
},
"ATTRIBUTE_TYPES": {
"TEXT": "Texte",
"NUMBER": "Nombre",
"LINK": "Lien",
"DATE": "Date",
"LIST": "Liste",
"CHECKBOX": "Case à cocher"
},
"ADD": { "ADD": {
"TITLE": "Ajouter un attribut personnalisé", "TITLE": "Ajouter un attribut personnalisé",
"SUBMIT": "Créer", "SUBMIT": "Créer",

View File

@ -150,6 +150,17 @@
"ADD_SLA": "Add SLA", "ADD_SLA": "Add SLA",
"OPEN_CONVERSATION": "Ouvrir la conversation" "OPEN_CONVERSATION": "Ouvrir la conversation"
}, },
"MESSAGE_TYPES": {
"INCOMING": "Incoming Message",
"OUTGOING": "Outgoing Message"
},
"PRIORITY_TYPES": {
"NONE": "Aucun",
"LOW": "Faible",
"MEDIUM": "Moyenne",
"HIGH": "Élevé",
"URGENT": "Urgent"
},
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Type de message", "MESSAGE_TYPE": "Type de message",
"MESSAGE_CONTAINS": "Le message contient", "MESSAGE_CONTAINS": "Le message contient",

View File

@ -134,6 +134,7 @@
"MULTISELECT": { "MULTISELECT": {
"ENTER_TO_SELECT": "Appuyer sur Entrée pour sélectionner", "ENTER_TO_SELECT": "Appuyer sur Entrée pour sélectionner",
"ENTER_TO_REMOVE": "Appuyer sur Entrée pour supprimer", "ENTER_TO_REMOVE": "Appuyer sur Entrée pour supprimer",
"NO_OPTIONS": "List is empty",
"SELECT_ONE": "Sélectionnez un", "SELECT_ONE": "Sélectionnez un",
"SELECT": "Sélectionner" "SELECT": "Sélectionner"
} }

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