Compare commits

..
Author SHA1 Message Date
Muhsin KelothandGitHub 7f9e94af2e Merge branch 'develop' into feat/github-oauth 2025-06-12 12:00:51 +05:30
Muhsin Keloth cf73ba195e feat: add github auth 2025-06-12 11:55:57 +05:30
166 changed files with 500 additions and 3349 deletions
+1 -12
View File
@@ -10,8 +10,7 @@ function toggleSecretField(e) {
if (!textElement) return; if (!textElement) return;
if (textElement.dataset.secretMasked === 'false') { if (textElement.dataset.secretMasked === 'false') {
const maskedLength = secretField.dataset.secretText?.length || 10; textElement.textContent = '•'.repeat(10);
textElement.textContent = '•'.repeat(maskedLength);
textElement.dataset.secretMasked = 'true'; textElement.dataset.secretMasked = 'true';
toggler.querySelector('svg use').setAttribute('xlink:href', '#eye-show'); toggler.querySelector('svg use').setAttribute('xlink:href', '#eye-show');
@@ -33,13 +32,3 @@ function copySecretField(e) {
navigator.clipboard.writeText(secretField.dataset.secretText); navigator.clipboard.writeText(secretField.dataset.secretText);
} }
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.cell-data__secret-field').forEach(field => {
const span = field.querySelector('[data-secret-masked]');
if (span && span.dataset.secretMasked === 'true') {
const len = field.dataset.secretText?.length || 10;
span.textContent = '•'.repeat(len);
}
});
});
@@ -46,25 +46,17 @@
.cell-data__secret-field { .cell-data__secret-field {
align-items: center; align-items: center;
color: $hint-grey;
display: flex; display: flex;
span { span {
flex: 0 0 auto; flex: 1;
} }
[data-secret-toggler], button {
[data-secret-copier] { margin-left: 5px;
background: transparent;
border: 0;
color: inherit;
margin-left: 0.5rem;
padding: 0;
svg { svg {
fill: currentColor; fill: currentColor;
height: 1.25rem;
width: 1.25rem;
} }
} }
} }
@@ -31,7 +31,7 @@ class V2::Reports::LabelSummaryBuilder < V2::Reports::BaseSummaryBuilder
resolved_counts: fetch_resolved_counts(conversation_filter), resolved_counts: fetch_resolved_counts(conversation_filter),
resolution_metrics: fetch_metrics(conversation_filter, 'conversation_resolved', use_business_hours), resolution_metrics: fetch_metrics(conversation_filter, 'conversation_resolved', use_business_hours),
first_response_metrics: fetch_metrics(conversation_filter, 'first_response', use_business_hours), first_response_metrics: fetch_metrics(conversation_filter, 'first_response', use_business_hours),
reply_metrics: fetch_metrics(conversation_filter, 'reply_time', use_business_hours) reply_metrics: fetch_metrics(conversation_filter, 'reply', use_business_hours)
} }
end end
@@ -63,9 +63,7 @@ class V2::Reports::LabelSummaryBuilder < V2::Reports::BaseSummaryBuilder
end end
def fetch_resolved_counts(conversation_filter) def fetch_resolved_counts(conversation_filter)
# since the base query is ActsAsTaggableOn, fetch_counts(conversation_filter.merge(status: :resolved))
# the status :resolved won't automatically be converted to integer status
fetch_counts(conversation_filter.merge(status: Conversation.statuses[:resolved]))
end end
def fetch_counts(conversation_filter) def fetch_counts(conversation_filter)
@@ -1,23 +1,32 @@
class Api::V1::Accounts::Google::AuthorizationsController < Api::V1::Accounts::OauthAuthorizationController class Api::V1::Accounts::Google::AuthorizationsController < Api::V1::Accounts::BaseController
include GoogleConcern include GoogleConcern
before_action :check_authorization
def create def create
email = params[:authorization][:email]
redirect_url = google_client.auth_code.authorize_url( redirect_url = google_client.auth_code.authorize_url(
{ {
redirect_uri: "#{base_url}/google/callback", redirect_uri: "#{base_url}/google/callback",
scope: scope, scope: 'email profile https://mail.google.com/',
response_type: 'code', response_type: 'code',
prompt: 'consent', # the oauth flow does not return a refresh token, this is supposed to fix it prompt: 'consent', # the oauth flow does not return a refresh token, this is supposed to fix it
access_type: 'offline', # the default is 'online' access_type: 'offline', # the default is 'online'
state: state,
client_id: GlobalConfigService.load('GOOGLE_OAUTH_CLIENT_ID', nil) client_id: GlobalConfigService.load('GOOGLE_OAUTH_CLIENT_ID', nil)
} }
) )
if redirect_url if redirect_url
cache_key = "google::#{email.downcase}"
::Redis::Alfred.setex(cache_key, Current.account.id, 5.minutes)
render json: { success: true, url: redirect_url } render json: { success: true, url: redirect_url }
else else
render json: { success: false }, status: :unprocessable_entity render json: { success: false }, status: :unprocessable_entity
end end
end end
private
def check_authorization
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
end
end end
@@ -1,6 +1,7 @@
class Api::V1::Accounts::Instagram::AuthorizationsController < Api::V1::Accounts::OauthAuthorizationController class Api::V1::Accounts::Instagram::AuthorizationsController < Api::V1::Accounts::BaseController
include InstagramConcern include InstagramConcern
include Instagram::IntegrationHelper include Instagram::IntegrationHelper
before_action :check_authorization
def create def create
# https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login#step-1--get-authorization # https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login#step-1--get-authorization
@@ -20,4 +21,10 @@ class Api::V1::Accounts::Instagram::AuthorizationsController < Api::V1::Accounts
render json: { success: false }, status: :unprocessable_entity render json: { success: false }, status: :unprocessable_entity
end end
end end
private
def check_authorization
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
end
end end
@@ -0,0 +1,13 @@
class Api::V1::Accounts::Integrations::GithubController < Api::V1::Accounts::BaseController
before_action :check_authorization
def show
@github_integration = Current.account.hooks.find_by(app_id: 'github')
end
private
def check_authorization
authorize ::Webhook, :show?
end
end
@@ -1,5 +1,5 @@
class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::BaseController class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::BaseController
before_action :fetch_conversation, only: [:create_issue, :link_issue, :unlink_issue, :linked_issues] before_action :fetch_conversation, only: [:link_issue, :linked_issues]
before_action :fetch_hook, only: [:destroy] before_action :fetch_hook, only: [:destroy]
def destroy def destroy
@@ -31,12 +31,6 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas
if issue[:error] if issue[:error]
render json: { error: issue[:error] }, status: :unprocessable_entity render json: { error: issue[:error] }, status: :unprocessable_entity
else else
Linear::ActivityMessageService.new(
conversation: @conversation,
action_type: :issue_created,
issue_data: { id: issue[:data][:identifier] },
user: Current.user
).perform
render json: issue[:data], status: :ok render json: issue[:data], status: :ok
end end
end end
@@ -48,30 +42,17 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas
if issue[:error] if issue[:error]
render json: { error: issue[:error] }, status: :unprocessable_entity render json: { error: issue[:error] }, status: :unprocessable_entity
else else
Linear::ActivityMessageService.new(
conversation: @conversation,
action_type: :issue_linked,
issue_data: { id: issue_id },
user: Current.user
).perform
render json: issue[:data], status: :ok render json: issue[:data], status: :ok
end end
end end
def unlink_issue def unlink_issue
link_id = permitted_params[:link_id] link_id = permitted_params[:link_id]
issue_id = permitted_params[:issue_id]
issue = linear_processor_service.unlink_issue(link_id) issue = linear_processor_service.unlink_issue(link_id)
if issue[:error] if issue[:error]
render json: { error: issue[:error] }, status: :unprocessable_entity render json: { error: issue[:error] }, status: :unprocessable_entity
else else
Linear::ActivityMessageService.new(
conversation: @conversation,
action_type: :issue_unlinked,
issue_data: { id: issue_id },
user: Current.user
).perform
render json: issue[:data], status: :ok render json: issue[:data], status: :ok
end end
end end
@@ -1,14 +0,0 @@
class Api::V1::Accounts::Integrations::NotionController < Api::V1::Accounts::BaseController
before_action :fetch_hook, only: [:destroy]
def destroy
@hook.destroy!
head :ok
end
private
def fetch_hook
@hook = Integrations::Hook.where(account: Current.account).find_by(app_id: 'notion')
end
end
@@ -1,19 +1,28 @@
class Api::V1::Accounts::Microsoft::AuthorizationsController < Api::V1::Accounts::OauthAuthorizationController class Api::V1::Accounts::Microsoft::AuthorizationsController < Api::V1::Accounts::BaseController
include MicrosoftConcern include MicrosoftConcern
before_action :check_authorization
def create def create
email = params[:authorization][:email]
redirect_url = microsoft_client.auth_code.authorize_url( redirect_url = microsoft_client.auth_code.authorize_url(
{ {
redirect_uri: "#{base_url}/microsoft/callback", redirect_uri: "#{base_url}/microsoft/callback",
scope: scope, scope: 'offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send openid profile',
state: state,
prompt: 'consent' prompt: 'consent'
} }
) )
if redirect_url if redirect_url
cache_key = "microsoft::#{email.downcase}"
::Redis::Alfred.setex(cache_key, Current.account.id, 5.minutes)
render json: { success: true, url: redirect_url } render json: { success: true, url: redirect_url }
else else
render json: { success: false }, status: :unprocessable_entity render json: { success: false }, status: :unprocessable_entity
end end
end end
private
def check_authorization
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
end
end end
@@ -1,21 +0,0 @@
class Api::V1::Accounts::Notion::AuthorizationsController < Api::V1::Accounts::OauthAuthorizationController
include NotionConcern
def create
redirect_url = notion_client.auth_code.authorize_url(
{
redirect_uri: "#{base_url}/notion/callback",
response_type: 'code',
owner: 'user',
state: state,
client_id: GlobalConfigService.load('NOTION_CLIENT_ID', nil)
}
)
if redirect_url
render json: { success: true, url: redirect_url }
else
render json: { success: false }, status: :unprocessable_entity
end
end
end
@@ -1,23 +0,0 @@
class Api::V1::Accounts::OauthAuthorizationController < Api::V1::Accounts::BaseController
before_action :check_authorization
protected
def scope
''
end
def state
Current.account.to_sgid(expires_in: 15.minutes).to_s
end
def base_url
ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
end
private
def check_authorization
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
end
end
+2 -2
View File
@@ -14,7 +14,7 @@ module GoogleConcern
private private
def scope def base_url
'email profile https://mail.google.com/' ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
end end
end end
@@ -15,7 +15,7 @@ module MicrosoftConcern
private private
def scope def base_url
'offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send openid profile email' ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
end end
end end
@@ -1,21 +0,0 @@
module NotionConcern
extend ActiveSupport::Concern
def notion_client
app_id = GlobalConfigService.load('NOTION_CLIENT_ID', nil)
app_secret = GlobalConfigService.load('NOTION_CLIENT_SECRET', nil)
::OAuth2::Client.new(app_id, app_secret, {
site: 'https://api.notion.com',
authorize_url: 'https://api.notion.com/v1/oauth/authorize',
token_url: 'https://api.notion.com/v1/oauth/token',
auth_scheme: :basic_auth
})
end
private
def scope
'read'
end
end
@@ -0,0 +1,70 @@
class Github::CallbacksController < ApplicationController
include Github::IntegrationHelper
before_action :authenticate_user!
before_action :set_account
def show
if params[:error].present?
redirect_to app_account_integrations_path(account_id: @account.id), alert: params[:error_description]
return
end
if params[:code].present?
response = exchange_code_for_token(params[:code])
if response[:access_token].present?
hook = @account.hooks.find_or_initialize_by(app_id: 'github')
hook.access_token = response[:access_token]
hook.status = 'enabled'
if hook.save
redirect_to app_account_integrations_path(account_id: @account.id), notice: 'GitHub integration connected successfully!'
else
redirect_to app_account_integrations_path(account_id: @account.id), alert: 'Failed to save GitHub integration'
end
else
redirect_to app_account_integrations_path(account_id: @account.id), alert: 'Failed to get access token from GitHub'
end
else
redirect_to app_account_integrations_path(account_id: @account.id), alert: 'GitHub authorization failed'
end
end
private
def set_account
@account = Current.user.accounts.find(params[:account_id]) if params[:account_id].present?
@account ||= Current.user.accounts.first
return if @account
redirect_to root_path, alert: 'Account not found'
end
def exchange_code_for_token(code)
client_id = GlobalConfigService.load('GITHUB_CLIENT_ID', nil)
client_secret = GlobalConfigService.load('GITHUB_CLIENT_SECRET', nil)
return {} unless client_id && client_secret
response = HTTParty.post('https://github.com/login/oauth/access_token', {
body: {
client_id: client_id,
client_secret: client_secret,
code: code
},
headers: {
'Accept' => 'application/json'
}
})
if response.success?
JSON.parse(response.body).with_indifferent_access
else
{}
end
rescue StandardError => e
Rails.logger.error "GitHub OAuth error: #{e.message}"
{}
end
end
@@ -1,36 +0,0 @@
class Notion::CallbacksController < OauthCallbackController
include NotionConcern
private
def provider_name
'notion'
end
def oauth_client
notion_client
end
def handle_response
hook = account.hooks.new(
access_token: parsed_body['access_token'],
status: 'enabled',
app_id: 'notion',
settings: {
token_type: parsed_body['token_type'],
workspace_name: parsed_body['workspace_name'],
workspace_id: parsed_body['workspace_id'],
workspace_icon: parsed_body['workspace_icon'],
bot_id: parsed_body['bot_id'],
owner: parsed_body['owner']
}
)
hook.save!
redirect_to notion_redirect_uri
end
def notion_redirect_uri
"#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/#{account.id}/settings/integrations/notion"
end
end
+8 -8
View File
@@ -6,6 +6,7 @@ class OauthCallbackController < ApplicationController
) )
handle_response handle_response
::Redis::Alfred.delete(cache_key)
rescue StandardError => e rescue StandardError => e
ChatwootExceptionTracker.new(e).capture_exception ChatwootExceptionTracker.new(e).capture_exception
redirect_to '/' redirect_to '/'
@@ -63,6 +64,10 @@ class OauthCallbackController < ApplicationController
raise NotImplementedError raise NotImplementedError
end end
def cache_key
"#{provider_name}::#{users_data['email'].downcase}"
end
def create_channel_with_inbox def create_channel_with_inbox
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
channel_email = Channel::Email.create!(email: users_data['email'], account: account) channel_email = Channel::Email.create!(email: users_data['email'], account: account)
@@ -81,17 +86,12 @@ class OauthCallbackController < ApplicationController
decoded_token[0] decoded_token[0]
end end
def account_from_signed_id def account_id
raise ActionController::BadRequest, 'Missing state variable' if params[:state].blank? ::Redis::Alfred.get(cache_key)
account = GlobalID::Locator.locate_signed(params[:state])
raise 'Invalid or expired state' if account.nil?
account
end end
def account def account
@account ||= account_from_signed_id @account ||= Account.find(account_id)
end end
# Fallback name, for when name field is missing from users_data # Fallback name, for when name field is missing from users_data
@@ -39,8 +39,8 @@ 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],
'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],
'instagram' => %w[INSTAGRAM_APP_ID INSTAGRAM_APP_SECRET INSTAGRAM_VERIFY_TOKEN INSTAGRAM_API_VERSION ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT] 'github' => %w[GITHUB_CLIENT_ID GITHUB_CLIENT_SECRET]
} }
@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])
+71
View File
@@ -0,0 +1,71 @@
module Github::IntegrationHelper
def github_integration_url(account_id)
"#{ENV.fetch('FRONTEND_URL', nil)}/github/callback?account_id=#{account_id}"
end
def github_oauth_url(account_id)
client_id = GlobalConfigService.load('GITHUB_CLIENT_ID', nil)
return nil unless client_id
params = {
client_id: client_id,
redirect_uri: github_integration_url(account_id),
scope: 'repo',
state: generate_state_token(account_id)
}
"https://github.com/login/oauth/authorize?#{params.to_query}"
end
def github_configured?
GlobalConfigService.load('GITHUB_CLIENT_ID', nil).present? &&
GlobalConfigService.load('GITHUB_CLIENT_SECRET', nil).present?
end
def github_integration_enabled?(account)
return false unless github_configured?
account.hooks.exists?(app_id: 'github', status: 'enabled')
end
def github_repositories(access_token)
return [] unless access_token
response = HTTParty.get('https://api.github.com/user/repos', {
headers: {
'Authorization' => "token #{access_token}",
'Accept' => 'application/vnd.github.v3+json'
},
query: {
per_page: 100,
sort: 'updated'
}
})
if response.success?
JSON.parse(response.body)
else
[]
end
rescue StandardError => e
Rails.logger.error "GitHub API error: #{e.message}"
[]
end
private
def generate_state_token(account_id)
payload = {
account_id: account_id,
timestamp: Time.current.to_i
}
JWT.encode(payload, Rails.application.secret_key_base, 'HS256')
end
def verify_state_token(state)
JWT.decode(state, Rails.application.secret_key_base, true, { algorithm: 'HS256' })
rescue JWT::DecodeError
nil
end
end
@@ -33,11 +33,9 @@ class LinearAPI extends ApiClient {
); );
} }
unlinkIssue(linkId, issueIdentifier, conversationId) { unlinkIssue(linkId) {
return axios.post(`${this.url}/unlink_issue`, { return axios.post(`${this.url}/unlink_issue`, {
link_id: linkId, link_id: linkId,
issue_id: issueIdentifier,
conversation_id: conversationId,
}); });
} }
@@ -1,14 +0,0 @@
/* global axios */
import ApiClient from './ApiClient';
class NotionOAuthClient extends ApiClient {
constructor() {
super('notion', { accountScoped: true });
}
generateAuthorization() {
return axios.post(`${this.url}/authorization`);
}
}
export default new NotionOAuthClient();
@@ -91,19 +91,6 @@ describe('#linearAPI', () => {
issueData issueData
); );
}); });
it('creates a valid request with conversation_id', () => {
const issueData = {
title: 'New Issue',
description: 'Issue description',
conversation_id: 123,
};
LinearAPIClient.createIssue(issueData);
expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/integrations/linear/create_issue',
issueData
);
});
}); });
describe('link_issue', () => { describe('link_issue', () => {
@@ -133,18 +120,6 @@ describe('#linearAPI', () => {
} }
); );
}); });
it('creates a valid request with title', () => {
LinearAPIClient.link_issue(1, 'ENG-123', 'Sample Issue');
expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/integrations/linear/link_issue',
{
issue_id: 'ENG-123',
conversation_id: 1,
title: 'Sample Issue',
}
);
});
}); });
describe('getLinkedIssue', () => { describe('getLinkedIssue', () => {
@@ -189,26 +164,12 @@ describe('#linearAPI', () => {
window.axios = originalAxios; window.axios = originalAxios;
}); });
it('creates a valid request with link_id only', () => { it('creates a valid request', () => {
LinearAPIClient.unlinkIssue('link123'); LinearAPIClient.unlinkIssue(1);
expect(axiosMock.post).toHaveBeenCalledWith( expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/integrations/linear/unlink_issue', '/api/v1/integrations/linear/unlink_issue',
{ {
link_id: 'link123', link_id: 1,
issue_id: undefined,
conversation_id: undefined,
}
);
});
it('creates a valid request with all parameters', () => {
LinearAPIClient.unlinkIssue('link123', 'ENG-456', 789);
expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/integrations/linear/unlink_issue',
{
link_id: 'link123',
issue_id: 'ENG-456',
conversation_id: 789,
} }
); );
}); });
@@ -47,7 +47,7 @@
@apply max-w-full; @apply max-w-full;
.multiselect__option { .multiselect__option {
@apply text-sm font-normal flex justify-between items-center; @apply text-sm font-normal;
span { span {
@apply inline-block overflow-hidden text-ellipsis whitespace-nowrap w-fit; @apply inline-block overflow-hidden text-ellipsis whitespace-nowrap w-fit;
@@ -58,7 +58,7 @@
} }
&::after { &::after {
@apply bottom-0 flex items-center justify-center text-center relative px-1 leading-tight; @apply bottom-0 flex items-center justify-center text-center;
} }
&.multiselect__option--highlight { &.multiselect__option--highlight {
@@ -74,7 +74,7 @@
} }
&.multiselect__option--highlight::after { &.multiselect__option--highlight::after {
@apply bg-transparent text-n-slate-12; @apply bg-transparent;
} }
&.multiselect__option--selected { &.multiselect__option--selected {
@@ -19,7 +19,6 @@ const isConversationRoute = computed(() => {
'conversation_through_mentions', 'conversation_through_mentions',
'conversation_through_unattended', 'conversation_through_unattended',
'conversation_through_participating', 'conversation_through_participating',
'inbox_view_conversation',
]; ];
return CONVERSATION_ROUTES.includes(route.name); return CONVERSATION_ROUTES.includes(route.name);
}); });
@@ -379,7 +379,7 @@ const shouldRenderMessage = computed(() => {
function openContextMenu(e) { function openContextMenu(e) {
const shouldSkipContextMenu = const shouldSkipContextMenu =
e.target?.classList.contains('skip-context-menu') || e.target?.classList.contains('skip-context-menu') ||
['a', 'img'].includes(e.target?.tagName.toLowerCase()); e.target?.tagName.toLowerCase() === 'a';
if (shouldSkipContextMenu || getSelection().toString()) { if (shouldSkipContextMenu || getSelection().toString()) {
return; return;
} }
@@ -14,8 +14,7 @@ const fromEmail = computed(() => {
}); });
const toEmail = computed(() => { const toEmail = computed(() => {
const { toEmails, email } = contentAttributes.value; return contentAttributes.value?.email?.to ?? [];
return email?.to ?? toEmails ?? [];
}); });
const ccEmail = computed(() => { const ccEmail = computed(() => {
@@ -756,7 +756,6 @@ function toggleSelectAll(check) {
} }
useEmitter('fetch_conversation_stats', () => { useEmitter('fetch_conversation_stats', () => {
if (hasAppliedFiltersOrActiveFolders.value) return;
store.dispatch('conversationStats/get', conversationFilters.value); store.dispatch('conversationStats/get', conversationFilters.value);
}); });
@@ -855,7 +854,7 @@ watch(conversationFilters, (newVal, oldVal) => {
:active-status="activeStatus" :active-status="activeStatus"
:is-on-expanded-layout="isOnExpandedLayout" :is-on-expanded-layout="isOnExpandedLayout"
:conversation-stats="conversationStats" :conversation-stats="conversationStats"
:is-list-loading="chatListLoading && !conversationList.length" :is-list-loading="chatListLoading"
@add-folders="onClickOpenAddFoldersModal" @add-folders="onClickOpenAddFoldersModal"
@delete-folders="onClickOpenDeleteFoldersModal" @delete-folders="onClickOpenDeleteFoldersModal"
@filters-modal="onToggleAdvanceFiltersModal" @filters-modal="onToggleAdvanceFiltersModal"
@@ -21,7 +21,7 @@ const props = defineProps({
const isRelaxed = computed(() => props.type === 'relaxed'); const isRelaxed = computed(() => props.type === 'relaxed');
const headerClass = computed(() => const headerClass = computed(() =>
isRelaxed.value isRelaxed.value
? 'ltr:first:rounded-bl-lg ltr:first:rounded-tl-lg ltr:last:rounded-br-lg ltr:last:rounded-tr-lg rtl:first:rounded-br-lg rtl:first:rounded-tr-lg rtl:last:rounded-bl-lg rtl:last:rounded-tl-lg' ? 'first:rounded-bl-lg first:rounded-tl-lg last:rounded-br-lg last:rounded-tr-lg'
: '' : ''
); );
</script> </script>
@@ -183,18 +183,13 @@ const createIssue = async () => {
state_id: formState.stateId || undefined, state_id: formState.stateId || undefined,
priority: formState.priority || undefined, priority: formState.priority || undefined,
label_ids: formState.labelId ? [formState.labelId] : undefined, label_ids: formState.labelId ? [formState.labelId] : undefined,
conversation_id: props.conversationId,
}; };
try { try {
isCreating.value = true; isCreating.value = true;
const response = await LinearAPI.createIssue(payload); const response = await LinearAPI.createIssue(payload);
const { identifier: issueIdentifier } = response.data; const { id: issueId } = response.data;
await LinearAPI.link_issue( await LinearAPI.link_issue(props.conversationId, issueId, props.title);
props.conversationId,
issueIdentifier,
props.title
);
useAlert(t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CREATE_SUCCESS')); useAlert(t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CREATE_SUCCESS'));
useTrack(LINEAR_EVENTS.CREATE_ISSUE); useTrack(LINEAR_EVENTS.CREATE_ISSUE);
onClose(); onClose();
@@ -46,9 +46,9 @@ const loadLinkedIssues = async () => {
} }
}; };
const unlinkIssue = async (linkId, issueIdentifier) => { const unlinkIssue = async linkId => {
try { try {
await LinearAPI.unlinkIssue(linkId, issueIdentifier, props.conversationId); await LinearAPI.unlinkIssue(linkId);
useTrack(LINEAR_EVENTS.UNLINK_ISSUE); useTrack(LINEAR_EVENTS.UNLINK_ISSUE);
linkedIssues.value = linkedIssues.value.filter( linkedIssues.value = linkedIssues.value.filter(
issue => issue.id !== linkId issue => issue.id !== linkId
@@ -110,7 +110,7 @@ onMounted(() => {
<LinearIssueItem <LinearIssueItem
v-for="linkedIssue in linkedIssues" v-for="linkedIssue in linkedIssues"
:key="linkedIssue.id" :key="linkedIssue.id"
class="px-4 pt-3 pb-4 border-b border-n-weak last:border-b-0" class="pt-3 px-4 pb-4 border-b border-n-weak last:border-b-0"
:linked-issue="linkedIssue" :linked-issue="linkedIssue"
@unlink-issue="unlinkIssue" @unlink-issue="unlinkIssue"
/> />
@@ -14,8 +14,6 @@ const props = defineProps({
const emit = defineEmits(['unlinkIssue']); const emit = defineEmits(['unlinkIssue']);
const { linkedIssue } = props;
const priorityMap = { const priorityMap = {
1: 'Urgent', 1: 'Urgent',
2: 'High', 2: 'High',
@@ -23,7 +21,7 @@ const priorityMap = {
4: 'Low', 4: 'Low',
}; };
const issue = computed(() => linkedIssue.issue); const issue = computed(() => props.linkedIssue.issue);
const assignee = computed(() => { const assignee = computed(() => {
const assigneeDetails = issue.value.assignee; const assigneeDetails = issue.value.assignee;
@@ -39,7 +37,7 @@ const labels = computed(() => issue.value.labels?.nodes || []);
const priorityLabel = computed(() => priorityMap[issue.value.priority]); const priorityLabel = computed(() => priorityMap[issue.value.priority]);
const unlinkIssue = () => { const unlinkIssue = () => {
emit('unlinkIssue', linkedIssue.id, linkedIssue.issue.identifier); emit('unlinkIssue', props.linkedIssue.id);
}; };
</script> </script>
@@ -63,7 +63,7 @@ const onSearch = async value => {
isFetching.value = true; isFetching.value = true;
const response = await LinearAPI.searchIssues(value); const response = await LinearAPI.searchIssues(value);
issues.value = response.data.map(issue => ({ issues.value = response.data.map(issue => ({
id: issue.identifier, id: issue.id,
name: `${issue.identifier} ${issue.title}`, name: `${issue.identifier} ${issue.title}`,
icon: 'status', icon: 'status',
iconColor: issue.state.color, iconColor: issue.state.color,
@@ -329,12 +329,26 @@
"BUTTON_TEXT": "Connect Linear workspace" "BUTTON_TEXT": "Connect Linear workspace"
} }
}, },
"NOTION": { "GITHUB": {
"DELETE": { "TITLE": "GitHub Integration",
"TITLE": "Are you sure you want to delete the Notion integration?", "DESCRIPTION": "Connect your GitHub repositories to create issues directly from customer conversations. This integration helps you track bugs, feature requests, and development tasks seamlessly.",
"MESSAGE": "Deleting this integration will remove access to your Notion workspace and stop all related functionality.", "NOT_CONFIGURED": {
"CONFIRM": "Yes, delete", "TITLE": "GitHub Integration Not Configured",
"CANCEL": "Cancel" "DESCRIPTION": "Contact your administrator to configure GitHub OAuth settings."
},
"CONNECT": {
"TITLE": "Connect GitHub",
"DESCRIPTION": "Connect your GitHub account to start creating issues from conversations.",
"BUTTON": "Connect GitHub"
},
"CONNECTED": {
"TITLE": "GitHub Connected",
"DESCRIPTION": "Your GitHub account is successfully connected. You can now create issues from conversations."
},
"DISCONNECT": {
"BUTTON": "Disconnect",
"SUCCESS": "GitHub integration disconnected successfully",
"ERROR": "Failed to disconnect GitHub integration"
} }
} }
}, },
@@ -152,7 +152,7 @@
"ATTRIBUTES": { "ATTRIBUTES": {
"MESSAGE_TYPE": "Tipo da Mensagem", "MESSAGE_TYPE": "Tipo da Mensagem",
"MESSAGE_CONTAINS": "A mensagem contém", "MESSAGE_CONTAINS": "A mensagem contém",
"EMAIL": "E-mail", "EMAIL": "e-mail",
"INBOX": "Caixa de Entrada", "INBOX": "Caixa de Entrada",
"CONVERSATION_LANGUAGE": "Idioma da conversa", "CONVERSATION_LANGUAGE": "Idioma da conversa",
"PHONE_NUMBER": "Número de Telefone", "PHONE_NUMBER": "Número de Telefone",
@@ -130,15 +130,15 @@ export default {
</div> </div>
<div class="flex multiselect-wrap--medium"> <div class="flex multiselect-wrap--medium">
<div <div
class="w-8 relative text-base text-slate-100 dark:text-slate-600 after:content-[''] after:h-12 after:w-0 ltr:after:left-4 rtl:after:right-4 after:absolute after:border-l after:border-solid after:border-slate-100 after:dark:border-slate-600 before:content-[''] before:h-0 before:w-4 ltr:before:left-4 rtl:before:right-4 before:top-12 before:absolute before:border-b before:border-solid before:border-slate-100 before:dark:border-slate-600" class="w-8 relative text-base text-slate-100 dark:text-slate-600 after:content-[''] after:h-12 after:w-0 after:left-4 after:absolute after:border-l after:border-solid after:border-slate-100 after:dark:border-slate-600 before:content-[''] before:h-0 before:w-4 before:left-4 before:top-12 before:absolute before:border-b before:border-solid before:border-slate-100 before:dark:border-slate-600"
> >
<fluent-icon <fluent-icon
icon="arrow-up" icon="arrow-up"
class="absolute -top-1 ltr:left-2 rtl:right-2" class="absolute -top-1 left-2"
size="17" size="17"
/> />
</div> </div>
<div class="flex flex-col w-full ltr:pl-8 rtl:pr-8"> <div class="flex flex-col w-full">
<label class="multiselect__label"> <label class="multiselect__label">
{{ $t('MERGE_CONTACTS.PRIMARY.TITLE') }} {{ $t('MERGE_CONTACTS.PRIMARY.TITLE') }}
<woot-label <woot-label
@@ -71,12 +71,6 @@ export default {
FEATURE_FLAGS.AUTO_RESOLVE_CONVERSATIONS FEATURE_FLAGS.AUTO_RESOLVE_CONVERSATIONS
); );
}, },
showAudioTranscriptionConfig() {
return this.isFeatureEnabledonAccount(
this.accountId,
FEATURE_FLAGS.CAPTAIN
);
},
languagesSortedByCode() { languagesSortedByCode() {
const enabledLanguages = [...this.enabledLanguages]; const enabledLanguages = [...this.enabledLanguages];
return enabledLanguages.sort((l1, l2) => return enabledLanguages.sort((l1, l2) =>
@@ -243,7 +237,7 @@ export default {
<woot-loading-state v-if="uiFlags.isFetchingItem" /> <woot-loading-state v-if="uiFlags.isFetchingItem" />
</div> </div>
<AutoResolve v-if="showAutoResolutionConfig" /> <AutoResolve v-if="showAutoResolutionConfig" />
<AudioTranscription v-if="showAudioTranscriptionConfig" /> <AudioTranscription v-if="isOnChatwootCloud" />
<AccountId /> <AccountId />
<div v-if="!uiFlags.isFetchingItem && isOnChatwootCloud"> <div v-if="!uiFlags.isFetchingItem && isOnChatwootCloud">
<AccountDelete /> <AccountDelete />
@@ -12,6 +12,7 @@ defineOptions({
provider="google" provider="google"
:title="$t('INBOX_MGMT.ADD.GOOGLE.TITLE')" :title="$t('INBOX_MGMT.ADD.GOOGLE.TITLE')"
:description="$t('INBOX_MGMT.ADD.GOOGLE.DESCRIPTION')" :description="$t('INBOX_MGMT.ADD.GOOGLE.DESCRIPTION')"
:input-placeholder="$t('INBOX_MGMT.ADD.GOOGLE.EMAIL_PLACEHOLDER')"
:submit-button-text="$t('INBOX_MGMT.ADD.GOOGLE.SIGN_IN')" :submit-button-text="$t('INBOX_MGMT.ADD.GOOGLE.SIGN_IN')"
:error-message="$t('INBOX_MGMT.ADD.GOOGLE.ERROR_MESSAGE')" :error-message="$t('INBOX_MGMT.ADD.GOOGLE.ERROR_MESSAGE')"
/> />
@@ -12,6 +12,7 @@ defineOptions({
provider="microsoft" provider="microsoft"
:title="$t('INBOX_MGMT.ADD.MICROSOFT.TITLE')" :title="$t('INBOX_MGMT.ADD.MICROSOFT.TITLE')"
:description="$t('INBOX_MGMT.ADD.MICROSOFT.DESCRIPTION')" :description="$t('INBOX_MGMT.ADD.MICROSOFT.DESCRIPTION')"
:input-placeholder="$t('INBOX_MGMT.ADD.MICROSOFT.EMAIL_PLACEHOLDER')"
:submit-button-text="$t('INBOX_MGMT.ADD.MICROSOFT.SIGN_IN')" :submit-button-text="$t('INBOX_MGMT.ADD.MICROSOFT.SIGN_IN')"
:error-message="$t('INBOX_MGMT.ADD.MICROSOFT.ERROR_MESSAGE')" :error-message="$t('INBOX_MGMT.ADD.MICROSOFT.ERROR_MESSAGE')"
/> />
@@ -30,9 +30,14 @@ const props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
inputPlaceholder: {
type: String,
required: true,
},
}); });
const isRequestingAuthorization = ref(false); const isRequestingAuthorization = ref(false);
const email = ref('');
const client = computed(() => { const client = computed(() => {
if (props.provider === 'microsoft') { if (props.provider === 'microsoft') {
@@ -45,7 +50,9 @@ const client = computed(() => {
async function requestAuthorization() { async function requestAuthorization() {
try { try {
isRequestingAuthorization.value = true; isRequestingAuthorization.value = true;
const response = await client.value.generateAuthorization(); const response = await client.value.generateAuthorization({
email: email.value,
});
const { const {
data: { url }, data: { url },
} = response; } = response;
@@ -68,6 +75,11 @@ async function requestAuthorization() {
:header-content="description" :header-content="description"
/> />
<form class="mt-6" @submit.prevent="requestAuthorization"> <form class="mt-6" @submit.prevent="requestAuthorization">
<woot-input
v-model="email"
type="email"
:placeholder="inputPlaceholder"
/>
<NextButton <NextButton
:is-loading="isRequestingAuthorization" :is-loading="isRequestingAuthorization"
type="submit" type="submit"
@@ -5,19 +5,15 @@ import {
useMapGetter, useMapGetter,
useStore, useStore,
} from 'dashboard/composables/store'; } from 'dashboard/composables/store';
import { useI18n } from 'vue-i18n';
import ButtonNext from 'next/button/Button.vue';
import notionClient from 'dashboard/api/notion_auth.js';
import Integration from './Integration.vue'; import Integration from './Integration.vue';
import Spinner from 'shared/components/Spinner.vue'; import Spinner from 'shared/components/Spinner.vue';
const { t } = useI18n();
const store = useStore(); const store = useStore();
const integrationLoaded = ref(false); const integrationLoaded = ref(false);
const integration = useFunctionGetter('integrations/getIntegration', 'notion'); const integration = useFunctionGetter('integrations/getIntegration', 'github');
const uiFlags = useMapGetter('integrations/getUIFlags'); const uiFlags = useMapGetter('integrations/getUIFlags');
@@ -25,32 +21,22 @@ const integrationAction = computed(() => {
if (integration.value.enabled) { if (integration.value.enabled) {
return 'disconnect'; return 'disconnect';
} }
return integration.value.action;
return '';
}); });
const authorize = async () => { const initializeGithubIntegration = async () => {
const response = await notionClient.generateAuthorization(); await store.dispatch('integrations/get', 'github');
const {
data: { url },
} = response;
window.location.href = url;
};
const initializeNotionIntegration = async () => {
await store.dispatch('integrations/get', 'notion');
integrationLoaded.value = true; integrationLoaded.value = true;
}; };
onMounted(() => { onMounted(() => {
initializeNotionIntegration(); initializeGithubIntegration();
}); });
</script> </script>
<template> <template>
<div class="flex-grow flex-shrink p-4 overflow-auto mx-auto"> <div class="flex-grow flex-shrink max-w-6xl p-4 mx-auto overflow-auto">
<div v-if="integrationLoaded && !uiFlags.isCreatingNotion"> <div v-if="integrationLoaded && !uiFlags.isCreatingGithub">
<Integration <Integration
:integration-id="integration.id" :integration-id="integration.id"
:integration-logo="integration.logo" :integration-logo="integration.logo"
@@ -59,19 +45,10 @@ onMounted(() => {
:integration-enabled="integration.enabled" :integration-enabled="integration.enabled"
:integration-action="integrationAction" :integration-action="integrationAction"
:delete-confirmation-text="{ :delete-confirmation-text="{
title: t('INTEGRATION_SETTINGS.NOTION.DELETE.TITLE'), title: $t('INTEGRATION_SETTINGS.GITHUB.DELETE.TITLE'),
message: t('INTEGRATION_SETTINGS.NOTION.DELETE.MESSAGE'), message: $t('INTEGRATION_SETTINGS.GITHUB.DELETE.MESSAGE'),
}" }"
> />
<template #action>
<ButtonNext
faded
blue
:label="t('INTEGRATION_SETTINGS.CONNECT.BUTTON_TEXT')"
@click="authorize"
/>
</template>
</Integration>
</div> </div>
<div v-else class="flex items-center justify-center flex-1"> <div v-else class="flex items-center justify-center flex-1">
<Spinner size="" color-scheme="primary" /> <Spinner size="" color-scheme="primary" />
@@ -8,8 +8,8 @@ import DashboardApps from './DashboardApps/Index.vue';
import Slack from './Slack.vue'; import Slack from './Slack.vue';
import SettingsContent from '../Wrapper.vue'; import SettingsContent from '../Wrapper.vue';
import Linear from './Linear.vue'; import Linear from './Linear.vue';
import Notion from './Notion.vue';
import Shopify from './Shopify.vue'; import Shopify from './Shopify.vue';
import Github from './Github.vue';
export default { export default {
routes: [ routes: [
@@ -91,15 +91,6 @@ export default {
}, },
props: route => ({ code: route.query.code }), props: route => ({ code: route.query.code }),
}, },
{
path: 'notion',
name: 'settings_integrations_notion',
component: Notion,
meta: {
permissions: ['administrator'],
},
props: route => ({ code: route.query.code }),
},
{ {
path: 'shopify', path: 'shopify',
name: 'settings_integrations_shopify', name: 'settings_integrations_shopify',
@@ -110,6 +101,14 @@ export default {
}, },
props: route => ({ error: route.query.error }), props: route => ({ error: route.query.error }),
}, },
{
path: 'github',
name: 'settings_integrations_github',
component: Github,
meta: {
permissions: ['administrator'],
},
},
{ {
path: ':integration_id', path: ':integration_id',
name: 'settings_applications_integration', name: 'settings_applications_integration',
@@ -75,34 +75,10 @@ export default {
onRegistrationSuccess() { onRegistrationSuccess() {
this.hasEnabledPushPermissions = true; this.hasEnabledPushPermissions = true;
}, },
onRequestPermissions(value) { onRequestPermissions() {
if (value) { requestPushPermissions({
// Enable / re-enable push notifications onSuccess: this.onRegistrationSuccess,
requestPushPermissions({ });
onSuccess: this.onRegistrationSuccess,
});
} else {
// Disable push notifications
this.disablePushPermissions();
}
},
disablePushPermissions() {
verifyServiceWorkerExistence(registration =>
registration.pushManager
.getSubscription()
.then(subscription => {
if (subscription) {
return subscription.unsubscribe();
}
return null;
})
.finally(() => {
this.hasEnabledPushPermissions = false;
})
.catch(() => {
// error
})
);
}, },
getPushSubscription() { getPushSubscription() {
verifyServiceWorkerExistence(registration => verifyServiceWorkerExistence(registration =>
@@ -59,7 +59,7 @@ const defaulSpanRender = cellProps =>
cellProps.getValue() cellProps.getValue()
); );
const columns = computed(() => [ const columns = [
columnHelper.accessor('name', { columnHelper.accessor('name', {
header: t(`SUMMARY_REPORTS.${props.type.toUpperCase()}`), header: t(`SUMMARY_REPORTS.${props.type.toUpperCase()}`),
width: 300, width: 300,
@@ -90,7 +90,7 @@ const columns = computed(() => [
width: 200, width: 200,
cell: defaulSpanRender, cell: defaulSpanRender,
}), }),
]); ];
const renderAvgTime = value => (value ? formatTime(value) : '--'); const renderAvgTime = value => (value ? formatTime(value) : '--');
@@ -142,9 +142,7 @@ const table = useVueTable({
get data() { get data() {
return tableData.value; return tableData.value;
}, },
get columns() { columns,
return columns.value;
},
enableSorting: false, enableSorting: false,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
}); });
+1 -1
View File
@@ -35,7 +35,7 @@ class Webhooks::TelegramEventsJob < ApplicationJob
def process_event_params(channel, params) def process_event_params(channel, params)
return unless params[:telegram] return unless params[:telegram]
if params.dig(:telegram, :edited_message).present? || params.dig(:telegram, :edited_business_message).present? if params.dig(:telegram, :edited_message).present?
Telegram::UpdateMessageService.new(inbox: channel.inbox, params: params['telegram'].with_indifferent_access).perform Telegram::UpdateMessageService.new(inbox: channel.inbox, params: params['telegram'].with_indifferent_access).perform
else else
Telegram::IncomingMessageService.new(inbox: channel.inbox, params: params['telegram'].with_indifferent_access).perform Telegram::IncomingMessageService.new(inbox: channel.inbox, params: params['telegram'].with_indifferent_access).perform
+3 -16
View File
@@ -69,10 +69,6 @@ class Channel::Telegram < ApplicationRecord
message.conversation[:additional_attributes]['chat_id'] message.conversation[:additional_attributes]['chat_id']
end end
def business_connection_id(message)
message.conversation[:additional_attributes]['business_connection_id']
end
def reply_to_message_id(message) def reply_to_message_id(message)
message.content_attributes['in_reply_to_external_id'] message.content_attributes['in_reply_to_external_id']
end end
@@ -99,13 +95,7 @@ class Channel::Telegram < ApplicationRecord
end end
def send_message(message) def send_message(message)
response = message_request( response = message_request(chat_id(message), message.outgoing_content, reply_markup(message), reply_to_message_id(message))
chat_id(message),
message.outgoing_content,
reply_markup(message),
reply_to_message_id(message),
business_connection_id: business_connection_id(message)
)
process_error(message, response) process_error(message, response)
response.parsed_response['result']['message_id'] if response.success? response.parsed_response['result']['message_id'] if response.success?
end end
@@ -141,12 +131,9 @@ class Channel::Telegram < ApplicationRecord
stripped_html.gsub('&lt;br&gt;', "\n") stripped_html.gsub('&lt;br&gt;', "\n")
end end
def message_request(chat_id, text, reply_markup = nil, reply_to_message_id = nil, business_connection_id: nil) def message_request(chat_id, text, reply_markup = nil, reply_to_message_id = nil)
text_payload = convert_markdown_to_telegram_html(text) text_payload = convert_markdown_to_telegram_html(text)
business_body = {}
business_body[:business_connection_id] = business_connection_id if business_connection_id
HTTParty.post("#{telegram_api_url}/sendMessage", HTTParty.post("#{telegram_api_url}/sendMessage",
body: { body: {
chat_id: chat_id, chat_id: chat_id,
@@ -154,6 +141,6 @@ class Channel::Telegram < ApplicationRecord
reply_markup: reply_markup, reply_markup: reply_markup,
parse_mode: 'HTML', parse_mode: 'HTML',
reply_to_message_id: reply_to_message_id reply_to_message_id: reply_to_message_id
}.merge(business_body)) })
end end
end end
+18 -2
View File
@@ -43,6 +43,8 @@ class Integrations::App
"#{params[:action]}&client_id=#{client_id}&redirect_uri=#{self.class.slack_integration_url}" "#{params[:action]}&client_id=#{client_id}&redirect_uri=#{self.class.slack_integration_url}"
when 'linear' when 'linear'
build_linear_action build_linear_action
when 'github'
build_github_action
else else
params[:action] params[:action]
end end
@@ -58,8 +60,8 @@ class Integrations::App
account.feature_enabled?('shopify_integration') && GlobalConfigService.load('SHOPIFY_CLIENT_ID', nil).present? account.feature_enabled?('shopify_integration') && GlobalConfigService.load('SHOPIFY_CLIENT_ID', nil).present?
when 'leadsquared' when 'leadsquared'
account.feature_enabled?('crm_integration') account.feature_enabled?('crm_integration')
when 'notion' when 'github'
account.feature_enabled?('notion_integration') && GlobalConfigService.load('NOTION_CLIENT_ID', nil).present? GlobalConfigService.load('GITHUB_CLIENT_ID', nil).present?
else else
true true
end end
@@ -77,6 +79,16 @@ class Integrations::App
].join('&') ].join('&')
end end
def build_github_action
client_id = GlobalConfigService.load('GITHUB_CLIENT_ID', nil)
[
'https://github.com/login/oauth/authorize?response_type=code',
"client_id=#{client_id}",
"redirect_uri=#{self.class.github_integration_url}",
'scope=repo'
].join('&')
end
def enabled?(account) def enabled?(account)
case params[:id] case params[:id]
when 'webhook' when 'webhook'
@@ -100,6 +112,10 @@ class Integrations::App
"#{ENV.fetch('FRONTEND_URL', nil)}/linear/callback" "#{ENV.fetch('FRONTEND_URL', nil)}/linear/callback"
end end
def self.github_integration_url
"#{ENV.fetch('FRONTEND_URL', nil)}/github/callback"
end
class << self class << self
def apps def apps
Hashie::Mash.new(APPS_CONFIG) Hashie::Mash.new(APPS_CONFIG)
-4
View File
@@ -53,10 +53,6 @@ class Integrations::Hook < ApplicationRecord
app_id == 'dialogflow' app_id == 'dialogflow'
end end
def notion?
app_id == 'notion'
end
def disable def disable
update(status: 'disabled') update(status: 'disabled')
end end
-61
View File
@@ -1,61 +0,0 @@
class NotionPagePresenter
def initialize(page_response, blocks_response)
@page_response = page_response
@blocks_response = blocks_response
end
def to_hash
{
'id' => @page_response['id'],
'icon' => @page_response['icon'],
'title' => extract_page_title,
'url' => @page_response['url'],
'created_time' => @page_response['created_time'],
'last_edited_time' => @page_response['last_edited_time'],
'md' => generate_markdown,
'child_pages' => extract_child_pages
}
end
private
def extract_page_title
# Try to get title from properties (for database pages)
if @page_response['properties']
title_property = @page_response['properties'].values.find { |prop| prop['type'] == 'title' }
return title_property['title'].map { |t| t['plain_text'] }.join if title_property && title_property['title']&.any?
end
nil
end
def generate_markdown
title = extract_page_title
content_md = NotionToMarkdown.new.convert(@blocks_response['results'])
title_md = title ? "# #{title}\n\n" : ''
"#{title_md}#{content_md}"
end
def extract_child_pages
extract_child_pages_from_blocks(@blocks_response['results'])
end
def extract_child_pages_from_blocks(blocks)
child_pages = []
blocks.each do |block|
if block['type'] == 'child_page'
child_pages << {
'id' => block['id'],
'title' => block['child_page']['title']
}
end
# Recursively check nested blocks
child_pages.concat(extract_child_pages_from_blocks(block['children'])) if block['has_children'] && block['children']
end
child_pages
end
end
@@ -24,15 +24,6 @@ class Instagram::Messenger::MessageText < Instagram::BaseMessageText
end end
def handle_client_error(error) def handle_client_error(error)
# Handle error code 230: User consent is required to access user profile
# This typically occurs when the connected Instagram account attempts to send a message to a user
# who has never messaged this Instagram account before.
# We can safely ignore this error as per Facebook documentation.
if error.message.include?('230')
Rails.logger.warn error
return
end
Rails.logger.warn("[FacebookUserFetchClientError]: account_id #{@inbox.account_id} inbox_id #{@inbox.id}") Rails.logger.warn("[FacebookUserFetchClientError]: account_id #{@inbox.account_id} inbox_id #{@inbox.id}")
Rails.logger.warn("[FacebookUserFetchClientError]: #{error.message}") Rails.logger.warn("[FacebookUserFetchClientError]: #{error.message}")
ChatwootExceptionTracker.new(error, account: @inbox.account).capture_exception ChatwootExceptionTracker.new(error, account: @inbox.account).capture_exception
@@ -1,41 +0,0 @@
class Linear::ActivityMessageService
attr_reader :conversation, :action_type, :issue_data, :user
def initialize(conversation:, action_type:, user:, issue_data: {})
@conversation = conversation
@action_type = action_type
@issue_data = issue_data
@user = user
end
def perform
return unless conversation && issue_data[:id] && user
content = generate_activity_content
return unless content
::Conversations::ActivityMessageJob.perform_later(conversation, activity_message_params(content))
end
private
def generate_activity_content
case action_type.to_sym
when :issue_created
I18n.t('conversations.activity.linear.issue_created', user_name: user.name, issue_id: issue_data[:id])
when :issue_linked
I18n.t('conversations.activity.linear.issue_linked', user_name: user.name, issue_id: issue_data[:id])
when :issue_unlinked
I18n.t('conversations.activity.linear.issue_unlinked', user_name: user.name, issue_id: issue_data[:id])
end
end
def activity_message_params(content)
{
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: :activity,
content: content
}
end
end
@@ -11,13 +11,6 @@ class LlmFormatter::ConversationLlmFormatter < LlmFormatter::DefaultLlmFormatter
end end
sections << "Contact Details: #{@record.contact.to_llm_text}" if config[:include_contact_details] sections << "Contact Details: #{@record.contact.to_llm_text}" if config[:include_contact_details]
attributes = build_attributes
if attributes.present?
sections << 'Conversation Attributes:'
sections << attributes
end
sections.join("\n") sections.join("\n")
end end
@@ -37,11 +30,4 @@ class LlmFormatter::ConversationLlmFormatter < LlmFormatter::DefaultLlmFormatter
sender = message.message_type == 'incoming' ? 'User' : 'Support agent' sender = message.message_type == 'incoming' ? 'User' : 'Support agent'
"#{sender}: #{message.content}\n" "#{sender}: #{message.content}\n"
end end
def build_attributes
attributes = @record.account.custom_attribute_definitions.with_attribute_model('conversation_attribute').map do |attribute|
"#{attribute.attribute_display_name}: #{@record.custom_attributes[attribute.attribute_key]}"
end
attributes.join("\n")
end
end end
@@ -4,10 +4,8 @@ class MessageTemplates::Template::AutoResolve
def perform def perform
return if conversation.account.auto_resolve_message.blank? return if conversation.account.auto_resolve_message.blank?
if within_messaging_window? ActiveRecord::Base.transaction do
conversation.messages.create!(auto_resolve_message_params) conversation.messages.create!(auto_resolve_message_params)
else
create_auto_resolve_not_sent_activity_message
end end
end end
@@ -16,21 +14,6 @@ class MessageTemplates::Template::AutoResolve
delegate :contact, :account, to: :conversation delegate :contact, :account, to: :conversation
delegate :inbox, to: :message delegate :inbox, to: :message
def within_messaging_window?
conversation.can_reply?
end
def create_auto_resolve_not_sent_activity_message
content = I18n.t('conversations.activity.auto_resolve.not_sent_due_to_messaging_window')
activity_message_params = {
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: :activity,
content: content
}
::Conversations::ActivityMessageJob.perform_later(conversation, activity_message_params) if content
end
def auto_resolve_message_params def auto_resolve_message_params
{ {
account_id: @conversation.account_id, account_id: @conversation.account_id,
-104
View File
@@ -1,104 +0,0 @@
class NotionToMarkdown
# Main entry: pass in the Notion page blocks (array of block hashes)
def convert(blocks)
blocks.map { |block| block_to_markdown(block) }.join("\n\n")
end
private
def block_to_markdown(block, depth = 0)
type = block['type']
return '' unless type
case type
when 'paragraph'
rich_text_to_md(block['paragraph']['rich_text'])
when 'heading_1'
"# #{rich_text_to_md(block['heading_1']['rich_text'])}"
when 'heading_2'
"## #{rich_text_to_md(block['heading_2']['rich_text'])}"
when 'heading_3'
"### #{rich_text_to_md(block['heading_3']['rich_text'])}"
when 'bulleted_list_item'
indent = ' ' * depth
"#{indent}- #{rich_text_to_md(block['bulleted_list_item']['rich_text'])}" +
children_to_md(block, depth + 1)
when 'numbered_list_item'
indent = ' ' * depth
"#{indent}1. #{rich_text_to_md(block['numbered_list_item']['rich_text'])}" +
children_to_md(block, depth + 1)
when 'to_do'
box = block['to_do']['checked'] ? '[x]' : '[ ]'
"- #{box} #{rich_text_to_md(block['to_do']['rich_text'])}"
when 'toggle'
summary = rich_text_to_md(block['toggle']['rich_text'])
details = children_to_md(block, depth + 1)
"<details>\n<summary>#{summary}</summary>\n\n#{details}\n</details>"
when 'quote'
quote = rich_text_to_md(block['quote']['rich_text'])
quote_lines = quote.lines.map { |line| "> #{line}" }.join
quote_lines + children_to_md(block, depth)
when 'code'
lang = block['code']['language'] || ''
code = block['code']['rich_text'].pluck('plain_text').join
"```#{lang}\n#{code}\n```"
when 'callout'
icon = block['callout']['icon']&.dig('emoji') || '💡'
content = rich_text_to_md(block['callout']['rich_text'])
"> [!#{icon}] #{content}" + children_to_md(block, depth + 1)
when 'divider'
'---'
when 'image'
url = block['image']['type'] == 'external' ? block['image']['external']['url'] : block['image']['file']['url']
caption = block['image']['caption']&.pluck('plain_text')&.join || 'image'
"![#{caption}](#{url})"
when 'bookmark'
url = block['bookmark']['url']
"[#{url}](#{url})"
when 'child_page'
"## #{block['child_page']['title']}" + children_to_md(block, depth + 1)
when 'table'
table_to_md(block)
else
'' # Add more block types as needed
end
end
def children_to_md(block, depth)
return '' unless block['has_children'] && block['children']
"\n" + block['children'].map { |child| block_to_markdown(child, depth) }.join("\n")
end
def rich_text_to_md(rich_text_array)
return '' unless rich_text_array
rich_text_array.map { |t| annotate_text(t) }.join
end
def annotate_text(text_obj)
text = text_obj['plain_text']
ann = text_obj['annotations'] || {}
text = "**#{text}**" if ann['bold']
text = "*#{text}*" if ann['italic']
text = "~~#{text}~~" if ann['strikethrough']
text = "<u>#{text}</u>" if ann['underline']
text = "`#{text}`" if ann['code']
text = "[#{text}](#{text_obj['href']})" if text_obj['href']
text
end
def table_to_md(block)
rows = block['children'] || []
table = rows.map do |row|
cells = row['table_row']['cells']
cells.map { |cell| rich_text_to_md(cell) }
end
return '' if table.empty?
header = table.first
sep = header.map { '---' }
([header, sep] + table[1..]).map { |row| "| #{row.join(' | ')} |" }.join("\n")
end
end
@@ -8,25 +8,17 @@ class Telegram::IncomingMessageService
def perform def perform
# chatwoot doesn't support group conversations at the moment # chatwoot doesn't support group conversations at the moment
transform_business_message!
return unless private_message? return unless private_message?
set_contact set_contact
update_contact_avatar update_contact_avatar
set_conversation set_conversation
# TODO: Since the recent Telegram Business update, we need to explicitly mark messages as read using an additional request.
# Otherwise, the client will see their messages as unread.
# Chatwoot defines a 'read' status in its enum but does not currently update this status for Telegram conversations.
# We have two options:
# 1. Send the read request to Telegram here, immediately when the message is created.
# 2. Properly update the read status in the Chatwoot UI and trigger the Telegram request when the agent actually reads the message.
# See: https://core.telegram.org/bots/api#readbusinessmessage
@message = @conversation.messages.build( @message = @conversation.messages.build(
content: telegram_params_message_content, content: telegram_params_message_content,
account_id: @inbox.account_id, account_id: @inbox.account_id,
inbox_id: @inbox.id, inbox_id: @inbox.id,
message_type: message_type, message_type: :incoming,
sender: message_sender, sender: @contact,
content_attributes: telegram_params_content_attributes, content_attributes: telegram_params_content_attributes,
source_id: telegram_params_message_id.to_s source_id: telegram_params_message_id.to_s
) )
@@ -44,11 +36,6 @@ class Telegram::IncomingMessageService
contact_attributes: contact_attributes contact_attributes: contact_attributes
).perform ).perform
# TODO: Should we update contact_attributes when the user changes their first or last name?
# In business chats, when our Telegram bot initiates the conversation,
# the message does not include a language code.
# This is critical for AI assistants and translation plugins.
@contact_inbox = contact_inbox @contact_inbox = contact_inbox
@contact = contact_inbox.contact @contact = contact_inbox.contact
end end
@@ -102,19 +89,10 @@ class Telegram::IncomingMessageService
def conversation_additional_attributes def conversation_additional_attributes
{ {
chat_id: telegram_params_chat_id, chat_id: telegram_params_chat_id
business_connection_id: telegram_params_business_connection_id
} }
end end
def message_type
business_message_outgoing? ? :outgoing : :incoming
end
def message_sender
business_message_outgoing? ? nil : @contact
end
def file_content_type def file_content_type
return :image if image_message? return :image if image_message?
return :audio if audio_message? return :audio if audio_message?
@@ -213,8 +191,4 @@ class Telegram::IncomingMessageService
params[:message][:video].presence || params[:message][:video].presence ||
params[:message][:video_note].presence params[:message][:video_note].presence
end end
def transform_business_message!
params[:message] = params[:business_message] if params[:business_message] && !params[:message]
end
end end
+4 -33
View File
@@ -13,17 +13,6 @@ module Telegram::ParamHelpers
{} {}
end end
def business_message?
telegram_params_business_connection_id.present?
end
# In business bot mode we will receive messages from our telegram.
# This is our messages posted via telegram client.
# Such messages should be outgoing (from us to client)
def business_message_outgoing?
business_message? && telegram_params_base_object[:chat][:id] != telegram_params_base_object[:from][:id]
end
def message_params? def message_params?
params[:message].present? params[:message].present?
end end
@@ -40,34 +29,24 @@ module Telegram::ParamHelpers
end end
end end
def contact_params
if business_message_outgoing?
telegram_params_base_object[:chat]
else
telegram_params_base_object[:from]
end
end
def telegram_params_from_id def telegram_params_from_id
return telegram_params_base_object[:chat][:id] if business_message?
telegram_params_base_object[:from][:id] telegram_params_base_object[:from][:id]
end end
def telegram_params_first_name def telegram_params_first_name
contact_params[:first_name] telegram_params_base_object[:from][:first_name]
end end
def telegram_params_last_name def telegram_params_last_name
contact_params[:last_name] telegram_params_base_object[:from][:last_name]
end end
def telegram_params_username def telegram_params_username
contact_params[:username] telegram_params_base_object[:from][:username]
end end
def telegram_params_language_code def telegram_params_language_code
contact_params[:language_code] telegram_params_base_object[:from][:language_code]
end end
def telegram_params_chat_id def telegram_params_chat_id
@@ -78,14 +57,6 @@ module Telegram::ParamHelpers
end end
end end
def telegram_params_business_connection_id
if callback_query_params?
params[:callback_query][:message][:business_connection_id]
else
telegram_params_base_object[:business_connection_id]
end
end
def telegram_params_message_content def telegram_params_message_content
if callback_query_params? if callback_query_params?
params[:callback_query][:data] params[:callback_query][:data]
@@ -71,7 +71,6 @@ class Telegram::SendAttachmentsService
HTTParty.post("#{channel.telegram_api_url}/sendMediaGroup", HTTParty.post("#{channel.telegram_api_url}/sendMediaGroup",
body: { body: {
chat_id: chat_id, chat_id: chat_id,
**business_connection_body,
media: attachments.map { |hash| hash.except(:attachment) }.to_json, media: attachments.map { |hash| hash.except(:attachment) }.to_json,
reply_to_message_id: reply_to_message_id reply_to_message_id: reply_to_message_id
}) })
@@ -109,7 +108,6 @@ class Telegram::SendAttachmentsService
HTTParty.post("#{channel.telegram_api_url}/sendDocument", HTTParty.post("#{channel.telegram_api_url}/sendDocument",
body: { body: {
chat_id: chat_id, chat_id: chat_id,
**business_connection_body,
document: file, document: file,
reply_to_message_id: reply_to_message_id reply_to_message_id: reply_to_message_id
}, },
@@ -137,14 +135,4 @@ class Telegram::SendAttachmentsService
def channel def channel
@channel ||= message.inbox.channel @channel ||= message.inbox.channel
end end
def business_connection_id
@business_connection_id ||= channel.business_connection_id(message)
end
def business_connection_body
body = {}
body[:business_connection_id] = business_connection_id if business_connection_id
body
end
end end
@@ -5,7 +5,6 @@ class Telegram::UpdateMessageService
pattr_initialize [:inbox!, :params!] pattr_initialize [:inbox!, :params!]
def perform def perform
transform_business_message!
find_contact_inbox find_contact_inbox
find_conversation find_conversation
find_message find_message
@@ -37,8 +36,4 @@ class Telegram::UpdateMessageService
@message.update!(content: edited_message[:caption]) @message.update!(content: edited_message[:caption])
end end
end end
def transform_business_message!
params[:edited_message] = params[:edited_business_message] if params[:edited_business_message].present?
end
end end
@@ -4,9 +4,7 @@
I18n.t('reports.label_csv.label_title'), I18n.t('reports.label_csv.label_title'),
I18n.t('reports.label_csv.conversations_count'), I18n.t('reports.label_csv.conversations_count'),
I18n.t('reports.label_csv.avg_first_response_time'), I18n.t('reports.label_csv.avg_first_response_time'),
I18n.t('reports.label_csv.avg_resolution_time'), I18n.t('reports.label_csv.avg_resolution_time')
I18n.t('reports.label_csv.avg_reply_time'),
I18n.t('reports.label_csv.resolution_count'),
] ]
%> %>
<%= CSVSafe.generate_line headers -%> <%= CSVSafe.generate_line headers -%>
@@ -3,7 +3,7 @@
%> %>
<%= javascript_include_tag "secretField" %> <%= javascript_include_tag "secretField" %>
<div data-secret-text="<%= field.data %>" class="cell-data__secret-field"> <div data-secret-text="<%= field.data %>" class="cell-data__secret-field">
<span data-secret-masked="true"></span> <span data-secret-masked="true">••••••••••</span>
<button onclick="toggleSecretField(event)" data-secret-toggler> <button onclick="toggleSecretField(event)" data-secret-toggler>
<svg width="20" height="20"> <svg width="20" height="20">
<use xlink:href="#eye-show" /> <use xlink:href="#eye-show" />
+1 -1
View File
@@ -4,7 +4,7 @@
<%= javascript_include_tag "secretField" %> <%= javascript_include_tag "secretField" %>
<div data-secret-text="<%= field.data %>" class="cell-data__secret-field"> <div data-secret-text="<%= field.data %>" class="cell-data__secret-field">
<span data-secret-masked="true"></span> <span data-secret-masked="true">••••••••••</span>
<button onclick="toggleSecretField(event)" data-secret-toggler> <button onclick="toggleSecretField(event)" data-secret-toggler>
<svg width="20" height="20"> <svg width="20" height="20">
<use xlink:href="#eye-show" /> <use xlink:href="#eye-show" />
@@ -6,8 +6,8 @@
<strong>Chatwoot Installation:</strong> {{ meta.instance_url }}<br> <strong>Chatwoot Installation:</strong> {{ meta.instance_url }}<br>
<strong>Account ID:</strong> {{ meta.account_id }}<br> <strong>Account ID:</strong> {{ meta.account_id }}<br>
<strong>Account Name:</strong> {{ meta.account_name }}<br> <strong>Account Name:</strong> {{ meta.account_name }}<br>
<strong>Deletion due at:</strong> {{ meta.marked_for_deletion_at }}<br>
<strong>Deleted At:</strong> {{ meta.deleted_at }}<br> <strong>Deleted At:</strong> {{ meta.deleted_at }}<br>
<strong>Marked for Deletion at:</strong> {{ meta.marked_for_deletion_at }}<br>
<strong>Deletion Reason:</strong> {{ meta.deletion_reason }} <strong>Deletion Reason:</strong> {{ meta.deletion_reason }}
</p> </p>
@@ -1,18 +1,18 @@
<header class="sticky top-0 z-50 w-full bg-white shadow-sm dark:bg-slate-900"> <header class="sticky top-0 z-50 w-full bg-white shadow-sm dark:bg-slate-900">
<nav class="hidden sm:flex max-w-5xl px-4 mx-auto md:px-8" aria-label="Top"> <nav class="hidden sm:flex max-w-5xl px-4 mx-auto md:px-8" aria-label="Top">
<div class="flex items-center w-full py-5 overflow-hidden"> <div class="flex items-center w-full py-5 overflow-hidden">
<a href="<%= generate_home_link(@portal.slug, @portal.config['default_locale'] || params[:locale], @theme_from_params, @is_plain_layout_enabled) %>" class="flex items-center min-w-0 h-10"> <a href="<%= generate_home_link(@portal.slug, @portal.config['default_locale'] || params[:locale], @theme_from_params, @is_plain_layout_enabled) %>" class="flex items-center h-10">
<% if @portal.logo.present? %> <% if @portal.logo.present? %>
<img src="<%= url_for(@portal.logo) %>" class="w-auto h-10 ltr:mr-2 rtl:ml-2" draggable="false" /> <img src="<%= url_for(@portal.logo) %>" class="w-auto h-10 ltr:mr-2 rtl:ml-2" />
<% end %> <% end %>
<span class="text-lg font-semibold text-slate-900 truncate dark:text-white <%= @portal.logo.present? ? 'hidden lg:block' : 'hidden sm:block' %>"><%= @portal.name %></span> <span class="text-lg font-semibold text-slate-900 dark:text-white <%= @portal.logo.present? ? 'hidden' : 'hidden sm:block' %>"><%= @portal.name %></span>
</a> </a>
</div> </div>
<%# Go to homepage link section %> <%# Go to homepage link section %>
<div class="flex items-center justify-between gap-2 sm:gap-5"> <div class="flex items-center justify-between gap-2 sm:gap-5">
<% if @portal.homepage_link %> <% if @portal.homepage_link %>
<div class="px-1 py-2 ltr:ml-8 rtl:mr-8 cursor-pointer block"> <div class="hidden px-1 py-2 ltr:ml-8 rtl:mr-8 cursor-pointer md:block">
<div class="flex-grow flex-shrink-0"> <div class="flex-grow flex-shrink-0">
<a id="header-action-button" target="_blank" rel="noopener noreferrer nofollow" href="<%= @portal.homepage_link %>" class="flex flex-row items-center gap-1 text-sm font-medium whitespace-nowrap text-slate-800 dark:text-slate-100 stroke-slate-700 dark:stroke-slate-200"> <a id="header-action-button" target="_blank" rel="noopener noreferrer nofollow" href="<%= @portal.homepage_link %>" class="flex flex-row items-center gap-1 text-sm font-medium whitespace-nowrap text-slate-800 dark:text-slate-100 stroke-slate-700 dark:stroke-slate-200">
<%= render partial: 'icons/redirect' %> <%= render partial: 'icons/redirect' %>
@@ -99,11 +99,11 @@
<nav class="flex sm:hidden max-w-5xl px-4 mx-auto" aria-label="Mobile Top"> <nav class="flex sm:hidden max-w-5xl px-4 mx-auto" aria-label="Mobile Top">
<div class="flex items-center justify-between w-full py-5"> <div class="flex items-center justify-between w-full py-5">
<a href="<%= generate_home_link(@portal.slug, @portal.config['default_locale'] || params[:locale], @theme_from_params, @is_plain_layout_enabled) %>" class="flex items-center min-w-0 h-10 text-lg font-semibold text-slate-900 dark:text-white"> <a href="<%= generate_home_link(@portal.slug, @portal.config['default_locale'] || params[:locale], @theme_from_params, @is_plain_layout_enabled) %>" class="flex items-center h-10 text-lg font-semibold text-slate-900 dark:text-white">
<% if @portal.logo.present? %> <% if @portal.logo.present? %>
<img src="<%= url_for(@portal.logo) %>" class="w-auto h-10 ltr:mr-2 rtl:ml-2" draggable="false" /> <img src="<%= url_for(@portal.logo) %>" class="w-auto h-10 ltr:mr-2 rtl:ml-2" />
<% end %> <% end %>
<span class="text-lg font-semibold text-slate-900 truncate dark:text-white <%= @portal.logo.present? ? 'hidden' : 'sm:hidden block' %>"><%= @portal.name %></span> <span class="text-lg font-semibold text-slate-900 dark:text-white <%= @portal.logo.present? ? 'hidden' : 'sm:hidden block' %>"><%= @portal.name %></span>
</a> </a>
<!-- Mobile Menu Component --> <!-- Mobile Menu Component -->
@@ -2,7 +2,7 @@
<section id="portal-bg" class="w-full bg-white dark:bg-slate-900 shadow-inner"> <section id="portal-bg" class="w-full bg-white dark:bg-slate-900 shadow-inner">
<div id="portal-bg-gradient" class="pt-8 pb-8 md:pt-14 md:pb-6 min-h-[240px] md:min-h-[260px]"> <div id="portal-bg-gradient" class="pt-8 pb-8 md:pt-14 md:pb-6 min-h-[240px] md:min-h-[260px]">
<div class="mx-auto max-w-5xl px-4 md:px-8 flex flex-col items-start"> <div class="mx-auto max-w-5xl px-4 md:px-8 flex flex-col items-start">
<span class="text-sm leading-[24px] font-semibold text-slate-600 dark:text-slate-300 mb-1 <%= @portal.logo.present? ? 'block lg:hidden' : 'hidden' %>"><%= @portal.name %></span> <span class="text-sm leading-[24px] font-semibold text-slate-600 dark:text-slate-300 mb-1 <%= @portal.logo.present? ? 'block' : 'hidden' %>"><%= @portal.name %></span>
<h1 class="text-2xl md:text-4xl text-slate-900 dark:text-white font-semibold leading-normal"> <h1 class="text-2xl md:text-4xl text-slate-900 dark:text-white font-semibold leading-normal">
<%= portal.header_text %> <%= portal.header_text %>
</h1> </h1>
@@ -1,4 +1,3 @@
<% has_multiple_locales = @portal.config["allowed_locales"].length > 1 %>
<input type="checkbox" id="mobile-menu-toggle" class="peer/menu hidden" /> <input type="checkbox" id="mobile-menu-toggle" class="peer/menu hidden" />
@@ -47,12 +46,10 @@
</div> </div>
</div> </div>
<% if has_multiple_locales %> <span class="h-px bg-slate-100/70 dark:bg-slate-800/70 w-full"></span>
<span class="h-px bg-slate-100/70 dark:bg-slate-800/70 w-full"></span>
<% end %>
<!-- Locale Switcher --> <!-- Locale Switcher -->
<% if has_multiple_locales %> <% if @portal.config["allowed_locales"].length > 1 %>
<div id="header-action-button" class="flex flex-col gap-2"> <div id="header-action-button" class="flex flex-col gap-2">
<h3 class="text-base font-medium text-slate-700 dark:text-slate-300 my-2"> <h3 class="text-base font-medium text-slate-700 dark:text-slate-300 my-2">
<%= I18n.t('public_portal.header.language', default: 'Language') %> <%= I18n.t('public_portal.header.language', default: 'Language') %>
@@ -156,14 +156,12 @@
<path d="M 8 3 C 5.243 3 3 5.243 3 8 L 3 16 C 3 18.757 5.243 21 8 21 L 16 21 C 18.757 21 21 18.757 21 16 L 21 8 C 21 5.243 18.757 3 16 3 L 8 3 z M 8 5 L 16 5 C 17.654 5 19 6.346 19 8 L 19 16 C 19 17.654 17.654 19 16 19 L 8 19 C 6.346 19 5 17.654 5 16 L 5 8 C 5 6.346 6.346 5 8 5 z M 17 6 A 1 1 0 0 0 16 7 A 1 1 0 0 0 17 8 A 1 1 0 0 0 18 7 A 1 1 0 0 0 17 6 z M 12 7 C 9.243 7 7 9.243 7 12 C 7 14.757 9.243 17 12 17 C 14.757 17 17 14.757 17 12 C 17 9.243 14.757 7 12 7 z M 12 9 C 13.654 9 15 10.346 15 12 C 15 13.654 13.654 15 12 15 C 10.346 15 9 13.654 9 12 C 9 10.346 10.346 9 12 9 z"/> <path d="M 8 3 C 5.243 3 3 5.243 3 8 L 3 16 C 3 18.757 5.243 21 8 21 L 16 21 C 18.757 21 21 18.757 21 16 L 21 8 C 21 5.243 18.757 3 16 3 L 8 3 z M 8 5 L 16 5 C 17.654 5 19 6.346 19 8 L 19 16 C 19 17.654 17.654 19 16 19 L 8 19 C 6.346 19 5 17.654 5 16 L 5 8 C 5 6.346 6.346 5 8 5 z M 17 6 A 1 1 0 0 0 16 7 A 1 1 0 0 0 17 8 A 1 1 0 0 0 18 7 A 1 1 0 0 0 17 6 z M 12 7 C 9.243 7 7 9.243 7 12 C 7 14.757 9.243 17 12 17 C 14.757 17 17 14.757 17 12 C 17 9.243 14.757 7 12 7 z M 12 9 C 13.654 9 15 10.346 15 12 C 15 13.654 13.654 15 12 15 C 10.346 15 9 13.654 9 12 C 9 10.346 10.346 9 12 9 z"/>
</symbol> </symbol>
<symbol id="icon-notion" viewBox="0 0 24 24">
<path fill="currentColor" d="M6.104 5.91c.584.474.802.438 1.898.365l10.332-.62c.22 0 .037-.22-.036-.256l-1.716-1.24c-.329-.255-.767-.548-1.606-.475l-10.005.73c-.364.036-.437.219-.292.365zm.62 2.408v10.87c0 .585.292.803.95.767l11.354-.657c.657-.036.73-.438.73-.913V7.588c0-.474-.182-.73-.584-.693l-11.866.693c-.438.036-.584.255-.584.73m11.21.583c.072.328 0 .657-.33.694l-.547.109v8.025c-.475.256-.913.401-1.278.401c-.584 0-.73-.182-1.168-.729l-3.579-5.618v5.436l1.133.255s0 .656-.914.656l-2.519.146c-.073-.146 0-.51.256-.583l.657-.182v-7.187l-.913-.073c-.073-.329.11-.803.621-.84l2.702-.182l3.724 5.692V9.886l-.95-.109c-.072-.402.22-.693.585-.73zM4.131 3.429l10.406-.766c1.277-.11 1.606-.036 2.41.547l3.321 2.335c.548.401.731.51.731.948v12.805c0 .803-.292 1.277-1.314 1.35l-12.085.73c-.767.036-1.132-.073-1.534-.584L3.62 17.62c-.438-.584-.62-1.021-.62-1.533V4.705c0-.656.292-1.203 1.132-1.276"/>
</symbol>
<symbol id="icon-shopify" viewBox="0 0 32 32"> <symbol id="icon-shopify" viewBox="0 0 32 32">
<path fill="currentColor" d="m20.448 31.974l9.625-2.083s-3.474-23.484-3.5-23.641s-.156-.255-.281-.255c-.13 0-2.573-.182-2.573-.182s-1.703-1.698-1.922-1.88a.4.4 0 0 0-.161-.099l-1.219 28.141zm-4.833-16.901s-1.083-.563-2.365-.563c-1.932 0-2.005 1.203-2.005 1.521c0 1.641 4.318 2.286 4.318 6.172c0 3.057-1.922 5.01-4.542 5.01c-3.141 0-4.719-1.953-4.719-1.953l.859-2.781s1.661 1.422 3.042 1.422c.901 0 1.302-.724 1.302-1.245c0-2.156-3.542-2.255-3.542-5.807c-.047-2.984 2.094-5.891 6.438-5.891c1.677 0 2.5.479 2.5.479l-1.26 3.625zm-.719-13.969c.177 0 .359.052.536.182c-1.313.62-2.75 2.188-3.344 5.323a76 76 0 0 1-2.516.771c.688-2.38 2.359-6.26 5.323-6.26zm1.646 3.932v.182c-1.005.307-2.115.646-3.193.979c.62-2.37 1.776-3.526 2.781-3.958c.255.667.411 1.568.411 2.797zm.718-2.973c.922.094 1.521 1.151 1.901 2.339c-.464.151-.979.307-1.542.484v-.333c0-1.005-.13-1.828-.359-2.495zm3.99 1.718c-.031 0-.083.026-.104.026c-.026 0-.385.099-.953.281C19.63 2.442 18.625.927 16.849.927h-.156C16.183.281 15.558 0 15.021 0c-4.141 0-6.12 5.172-6.74 7.797c-1.594.484-2.75.844-2.88.896c-.901.286-.927.313-1.031 1.161c-.099.615-2.438 18.75-2.438 18.75L20.01 32z"/> <path fill="currentColor" d="m20.448 31.974l9.625-2.083s-3.474-23.484-3.5-23.641s-.156-.255-.281-.255c-.13 0-2.573-.182-2.573-.182s-1.703-1.698-1.922-1.88a.4.4 0 0 0-.161-.099l-1.219 28.141zm-4.833-16.901s-1.083-.563-2.365-.563c-1.932 0-2.005 1.203-2.005 1.521c0 1.641 4.318 2.286 4.318 6.172c0 3.057-1.922 5.01-4.542 5.01c-3.141 0-4.719-1.953-4.719-1.953l.859-2.781s1.661 1.422 3.042 1.422c.901 0 1.302-.724 1.302-1.245c0-2.156-3.542-2.255-3.542-5.807c-.047-2.984 2.094-5.891 6.438-5.891c1.677 0 2.5.479 2.5.479l-1.26 3.625zm-.719-13.969c.177 0 .359.052.536.182c-1.313.62-2.75 2.188-3.344 5.323a76 76 0 0 1-2.516.771c.688-2.38 2.359-6.26 5.323-6.26zm1.646 3.932v.182c-1.005.307-2.115.646-3.193.979c.62-2.37 1.776-3.526 2.781-3.958c.255.667.411 1.568.411 2.797zm.718-2.973c.922.094 1.521 1.151 1.901 2.339c-.464.151-.979.307-1.542.484v-.333c0-1.005-.13-1.828-.359-2.495zm3.99 1.718c-.031 0-.083.026-.104.026c-.026 0-.385.099-.953.281C19.63 2.442 18.625.927 16.849.927h-.156C16.183.281 15.558 0 15.021 0c-4.141 0-6.12 5.172-6.74 7.797c-1.594.484-2.75.844-2.88.896c-.901.286-.927.313-1.031 1.161c-.099.615-2.438 18.75-2.438 18.75L20.01 32z"/>
</symbol> </symbol>
<symbol id="icon-github" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</symbol>
<symbol id="icon-slack" viewBox="0 0 24 24"> <symbol id="icon-slack" viewBox="0 0 24 24">
<path fill="currentColor" d="M6.527 14.514A1.973 1.973 0 0 1 4.56 16.48a1.973 1.973 0 0 1-1.968-1.967c0-1.083.885-1.968 1.968-1.968h1.967zm.992 0c0-1.083.885-1.968 1.968-1.968s1.967.885 1.967 1.968v4.927a1.973 1.973 0 0 1-1.967 1.968a1.973 1.973 0 0 1-1.968-1.968zm1.968-7.987A1.973 1.973 0 0 1 7.519 4.56c0-1.083.885-1.967 1.968-1.967s1.967.884 1.967 1.967v1.968zm0 .992c1.083 0 1.967.884 1.967 1.967a1.973 1.973 0 0 1-1.967 1.968H4.56a1.973 1.973 0 0 1-1.968-1.968c0-1.083.885-1.967 1.968-1.967zm7.986 1.967c0-1.083.885-1.967 1.968-1.967s1.968.884 1.968 1.967a1.973 1.973 0 0 1-1.968 1.968h-1.968zm-.991 0a1.973 1.973 0 0 1-1.968 1.968a1.973 1.973 0 0 1-1.968-1.968V4.56c0-1.083.885-1.967 1.968-1.967s1.968.884 1.968 1.967zm-1.968 7.987c1.083 0 1.968.885 1.968 1.968a1.973 1.973 0 0 1-1.968 1.968a1.973 1.973 0 0 1-1.968-1.968v-1.968zm0-.992a1.973 1.973 0 0 1-1.968-1.967c0-1.083.885-1.968 1.968-1.968h4.927c1.083 0 1.968.885 1.968 1.968a1.973 1.973 0 0 1-1.968 1.967z"/> <path fill="currentColor" d="M6.527 14.514A1.973 1.973 0 0 1 4.56 16.48a1.973 1.973 0 0 1-1.968-1.967c0-1.083.885-1.968 1.968-1.968h1.967zm.992 0c0-1.083.885-1.968 1.968-1.968s1.967.885 1.967 1.968v4.927a1.973 1.973 0 0 1-1.967 1.968a1.973 1.973 0 0 1-1.968-1.968zm1.968-7.987A1.973 1.973 0 0 1 7.519 4.56c0-1.083.885-1.967 1.968-1.967s1.967.884 1.967 1.967v1.968zm0 .992c1.083 0 1.967.884 1.967 1.967a1.973 1.973 0 0 1-1.967 1.968H4.56a1.973 1.973 0 0 1-1.968-1.968c0-1.083.885-1.967 1.968-1.967zm7.986 1.967c0-1.083.885-1.967 1.968-1.967s1.968.884 1.968 1.967a1.973 1.973 0 0 1-1.968 1.968h-1.968zm-.991 0a1.973 1.973 0 0 1-1.968 1.968a1.973 1.973 0 0 1-1.968-1.968V4.56c0-1.083.885-1.967 1.968-1.967s1.968.884 1.968 1.967zm-1.968 7.987c1.083 0 1.968.885 1.968 1.968a1.973 1.973 0 0 1-1.968 1.968a1.973 1.973 0 0 1-1.968-1.968v-1.968zm0-.992a1.973 1.973 0 0 1-1.968-1.967c0-1.083.885-1.968 1.968-1.968h4.927c1.083 0 1.968.885 1.968 1.968a1.973 1.973 0 0 1-1.968 1.967z"/>
</symbol> </symbol>

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

+20 -29
View File
@@ -22,36 +22,27 @@
<% end %> <% end %>
<div class="bg-white py-2 px-4 xl:px-0"> <div class="bg-white py-2 px-4 xl:px-0">
<%= javascript_include_tag "secretField" %> <div class="mb-4">
<div class="flex items-center mb-6"> <div class="flex items-center gap-2">
<h4 class="text-sm font-medium leading-5 h-5 text-n-slate-12 mr-4">Installation Identifier</h4> <h2 class="h-5 leading-5 text-n-slate-12 font-medium">Current plan</h2>
<div data-secret-text="<%= ChatwootHub.installation_identifier %>" class="cell-data__secret-field text-sm leading-5 h-5 text-n-slate-11"> <a href="<%= refresh_super_admin_settings_url %>" class="inline-flex gap-1 text-xs font-medium items-center text-woot-500 hover:text-woot-700">
<span data-secret-masked="true" class="select-none"></span> <svg width="16" height="16"><use xlink:href="#icon-refresh-line" /></svg>
<button type="button" onclick="toggleSecretField(event)" data-secret-toggler class="inline-flex items-center justify-center h-5 w-5 p-0 rounded text-n-slate-11 hover:text-n-slate-12 transition-colors focus:outline-none"> <span>Refresh</span>
<svg width="20" height="20" class="w-5 h-5 fill-current"> </a>
<use xlink:href="#eye-show" /> </div>
</svg> <p class="text-n-slate-11 mt-1"><%= SuperAdmin::FeaturesHelper.plan_details.html_safe %></p>
</button> <div class="flex items-center mt-6">
<button type="button" onclick="copySecretField(event)" data-secret-copier class="inline-flex items-center justify-center h-5 w-5 p-0 rounded text-n-slate-11 hover:text-n-slate-12 transition-colors focus:outline-none"> <h4 class="text-sm font-medium leading-5 h-5 text-n-slate-12 mr-4">Installation Identifier</h4>
<svg width="20" height="20" class="w-5 h-5 fill-current"> <span class="text-sm leading-5 h-5 text-n-slate-11"><%= ChatwootHub.installation_identifier %></span>
<use xlink:href="#icon-copy" />
</svg>
</button>
</div> </div>
</div> </div>
<div class="flex p-4 outline outline-1 outline-n-container rounded-lg items-start md:items-center shadow-sm flex-col md:flex-row"> <div class="flex p-4 outline outline-1 outline-n-container rounded-lg mt-8 items-start md:items-center shadow-sm flex-col md:flex-row">
<div class="flex flex-col flex-grow gap-1"> <div class="flex flex-col flex-grow gap-1">
<div class="flex items-center gap-2"> <h2 class="h-5 leading-5 text-n-slate-12 text-sm font-medium">Current plan</h2>
<h2 class="h-5 leading-5 text-n-slate-12 text-sm font-medium">Current plan</h2>
<a href="<%= refresh_super_admin_settings_url %>" class="inline-flex gap-1 text-xs font-medium items-center text-woot-500 hover:text-woot-700">
<svg width="16" height="16"><use xlink:href="#icon-refresh-line" /></svg>
<span>Refresh</span>
</a>
</div>
<p class="text-n-slate-11 m-0 text-sm"><%= SuperAdmin::FeaturesHelper.plan_details.html_safe %></p> <p class="text-n-slate-11 m-0 text-sm"><%= SuperAdmin::FeaturesHelper.plan_details.html_safe %></p>
</div> </div>
<a href="<%= ChatwootHub.billing_url %>" target="_blank" rel="noopener noreferrer"> <a href="<%= ChatwootHub.billing_url %>" target="_blank" rel="noopener noreferrer">
<button class="mt-4 md:mt-0 flex gap-1 items-center bg-transparent shadow-sm h-9 hover:text-n-slate-12 hover:bg-slate-50 outline outline-1 outline-n-container rounded text-n-slate-11 font-medium p-2 focus:outline-none"> <button class="mt-4 md:mt-0 flex gap-1 items-center bg-transparent shadow-sm h-9 hover:text-n-slate-12 hover:bg-slate-50 outline outline-1 outline-n-container rounded text-n-slate-11 font-medium p-2">
<svg width="16" height="16"><use xlink:href="#icon-settings-2-line" /></svg> <svg width="16" height="16"><use xlink:href="#icon-settings-2-line" /></svg>
<span class="px-1">Manage</span> <span class="px-1">Manage</span>
</button> </button>
@@ -73,14 +64,14 @@
<p class="text-n-slate-11 m-0 text-sm">Do you face any issues? We are here to help.</p> <p class="text-n-slate-11 m-0 text-sm">Do you face any issues? We are here to help.</p>
</div> </div>
<a href="https://discord.gg/cJXdrwS" target="_blank"> <a href="https://discord.gg/cJXdrwS" target="_blank">
<button class="flex mt-4 md:mt-0 gap-1 items-center bg-transparent shadow-sm h-9 bg-violet-500 hover:bg-violet-600 text-white border border-solid border-violet-600 rounded font-medium p-2 focus:outline-none"> <button class="flex mt-4 md:mt-0 gap-1 items-center bg-transparent shadow-sm h-9 bg-violet-500 hover:bg-violet-600 text-white border border-solid border-violet-600 rounded font-medium p-2">
<svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.209 3.103c-.495-1.004-1.926-1.004-2.421 0L8.43 7.88l-5.273.766c-1.107.161-1.55 1.522-.748 2.303l3.815 3.72-.9 5.25c-.19 1.103.968 1.944 1.959 1.424l4.715-2.48 4.716 2.48c.99.52 2.148-.32 1.96-1.424l-.902-5.25 3.816-3.72c.8-.78.359-2.142-.748-2.303l-5.273-.766-2.358-4.777ZM9.74 8.615l2.258-4.576 2.259 4.576a1.35 1.35 0 0 0 1.016.738l5.05.734-3.654 3.562a1.35 1.35 0 0 0-.388 1.195l.862 5.03-4.516-2.375a1.35 1.35 0 0 0-1.257 0l-4.516 2.374.862-5.029a1.35 1.35 0 0 0-.388-1.195l-3.654-3.562 5.05-.734c.44-.063.82-.34 1.016-.738ZM1.164 3.782a.75.75 0 0 0 .118 1.054l2.5 2a.75.75 0 1 0 .937-1.172l-2.5-2a.75.75 0 0 0-1.055.118Z" fill="currentColor"/><path d="M22.836 18.218a.75.75 0 0 0-.117-1.054l-2.5-2a.75.75 0 0 0-.938 1.172l2.5 2a.75.75 0 0 0 1.055-.117ZM1.282 17.164a.75.75 0 1 0 .937 1.172l2.5-2a.75.75 0 0 0-.937-1.172l-2.5 2ZM22.836 3.782a.75.75 0 0 1-.117 1.054l-2.5 2a.75.75 0 0 1-.938-1.172l2.5-2a.75.75 0 0 1 1.055.118Z" fill="currentColor"/></svg> <svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10a9.96 9.96 0 0 1-4.587-1.112l-3.826 1.067a1.25 1.25 0 0 1-1.54-1.54l1.068-3.823A9.96 9.96 0 0 1 2 12C2 6.477 6.477 2 12 2Zm0 1.5A8.5 8.5 0 0 0 3.5 12c0 1.47.373 2.883 1.073 4.137l.15.27-1.112 3.984 3.987-1.112.27.15A8.5 8.5 0 1 0 12 3.5ZM8.75 13h4.498a.75.75 0 0 1 .102 1.493l-.102.007H8.75a.75.75 0 0 1-.102-1.493L8.75 13h4.498H8.75Zm0-3.5h6.505a.75.75 0 0 1 .101 1.493l-.101.007H8.75a.75.75 0 0 1-.102-1.493L8.75 9.5h6.505H8.75Z" fill="currentColor"/></svg>
<span class="px-1">Community Support</span> <span class="px-1">Community Support</span>
</button> </button>
</a> </a>
<% if ChatwootHub.pricing_plan !='community' %> <% if ChatwootHub.pricing_plan !='community' %>
<button class="ml-4 flex gap-1 items-center bg-transparent h-9 hover:text-n-slate-12 hover:bg-slate-50 border border-solid border-slate-100 rounded text-n-slate-11 font-medium p-2 focus:outline-none" onclick="window.$chatwoot.toggle('open')"> <button class="ml-4 flex gap-1 items-center bg-transparent h-9 hover:text-n-slate-12 hover:bg-slate-50 border border-solid border-slate-100 rounded text-n-slate-11 font-medium p-2" onclick="window.$chatwoot.toggle('open')">
<svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.209 3.103c-.495-1.004-1.926-1.004-2.421 0L8.43 7.88l-5.273.766c-1.107.161-1.55 1.522-.748 2.303l3.815 3.72-.9 5.25c-.19 1.103.968 1.944 1.959 1.424l4.715-2.48 4.716 2.48c.99.52 2.148-.32 1.96-1.424l-.902-5.25 3.816-3.72c.8-.78.359-2.142-.748-2.303l-5.273-.766-2.358-4.777ZM9.74 8.615l2.258-4.576 2.259 4.576a1.35 1.35 0 0 0 1.016.738l5.05.734-3.654 3.562a1.35 1.35 0 0 0-.388 1.195l.862 5.03-4.516-2.375a1.35 1.35 0 0 0-1.257 0l-4.516 2.374.862-5.029a1.35 1.35 0 0 0-.388-1.195l-3.654-3.562 5.05-.734c.44-.063.82-.34 1.016-.738ZM1.164 3.782a.75.75 0 0 0 .118 1.054l2.5 2a.75.75 0 1 0 .937-1.172l-2.5-2a.75.75 0 0 0-1.055.118Z" fill="currentColor"/><path d="M22.836 18.218a.75.75 0 0 0-.117-1.054l-2.5-2a.75.75 0 0 0-.938 1.172l2.5 2a.75.75 0 0 0 1.055-.117ZM1.282 17.164a.75.75 0 1 0 .937 1.172l2.5-2a.75.75 0 0 0-.937-1.172l-2.5 2ZM22.836 3.782a.75.75 0 0 1-.117 1.054l-2.5 2a.75.75 0 0 1-.938-1.172l2.5-2a.75.75 0 0 1 1.055.118Z" fill="currentColor"/></svg> <svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10a9.96 9.96 0 0 1-4.587-1.112l-3.826 1.067a1.25 1.25 0 0 1-1.54-1.54l1.068-3.823A9.96 9.96 0 0 1 2 12C2 6.477 6.477 2 12 2Zm0 1.5A8.5 8.5 0 0 0 3.5 12c0 1.47.373 2.883 1.073 4.137l.15.27-1.112 3.984 3.987-1.112.27.15A8.5 8.5 0 1 0 12 3.5ZM8.75 13h4.498a.75.75 0 0 1 .102 1.493l-.102.007H8.75a.75.75 0 0 1-.102-1.493L8.75 13h4.498H8.75Zm0-3.5h6.505a.75.75 0 0 1 .101 1.493l-.101.007H8.75a.75.75 0 0 1-.102-1.493L8.75 9.5h6.505H8.75Z" fill="currentColor"/></svg>
<span class="px-1">Chat Support</span> <span class="px-1">Chat Support</span>
</button> </button>
<% end %> <% end %>
@@ -99,7 +90,7 @@
</div> </div>
<% if !attrs[:enabled] %> <% if !attrs[:enabled] %>
<div class="flex h-9 absolute top-5 items-center invisible group-hover:visible"> <div class="flex h-9 absolute top-5 items-center invisible group-hover:visible">
<a href="<%= ChatwootHub.billing_url %>" target="_blank" rel="noopener noreferrer" class="flex gap-1 items-center bg-slate-100 h-9 hover:bg-slate-300 hover:text-n-slate-12 border border-solid border-slate-100 rounded text-n-slate-11 font-medium p-2 focus:outline-none"> <a href="<%= ChatwootHub.billing_url %>" target="_blank" rel="noopener noreferrer" class="flex gap-1 items-center bg-slate-100 h-9 hover:bg-slate-300 hover:text-n-slate-12 border border-solid border-slate-100 rounded text-n-slate-11 font-medium p-2">
<svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.209 3.103c-.495-1.004-1.926-1.004-2.421 0L8.43 7.88l-5.273.766c-1.107.161-1.55 1.522-.748 2.303l3.815 3.72-.9 5.25c-.19 1.103.968 1.944 1.959 1.424l4.715-2.48 4.716 2.48c.99.52 2.148-.32 1.96-1.424l-.902-5.25 3.816-3.72c.8-.78.359-2.142-.748-2.303l-5.273-.766-2.358-4.777ZM9.74 8.615l2.258-4.576 2.259 4.576a1.35 1.35 0 0 0 1.016.738l5.05.734-3.654 3.562a1.35 1.35 0 0 0-.388 1.195l.862 5.03-4.516-2.375a1.35 1.35 0 0 0-1.257 0l-4.516 2.374.862-5.029a1.35 1.35 0 0 0-.388-1.195l-3.654-3.562 5.05-.734c.44-.063.82-.34 1.016-.738ZM1.164 3.782a.75.75 0 0 0 .118 1.054l2.5 2a.75.75 0 1 0 .937-1.172l-2.5-2a.75.75 0 0 0-1.055.118Z" fill="currentColor"/><path d="M22.836 18.218a.75.75 0 0 0-.117-1.054l-2.5-2a.75.75 0 0 0-.938 1.172l2.5 2a.75.75 0 0 0 1.055-.117ZM1.282 17.164a.75.75 0 1 0 .937 1.172l2.5-2a.75.75 0 0 0-.937-1.172l-2.5 2ZM22.836 3.782a.75.75 0 0 1-.117 1.054l-2.5 2a.75.75 0 0 1-.938-1.172l2.5-2a.75.75 0 0 1 1.055.118Z" fill="currentColor"/></svg> <svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.209 3.103c-.495-1.004-1.926-1.004-2.421 0L8.43 7.88l-5.273.766c-1.107.161-1.55 1.522-.748 2.303l3.815 3.72-.9 5.25c-.19 1.103.968 1.944 1.959 1.424l4.715-2.48 4.716 2.48c.99.52 2.148-.32 1.96-1.424l-.902-5.25 3.816-3.72c.8-.78.359-2.142-.748-2.303l-5.273-.766-2.358-4.777ZM9.74 8.615l2.258-4.576 2.259 4.576a1.35 1.35 0 0 0 1.016.738l5.05.734-3.654 3.562a1.35 1.35 0 0 0-.388 1.195l.862 5.03-4.516-2.375a1.35 1.35 0 0 0-1.257 0l-4.516 2.374.862-5.029a1.35 1.35 0 0 0-.388-1.195l-3.654-3.562 5.05-.734c.44-.063.82-.34 1.016-.738ZM1.164 3.782a.75.75 0 0 0 .118 1.054l2.5 2a.75.75 0 1 0 .937-1.172l-2.5-2a.75.75 0 0 0-1.055.118Z" fill="currentColor"/><path d="M22.836 18.218a.75.75 0 0 0-.117-1.054l-2.5-2a.75.75 0 0 0-.938 1.172l2.5 2a.75.75 0 0 0 1.055-.117ZM1.282 17.164a.75.75 0 1 0 .937 1.172l2.5-2a.75.75 0 0 0-.937-1.172l-2.5 2ZM22.836 3.782a.75.75 0 0 1-.117 1.054l-2.5 2a.75.75 0 0 1-.938-1.172l2.5-2a.75.75 0 0 1 1.055.118Z" fill="currentColor"/></svg>
<span class="px-1">Upgrade now</span> <span class="px-1">Upgrade now</span>
</a> </a>
+1 -1
View File
@@ -1,5 +1,5 @@
shared: &shared shared: &shared
version: '4.3.0' version: '4.2.0'
development: development:
<<: *shared <<: *shared
+3 -3
View File
@@ -169,6 +169,6 @@
- name: crm_integration - name: crm_integration
display_name: CRM Integration display_name: CRM Integration
enabled: false enabled: false
- name: notion_integration - name: github_integration
display_name: Notion Integration display_name: Github Integration
enabled: false enabled: false
+14 -19
View File
@@ -288,25 +288,6 @@
type: secret type: secret
## ------ End of Configs added for Linear ------ ## ## ------ End of Configs added for Linear ------ ##
## ------ Configs added for Notion ------ ##
- name: NOTION_CLIENT_ID
display_title: 'Notion Client ID'
value:
locked: false
description: 'Notion client ID'
- name: NOTION_CLIENT_SECRET
display_title: 'Notion Client Secret'
value:
locked: false
description: 'Notion client secret'
type: secret
- name: NOTION_VERSION
display_title: 'Notion Version'
value: '2022-06-28'
locked: false
description: 'Notion version'
## ------ End of Configs added for Notion ------ ##
## ------ Configs added for Slack ------ ## ## ------ Configs added for Slack ------ ##
- name: SLACK_CLIENT_ID - name: SLACK_CLIENT_ID
display_title: 'Slack Client ID' display_title: 'Slack Client ID'
@@ -360,3 +341,17 @@
value: 'v22.0' value: 'v22.0'
locked: true locked: true
# ------- End of Instagram Channel Related Config ------- # # ------- End of Instagram Channel Related Config ------- #
## ------ Configs added for GitHub ------ ##
- name: GITHUB_CLIENT_ID
display_title: 'GitHub Client ID'
value:
locked: false
description: 'GitHub OAuth app client ID'
- name: GITHUB_CLIENT_SECRET
display_title: 'GitHub Client Secret'
value:
locked: false
description: 'GitHub OAuth app client secret'
type: secret
## ------ End of Configs added for GitHub ------ ##
+7 -6
View File
@@ -63,12 +63,6 @@ linear:
action: https://linear.app/oauth/authorize action: https://linear.app/oauth/authorize
hook_type: account hook_type: account
allow_multiple_hooks: false allow_multiple_hooks: false
notion:
id: notion
logo: notion.png
i18n_key: notion
hook_type: account
allow_multiple_hooks: false
slack: slack:
id: slack id: slack
logo: slack.png logo: slack.png
@@ -284,3 +278,10 @@ leadsquared:
'conversation_activity_score', 'conversation_activity_score',
'transcript_activity_score', 'transcript_activity_score',
] ]
github:
id: github
logo: github.png
i18n_key: github
hook_type: account
allow_multiple_hooks: false
-8
View File
@@ -91,8 +91,6 @@ am:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ am:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ am:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ ar:
conversations_count: عدد المحادثات conversations_count: عدد المحادثات
avg_first_response_time: متوسط وقت الرد الأول avg_first_response_time: متوسط وقت الرد الأول
avg_resolution_time: متوسط وقت الحل avg_resolution_time: متوسط وقت الحل
avg_reply_time: Avg reply time
resolution_count: عدد مرات الإغلاق
team_csv: team_csv:
team_name: اسم الفريق team_name: اسم الفريق
conversations_count: عدد المحادثات conversations_count: عدد المحادثات
@@ -140,8 +138,6 @@ ar:
instagram_story_content: 'أشار %{story_sender} إليك في القصة: ' instagram_story_content: 'أشار %{story_sender} إليك في القصة: '
instagram_deleted_story_content: هذه القصة لم تعد متاحة. instagram_deleted_story_content: هذه القصة لم تعد متاحة.
deleted: تم حذف هذه الرسالة deleted: تم حذف هذه الرسالة
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'رمز الخطأ: %{error_code}' error_code: 'رمز الخطأ: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ ar:
sla: sla:
added: '%{user_name} أضاف سياسة مستوى الخدمة %{sla_name}' added: '%{user_name} أضاف سياسة مستوى الخدمة %{sla_name}'
removed: '%{user_name} أزال سياسة مستوى الخدمة %{sla_name}' removed: '%{user_name} أزال سياسة مستوى الخدمة %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} كتم صوت المحادثة' muted: '%{user_name} كتم صوت المحادثة'
-8
View File
@@ -91,8 +91,6 @@ az:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ az:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ az:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ bg:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ bg:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ bg:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ ca:
conversations_count: Nre. de converses conversations_count: Nre. de converses
avg_first_response_time: Temps mitjà de primera resposta avg_first_response_time: Temps mitjà de primera resposta
avg_resolution_time: Temps mitjà de resolució avg_resolution_time: Temps mitjà de resolució
avg_reply_time: Avg reply time
resolution_count: Total de resolucions
team_csv: team_csv:
team_name: Nom de l'equip team_name: Nom de l'equip
conversations_count: Recompte de converses conversations_count: Recompte de converses
@@ -140,8 +138,6 @@ ca:
instagram_story_content: '%{story_sender} t''ha mencionat a la història: ' instagram_story_content: '%{story_sender} t''ha mencionat a la història: '
instagram_deleted_story_content: Aquesta història ja no està disponible. instagram_deleted_story_content: Aquesta història ja no està disponible.
deleted: Aquest missatge a sigut eliminat deleted: Aquest missatge a sigut eliminat
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Codi d''error: %{error_code}' error_code: 'Codi d''error: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ ca:
sla: sla:
added: '%{user_name} ha afegit la política de SLA %{sla_name}' added: '%{user_name} ha afegit la política de SLA %{sla_name}'
removed: '%{user_name} ha eliminat la política de SLA %{sla_name}' removed: '%{user_name} ha eliminat la política de SLA %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} ha silenciat la conversa' muted: '%{user_name} ha silenciat la conversa'
-8
View File
@@ -91,8 +91,6 @@ cs:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Počet rozlišení
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ cs:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: Tato zpráva byla smazána deleted: Tato zpráva byla smazána
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ cs:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} ztlumil/a konverzaci' muted: '%{user_name} ztlumil/a konverzaci'
-8
View File
@@ -91,8 +91,6 @@ da:
conversations_count: Antal samtaler conversations_count: Antal samtaler
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Antal Afsluttede
team_csv: team_csv:
team_name: Team navn team_name: Team navn
conversations_count: Samtaler tæller conversations_count: Samtaler tæller
@@ -140,8 +138,6 @@ da:
instagram_story_content: '%{story_sender} nævnte dig i historien: ' instagram_story_content: '%{story_sender} nævnte dig i historien: '
instagram_deleted_story_content: Denne historie er ikke længere tilgængelig. instagram_deleted_story_content: Denne historie er ikke længere tilgængelig.
deleted: Denne besked blev slettet deleted: Denne besked blev slettet
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ da:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} har slukket for samtalen' muted: '%{user_name} har slukket for samtalen'
-8
View File
@@ -91,8 +91,6 @@ de:
conversations_count: Anzahl der Konversationen conversations_count: Anzahl der Konversationen
avg_first_response_time: Durchschnittliche Zeit bis zur ersten Antwort avg_first_response_time: Durchschnittliche Zeit bis zur ersten Antwort
avg_resolution_time: Durchschnittliche Auflösung avg_resolution_time: Durchschnittliche Auflösung
avg_reply_time: Avg reply time
resolution_count: Auflösungsanzahl
team_csv: team_csv:
team_name: Teamname team_name: Teamname
conversations_count: Anzahl Gespräche conversations_count: Anzahl Gespräche
@@ -140,8 +138,6 @@ de:
instagram_story_content: '%{story_sender} erwähnte sie in der Geschichte: ' instagram_story_content: '%{story_sender} erwähnte sie in der Geschichte: '
instagram_deleted_story_content: Diese Geschichte ist nicht mehr verfügbar. instagram_deleted_story_content: Diese Geschichte ist nicht mehr verfügbar.
deleted: Diese Nachricht wurde gelöscht deleted: Diese Nachricht wurde gelöscht
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Fehlercode: %{error_code}' error_code: 'Fehlercode: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ de:
sla: sla:
added: '%{user_name} hat SLA-Richtlinie %{sla_name} hinzugefügt' added: '%{user_name} hat SLA-Richtlinie %{sla_name} hinzugefügt'
removed: '%{user_name} hat SLA-Richtlinie %{sla_name} entfernt' removed: '%{user_name} hat SLA-Richtlinie %{sla_name} entfernt'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} hat das Gespräch stumm geschaltet' muted: '%{user_name} hat das Gespräch stumm geschaltet'
-8
View File
@@ -91,8 +91,6 @@ el:
conversations_count: Αριθμός συνομιλιών conversations_count: Αριθμός συνομιλιών
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Αριθμός Αναλύσεων
team_csv: team_csv:
team_name: Όνομα ομάδας team_name: Όνομα ομάδας
conversations_count: Αριθμός συνομιλιών conversations_count: Αριθμός συνομιλιών
@@ -140,8 +138,6 @@ el:
instagram_story_content: 'Ο %{story_sender} σας ανέφερε στην ιστορία: ' instagram_story_content: 'Ο %{story_sender} σας ανέφερε στην ιστορία: '
instagram_deleted_story_content: Η ιστορία δεν είναι πλέον διαθέσιμη. instagram_deleted_story_content: Η ιστορία δεν είναι πλέον διαθέσιμη.
deleted: Το μήνυμα διαγράφηκε deleted: Το μήνυμα διαγράφηκε
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ el:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: 'Ο χρήστης %{user_name} σίγασε την συνομιλία' muted: 'Ο χρήστης %{user_name} σίγασε την συνομιλία'
+4 -12
View File
@@ -105,8 +105,6 @@ en:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -190,14 +188,8 @@ en:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
auto_resolve:
not_sent_due_to_messaging_window: 'Auto-resolve message not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
unmuted: '%{user_name} has unmuted the conversation' unmuted: '%{user_name} has unmuted the conversation'
auto_resolution_message: 'Resolving the conversation as it has been inactive for a while. Please start a new conversation if you need further assistance.' auto_resolution_message: 'Resolving the conversation as it has been inactive for a while. Please start a new conversation if you need further assistance.'
@@ -257,10 +249,6 @@ en:
name: 'Linear' name: 'Linear'
short_description: 'Create and link Linear issues directly from conversations.' short_description: 'Create and link Linear issues directly from conversations.'
description: 'Create issues in Linear directly from your conversation window. Alternatively, link existing Linear issues for a more streamlined and efficient issue tracking process.' description: 'Create issues in Linear directly from your conversation window. Alternatively, link existing Linear issues for a more streamlined and efficient issue tracking process.'
notion:
name: 'Notion'
short_description: 'Integrate databases, documents and pages directly with Captain.'
description: 'Connect your Notion workspace to enable Captain to access and generate intelligent responses using content from your databases, documents, and pages to provide more contextual customer support.'
shopify: shopify:
name: 'Shopify' name: 'Shopify'
short_description: 'Access order details and customer data from your Shopify store.' short_description: 'Access order details and customer data from your Shopify store.'
@@ -269,6 +257,10 @@ en:
name: 'LeadSquared' name: 'LeadSquared'
short_description: 'Sync your contacts and conversations with LeadSquared CRM.' short_description: 'Sync your contacts and conversations with LeadSquared CRM.'
description: 'Sync your contacts and conversations with LeadSquared CRM. This integration automatically creates leads in LeadSquared when new contacts are added, and logs conversation activity to provide your sales team with complete context.' description: 'Sync your contacts and conversations with LeadSquared CRM. This integration automatically creates leads in LeadSquared when new contacts are added, and logs conversation activity to provide your sales team with complete context.'
github:
name: 'GitHub'
short_description: 'Create and manage GitHub issues directly from conversations.'
description: 'Connect your GitHub repositories to create issues directly from customer conversations. This integration helps you track bugs, feature requests, and development tasks seamlessly.'
captain: captain:
copilot_error: 'Please connect an assistant to this inbox to use Copilot' copilot_error: 'Please connect an assistant to this inbox to use Copilot'
copilot_limit: 'You are out of Copilot credits. You can buy more credits from the billing section.' copilot_limit: 'You are out of Copilot credits. You can buy more credits from the billing section.'
-8
View File
@@ -91,8 +91,6 @@ es:
conversations_count: Núm. de conversaciones conversations_count: Núm. de conversaciones
avg_first_response_time: Promedio de tiempo de la primera respuesta avg_first_response_time: Promedio de tiempo de la primera respuesta
avg_resolution_time: Tiempo promedio de resolución avg_resolution_time: Tiempo promedio de resolución
avg_reply_time: Avg reply time
resolution_count: Número de resoluciones
team_csv: team_csv:
team_name: Nombre del equipo team_name: Nombre del equipo
conversations_count: Cantidad de conversaciones conversations_count: Cantidad de conversaciones
@@ -140,8 +138,6 @@ es:
instagram_story_content: '%{story_sender} te mencionó en la historia: ' instagram_story_content: '%{story_sender} te mencionó en la historia: '
instagram_deleted_story_content: Esta historia ya no está disponible. instagram_deleted_story_content: Esta historia ya no está disponible.
deleted: Este mensaje se ha eliminado deleted: Este mensaje se ha eliminado
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Código de error: %{error_code}' error_code: 'Código de error: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ es:
sla: sla:
added: '%{user_name} agregó la política de SLA %{sla_name}' added: '%{user_name} agregó la política de SLA %{sla_name}'
removed: '%{user_name} eliminó la política de SLA %{sla_name}' removed: '%{user_name} eliminó la política de SLA %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} ha silenciado la conversación' muted: '%{user_name} ha silenciado la conversación'
-8
View File
@@ -91,8 +91,6 @@ fa:
conversations_count: تعداد گفتگوها conversations_count: تعداد گفتگوها
avg_first_response_time: میانگین زمان تا اولین پاسخ avg_first_response_time: میانگین زمان تا اولین پاسخ
avg_resolution_time: میانگین زمان حل مشکل avg_resolution_time: میانگین زمان حل مشکل
avg_reply_time: Avg reply time
resolution_count: تعداد مسائل حل شده
team_csv: team_csv:
team_name: نام تیم team_name: نام تیم
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ fa:
instagram_story_content: '%{story_sender} در داستان به شما اشاره کرده: ' instagram_story_content: '%{story_sender} در داستان به شما اشاره کرده: '
instagram_deleted_story_content: این داستان دیگر در دسترس نیست. instagram_deleted_story_content: این داستان دیگر در دسترس نیست.
deleted: این پیام حذف شد deleted: این پیام حذف شد
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'کد خطا " %{error_code}' error_code: 'کد خطا " %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ fa:
sla: sla:
added: '%{user_name} سیاست SLA %{sla_name} را اضافه کرد' added: '%{user_name} سیاست SLA %{sla_name} را اضافه کرد'
removed: '%{user_name} سیاست SLA %{sla_name} را حذف کرد' removed: '%{user_name} سیاست SLA %{sla_name} را حذف کرد'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} مکالمه را بی صدا کرد' muted: '%{user_name} مکالمه را بی صدا کرد'
-8
View File
@@ -91,8 +91,6 @@ fi:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Selvitysmäärä
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ fi:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ fi:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} mykisti keskustelun' muted: '%{user_name} mykisti keskustelun'
-8
View File
@@ -91,8 +91,6 @@ fr:
conversations_count: Nbre de conversations conversations_count: Nbre de conversations
avg_first_response_time: Temps moyen pour une première réponse avg_first_response_time: Temps moyen pour une première réponse
avg_resolution_time: Temps nécessaire pour résoudre une demande (en moyenne) avg_resolution_time: Temps nécessaire pour résoudre une demande (en moyenne)
avg_reply_time: Avg reply time
resolution_count: Nombre de résolutions
team_csv: team_csv:
team_name: Nom de l'équipe team_name: Nom de l'équipe
conversations_count: Nombre de conversations conversations_count: Nombre de conversations
@@ -140,8 +138,6 @@ fr:
instagram_story_content: '%{story_sender} vous a mentionné dans la story: ' instagram_story_content: '%{story_sender} vous a mentionné dans la story: '
instagram_deleted_story_content: Cette Story n'est plus disponible. instagram_deleted_story_content: Cette Story n'est plus disponible.
deleted: Ce message a été supprimé deleted: Ce message a été supprimé
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Code d''erreur : %{error_code}' error_code: 'Code d''erreur : %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ fr:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} a mis la conversation en sourdine' muted: '%{user_name} a mis la conversation en sourdine'
-8
View File
@@ -91,8 +91,6 @@ he:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: ספירת רזולוציות
team_csv: team_csv:
team_name: שם קבוצה team_name: שם קבוצה
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ he:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ he:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ hi:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ hi:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ hi:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ hr:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ hr:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ hr:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ hu:
conversations_count: Beszélgetések száma conversations_count: Beszélgetések száma
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Megoldások száma
team_csv: team_csv:
team_name: Csapatnév team_name: Csapatnév
conversations_count: Beszélgetésszám conversations_count: Beszélgetésszám
@@ -140,8 +138,6 @@ hu:
instagram_story_content: '%{story_sender} megemlített egy storyban: ' instagram_story_content: '%{story_sender} megemlített egy storyban: '
instagram_deleted_story_content: Ez a story már nem érhető el. instagram_deleted_story_content: Ez a story már nem érhető el.
deleted: Az üzenet törölve lett deleted: Az üzenet törölve lett
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Hibakód: %{error_code}' error_code: 'Hibakód: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ hu:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} elnémította a beszélgetést' muted: '%{user_name} elnémította a beszélgetést'
-8
View File
@@ -91,8 +91,6 @@ hy:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ hy:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ hy:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ id:
conversations_count: Jumlah percakapan conversations_count: Jumlah percakapan
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Jumlah Terselesaikan
team_csv: team_csv:
team_name: Nama Tim team_name: Nama Tim
conversations_count: Jumlah percakapan conversations_count: Jumlah percakapan
@@ -140,8 +138,6 @@ id:
instagram_story_content: '%{story_sender} menyebutmu dalam story: ' instagram_story_content: '%{story_sender} menyebutmu dalam story: '
instagram_deleted_story_content: Story ini tidak lagi tersedia. instagram_deleted_story_content: Story ini tidak lagi tersedia.
deleted: Pesan ini telah terhapus deleted: Pesan ini telah terhapus
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ id:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} me-mute percakapan' muted: '%{user_name} me-mute percakapan'
-8
View File
@@ -91,8 +91,6 @@ is:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ is:
instagram_story_content: '%{story_sender} minntist á þig í sögunni: ' instagram_story_content: '%{story_sender} minntist á þig í sögunni: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ is:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ it:
conversations_count: Numero di conversazioni conversations_count: Numero di conversazioni
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Conteggio risoluzioni
team_csv: team_csv:
team_name: Nome del team team_name: Nome del team
conversations_count: Numero di conversazioni conversations_count: Numero di conversazioni
@@ -140,8 +138,6 @@ it:
instagram_story_content: '%{story_sender} ti ha menzionato nella storia: ' instagram_story_content: '%{story_sender} ti ha menzionato nella storia: '
instagram_deleted_story_content: Questa storia non è più disponibile. instagram_deleted_story_content: Questa storia non è più disponibile.
deleted: Questo messaggio è stato eliminato deleted: Questo messaggio è stato eliminato
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ it:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} ha silenziato la conversazione' muted: '%{user_name} ha silenziato la conversazione'
-8
View File
@@ -91,8 +91,6 @@ ja:
conversations_count: 会話数 conversations_count: 会話数
avg_first_response_time: 初回応答の平均時間 avg_first_response_time: 初回応答の平均時間
avg_resolution_time: 解決までの平均時間 avg_resolution_time: 解決までの平均時間
avg_reply_time: Avg reply time
resolution_count: 処理件数
team_csv: team_csv:
team_name: チーム名 team_name: チーム名
conversations_count: 会話回数 conversations_count: 会話回数
@@ -140,8 +138,6 @@ ja:
instagram_story_content: '%{story_sender} さんがストーリーであなたについて言及しました: ' instagram_story_content: '%{story_sender} さんがストーリーであなたについて言及しました: '
instagram_deleted_story_content: このストーリーはもう利用できません。 instagram_deleted_story_content: このストーリーはもう利用できません。
deleted: このメッセージは削除されました deleted: このメッセージは削除されました
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'エラーコード: %{error_code}' error_code: 'エラーコード: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ ja:
sla: sla:
added: '%{user_name} がSLAポリシー "%{sla_name}" を追加しました' added: '%{user_name} がSLAポリシー "%{sla_name}" を追加しました'
removed: '%{user_name} がSLAポリシー "%{sla_name}" を削除しました' removed: '%{user_name} がSLAポリシー "%{sla_name}" を削除しました'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} が会話をミュートしました' muted: '%{user_name} が会話をミュートしました'
-8
View File
@@ -91,8 +91,6 @@ ka:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ ka:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ ka:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ ko:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: 해결 수
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ ko:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ ko:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ lt:
conversations_count: Pokalbių kiekis conversations_count: Pokalbių kiekis
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Sprendimų skaičius
team_csv: team_csv:
team_name: Komandos pavadinimas team_name: Komandos pavadinimas
conversations_count: Pokalbių skaičius conversations_count: Pokalbių skaičius
@@ -140,8 +138,6 @@ lt:
instagram_story_content: '%{story_sender} paminėjo jus pasakojime: ' instagram_story_content: '%{story_sender} paminėjo jus pasakojime: '
instagram_deleted_story_content: Šis pasakojimas nebepasiekiamas. instagram_deleted_story_content: Šis pasakojimas nebepasiekiamas.
deleted: Šis pranešimas buvo ištrintas deleted: Šis pranešimas buvo ištrintas
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Klaidos kodas: %{error_code}' error_code: 'Klaidos kodas: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ lt:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} nutildė pokalbį' muted: '%{user_name} nutildė pokalbį'
-8
View File
@@ -91,8 +91,6 @@ lv:
conversations_count: Sarunu skaits conversations_count: Sarunu skaits
avg_first_response_time: Vidējais pirmās reakcijas laiks avg_first_response_time: Vidējais pirmās reakcijas laiks
avg_resolution_time: Vidējais atrisināšanas laiks avg_resolution_time: Vidējais atrisināšanas laiks
avg_reply_time: Avg reply time
resolution_count: Atrisināšanas Skaits
team_csv: team_csv:
team_name: Komandas nosaukums team_name: Komandas nosaukums
conversations_count: Sarunu skaits conversations_count: Sarunu skaits
@@ -140,8 +138,6 @@ lv:
instagram_story_content: '%{story_sender} pieminēja jūs stāstā: ' instagram_story_content: '%{story_sender} pieminēja jūs stāstā: '
instagram_deleted_story_content: Šis stāsts vairs nav pieejams. instagram_deleted_story_content: Šis stāsts vairs nav pieejams.
deleted: Šis ziņojums ir izdzēsts deleted: Šis ziņojums ir izdzēsts
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Kļūdas kods: %{error_code}' error_code: 'Kļūdas kods: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ lv:
sla: sla:
added: '%{user_name} pievienoja SLA politiku %{sla_name}' added: '%{user_name} pievienoja SLA politiku %{sla_name}'
removed: '%{user_name} noņēma SLA politiku %{sla_name}' removed: '%{user_name} noņēma SLA politiku %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} izslēdza sarunu' muted: '%{user_name} izslēdza sarunu'
-8
View File
@@ -91,8 +91,6 @@ ml:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: മിഴിവ് എണ്ണം
team_csv: team_csv:
team_name: ടീമിന്റെ പേര് team_name: ടീമിന്റെ പേര്
conversations_count: സംഭാഷണങ്ങളുടെ എണ്ണം conversations_count: സംഭാഷണങ്ങളുടെ എണ്ണം
@@ -140,8 +138,6 @@ ml:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: ഈ സന്ദേശം ഇല്ലാതാക്കി deleted: ഈ സന്ദേശം ഇല്ലാതാക്കി
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ ml:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ ms:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ ms:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ ms:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
-8
View File
@@ -91,8 +91,6 @@ ne:
conversations_count: No. of conversations conversations_count: No. of conversations
avg_first_response_time: Avg first response time avg_first_response_time: Avg first response time
avg_resolution_time: Avg resolution time avg_resolution_time: Avg resolution time
avg_reply_time: Avg reply time
resolution_count: Resolution Count
team_csv: team_csv:
team_name: Team name team_name: Team name
conversations_count: Conversations count conversations_count: Conversations count
@@ -140,8 +138,6 @@ ne:
instagram_story_content: '%{story_sender} mentioned you in the story: ' instagram_story_content: '%{story_sender} mentioned you in the story: '
instagram_deleted_story_content: This story is no longer available. instagram_deleted_story_content: This story is no longer available.
deleted: This message was deleted deleted: This message was deleted
whatsapp:
list_button_label: 'Choose an item'
delivery_status: delivery_status:
error_code: 'Error code: %{error_code}' error_code: 'Error code: %{error_code}'
activity: activity:
@@ -176,10 +172,6 @@ ne:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
csat: csat:
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions' not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'

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