From 8f4944fda065e4e6282930ff76bdfc18798b0984 Mon Sep 17 00:00:00 2001 From: Vishnu Narayanan Date: Thu, 13 Oct 2022 13:46:28 +0530 Subject: [PATCH 01/35] chore: revert arm64 docker build in gh action (#5619) ref: https://github.com/chatwoot/chatwoot/pull/5575 https://github.com/chatwoot/chatwoot/pull/5575#issuecomment-1277208625 --- .github/workflows/publish_foss_docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish_foss_docker.yml b/.github/workflows/publish_foss_docker.yml index aa1b15df2..2ddaba7e5 100644 --- a/.github/workflows/publish_foss_docker.yml +++ b/.github/workflows/publish_foss_docker.yml @@ -58,6 +58,6 @@ jobs: with: context: . file: docker/Dockerfile - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 push: true tags: ${{ env.DOCKER_TAG }} From a533f43fbf5ed6273ad3e0026d87d2d2a934c83e Mon Sep 17 00:00:00 2001 From: Tejaswini Chile Date: Fri, 14 Oct 2022 02:01:49 +0530 Subject: [PATCH 02/35] fix: Stop raising errors for unsupported Whatsapp messages (#5541) - Handle unsupported Whatsapp messages --- .../whatsapp/incoming_message_base_service.rb | 6 ++- .../jobs/webhooks/whatsapp_events_job_spec.rb | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/app/services/whatsapp/incoming_message_base_service.rb b/app/services/whatsapp/incoming_message_base_service.rb index e55d5fd26..40dafaced 100644 --- a/app/services/whatsapp/incoming_message_base_service.rb +++ b/app/services/whatsapp/incoming_message_base_service.rb @@ -12,7 +12,7 @@ class Whatsapp::IncomingMessageBaseService set_conversation - return if @processed_params[:messages].blank? + return if @processed_params[:messages].blank? || unprocessable_message_type? @message = @conversation.messages.build( content: message_content(@processed_params[:messages].first), @@ -86,6 +86,10 @@ class Whatsapp::IncomingMessageBaseService @processed_params[:messages].first[:type] end + def unprocessable_message_type? + %w[reaction contacts].include?(message_type) + end + def attach_files return if %w[text button interactive].include?(message_type) diff --git a/spec/jobs/webhooks/whatsapp_events_job_spec.rb b/spec/jobs/webhooks/whatsapp_events_job_spec.rb index 2a28fe41f..00fde3d41 100644 --- a/spec/jobs/webhooks/whatsapp_events_job_spec.rb +++ b/spec/jobs/webhooks/whatsapp_events_job_spec.rb @@ -62,6 +62,59 @@ RSpec.describe Webhooks::WhatsappEventsJob, type: :job do job.perform_now(wb_params) end + it 'Ignore reaction type message and stop raising error' do + other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false, + validate_provider_config: false) + wb_params = { + phone_number: channel.phone_number, + object: 'whatsapp_business_account', + entry: [{ + changes: [{ + value: { + contacts: [{ profile: { name: 'Test Test' }, wa_id: '1111981136571' }], + messages: [{ + from: '1111981136571', reaction: { emoji: '👍' }, timestamp: '1664799904', type: 'reaction' + }], + metadata: { + phone_number_id: other_channel.provider_config['phone_number_id'], + display_phone_number: other_channel.phone_number.delete('+') + } + } + }] + }] + }.with_indifferent_access + expect do + Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: other_channel.inbox, params: wb_params).perform + end.not_to change(Message, :count) + end + + it 'Ignore contacts type message and stop raising error' do + other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false, + validate_provider_config: false) + wb_params = { + phone_number: channel.phone_number, + object: 'whatsapp_business_account', + entry: [{ + changes: [{ + value: { + contacts: [{ profile: { name: 'Test Test' }, wa_id: '1111981136571' }], + messages: [{ from: '1111981136571', + contacts: [{ phones: [{ phone: '+1987654' }], name: { first_name: 'contact name' } }], + timestamp: '1664799904', + type: 'contacts' }], + metadata: { + phone_number_id: other_channel.provider_config['phone_number_id'], + display_phone_number: other_channel.phone_number.delete('+') + } + } + }] + }] + }.with_indifferent_access + expect do + Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: other_channel.inbox, params: wb_params).perform + end.not_to change(Message, :count) + end + it 'will not enque Whatsapp::IncomingMessageWhatsappCloudService when invalid phone number id' do other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false, validate_provider_config: false) From bf4338ef9e3fc7637f0d8d3549afc7a002873e47 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Fri, 14 Oct 2022 02:05:11 +0530 Subject: [PATCH 03/35] feat: Make category name in article table clickable (#5626) Co-authored-by: Pranav Raj S --- .../helpcenter/components/ArticleItem.vue | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue index cecf1f39e..b602baaa8 100644 --- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue @@ -16,7 +16,12 @@ - {{ category.name }} + + {{ category.name }} + @@ -43,6 +48,8 @@ From 1f271356ca065e6a9a0169503cababad240cd3bc Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Fri, 14 Oct 2022 02:06:42 +0530 Subject: [PATCH 04/35] feat: Update the design for dropdown buttons (#5625) --- .../assets/scss/widgets/_buttons.scss | 17 +++++++++++- .../dashboard/assets/scss/widgets/_modal.scss | 10 ++----- app/javascript/dashboard/components/Modal.vue | 10 ++++--- .../components/ui/MultiselectDropdown.vue | 26 ++++++++++++++++--- .../ui/MultiselectDropdownItems.vue | 13 +++++----- .../components/ui/dropdown/DropdownItem.vue | 8 ++---- 6 files changed, 57 insertions(+), 27 deletions(-) diff --git a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss index 9d3f84b25..478045000 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss @@ -113,9 +113,22 @@ $default-button-height: 4.0rem; } &.clear { + color: var(--w-700); + + &.secondary { + color: var(--s-700) + } + + &.success { + color: var(--g-700) + } + + &.alert { + color: var(--r-700) + } &.warning { - color: var(--y-600); + color: var(--y-700) } &:hover { @@ -146,6 +159,8 @@ $default-button-height: 4.0rem; &.small { height: var(--space-large); + padding-bottom: var(--space-smaller); + padding-top: var(--space-smaller); } &.large { diff --git a/app/javascript/dashboard/assets/scss/widgets/_modal.scss b/app/javascript/dashboard/assets/scss/widgets/_modal.scss index fc497f069..543a60797 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_modal.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_modal.scss @@ -14,15 +14,9 @@ } .modal--close { - border-radius: 50%; - color: $color-heading; - cursor: pointer; - font-size: $font-size-big; - line-height: $space-normal; - padding: $space-normal; position: absolute; - right: $space-micro; - top: $space-micro; + right: $space-small; + top: $space-small; &:hover { background: $color-background; diff --git a/app/javascript/dashboard/components/Modal.vue b/app/javascript/dashboard/components/Modal.vue index f4cad844a..d5dd55ffa 100644 --- a/app/javascript/dashboard/components/Modal.vue +++ b/app/javascript/dashboard/components/Modal.vue @@ -7,9 +7,13 @@ @click="onBackDropClick" >
- +
diff --git a/app/javascript/shared/components/ui/MultiselectDropdown.vue b/app/javascript/shared/components/ui/MultiselectDropdown.vue index 76908c380..1636ca252 100644 --- a/app/javascript/shared/components/ui/MultiselectDropdown.vue +++ b/app/javascript/shared/components/ui/MultiselectDropdown.vue @@ -41,9 +41,18 @@ :class="{ 'dropdown-pane--open': showSearchDropdown }" class="dropdown-pane" > -

- {{ multiselectorTitle }} -

+ diff --git a/app/javascript/shared/components/ui/MultiselectDropdownItems.vue b/app/javascript/shared/components/ui/MultiselectDropdownItems.vue index 8aadcb7cb..96aa99323 100644 --- a/app/javascript/shared/components/ui/MultiselectDropdownItems.vue +++ b/app/javascript/shared/components/ui/MultiselectDropdownItems.vue @@ -20,6 +20,7 @@ .dropdown-menu__item { list-style: none; + margin-bottom: var(--space-micro); ::v-deep { a, .button { + display: inline-flex; width: 100%; text-align: left; color: var(--s-700); - white-space: nowrap; - display: inline-flex; - padding: var(--space-small); - padding-top: var(--space-small); - padding-bottom: var(--space-small); - border-radius: var(--border-radius-normal); &:hover { background: var(--color-background); From e310230f624df59f99c4c602129d29652bb26c5a Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Thu, 13 Oct 2022 15:12:04 -0700 Subject: [PATCH 05/35] chore: Refactor Contact Inbox Builders (#5617) - Remove duplicate code and move everything to builders - fixes: #4680 --- .rubocop.yml | 1 + app/builders/contact_inbox_builder.rb | 43 ++-- ... => contact_inbox_with_contact_builder.rb} | 56 +++-- .../messages/facebook/message_builder.rb | 32 +-- .../contacts/contact_inboxes_controller.rb | 7 +- .../api/v1/accounts/contacts_controller.rb | 7 +- .../v1/accounts/conversations_controller.rb | 27 ++- .../concerns/request_exception_handler.rb | 2 + .../api/v1/inboxes/contacts_controller.rb | 2 +- app/mailboxes/mailbox_helper.rb | 2 +- app/models/channel/facebook_page.rb | 15 +- app/models/channel/twitter_profile.rb | 15 +- app/models/channel/web_widget.rb | 18 +- app/services/line/incoming_message_service.rb | 2 +- app/services/sms/incoming_message_service.rb | 2 +- .../telegram/incoming_message_service.rb | 2 +- .../twilio/incoming_message_service.rb | 2 +- .../whatsapp/incoming_message_base_service.rb | 2 +- db/seeds.rb | 9 +- spec/builders/contact_inbox_builder_spec.rb | 196 +++++++++--------- ...ontact_inbox_with_contact_builder_spec.rb} | 2 +- 21 files changed, 234 insertions(+), 210 deletions(-) rename app/builders/{contact_builder.rb => contact_inbox_with_contact_builder.rb} (51%) rename spec/builders/{contact_builder_spec.rb => contact_inbox_with_contact_builder_spec.rb} (98%) diff --git a/.rubocop.yml b/.rubocop.yml index dafd9a620..3665ad2e3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,6 +16,7 @@ Metrics/ClassLength: - 'app/models/message.rb' - 'app/builders/messages/facebook/message_builder.rb' - 'app/controllers/api/v1/accounts/contacts_controller.rb' + - 'app/controllers/api/v1/accounts/conversations_controller.rb' - 'app/listeners/action_cable_listener.rb' - 'app/models/conversation.rb' RSpec/ExampleLength: diff --git a/app/builders/contact_inbox_builder.rb b/app/builders/contact_inbox_builder.rb index 1b8782c51..8fcd2b158 100644 --- a/app/builders/contact_inbox_builder.rb +++ b/app/builders/contact_inbox_builder.rb @@ -1,13 +1,12 @@ +# This Builder will create a contact inbox with specified attributes. If the contact inbox already exists, it will be returned. +# For Specific Channels like whatsapp, email etc . it smartly generated appropriate the source id when none is provided. + class ContactInboxBuilder - pattr_initialize [:contact_id!, :inbox_id!, :source_id] + pattr_initialize [:contact, :inbox, :source_id, { hmac_verified: false }] def perform - @contact = Contact.find(contact_id) - @inbox = @contact.account.inboxes.find(inbox_id) - return unless ['Channel::TwilioSms', 'Channel::Sms', 'Channel::Email', 'Channel::Api', 'Channel::Whatsapp'].include? @inbox.channel_type - - source_id = @source_id || generate_source_id - create_contact_inbox(source_id) if source_id.present? + @source_id ||= generate_source_id + create_contact_inbox if source_id.present? end private @@ -19,23 +18,37 @@ class ContactInboxBuilder when 'Channel::Whatsapp' wa_source_id when 'Channel::Email' - @contact.email + email_source_id when 'Channel::Sms' - @contact.phone_number - when 'Channel::Api' + phone_source_id + when 'Channel::Api', 'Channel::WebWidget' SecureRandom.uuid + else + raise "Unsupported operation for this channel: #{@inbox.channel_type}" end end + def email_source_id + raise ActionController::ParameterMissing, 'contact email' unless @contact.email + + @contact.email + end + + def phone_source_id + raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number + + @contact.phone_number + end + def wa_source_id - return unless @contact.phone_number + raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number # whatsapp doesn't want the + in e164 format @contact.phone_number.delete('+').to_s end def twilio_source_id - return unless @contact.phone_number + raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number case @inbox.channel.medium when 'sms' @@ -45,11 +58,11 @@ class ContactInboxBuilder end end - def create_contact_inbox(source_id) - ::ContactInbox.find_or_create_by!( + def create_contact_inbox + ::ContactInbox.create_with(hmac_verified: hmac_verified || false).find_or_create_by!( contact_id: @contact.id, inbox_id: @inbox.id, - source_id: source_id + source_id: @source_id ) end end diff --git a/app/builders/contact_builder.rb b/app/builders/contact_inbox_with_contact_builder.rb similarity index 51% rename from app/builders/contact_builder.rb rename to app/builders/contact_inbox_with_contact_builder.rb index 938072643..d97f64cfe 100644 --- a/app/builders/contact_builder.rb +++ b/app/builders/contact_inbox_with_contact_builder.rb @@ -1,25 +1,47 @@ -class ContactBuilder - pattr_initialize [:source_id!, :inbox!, :contact_attributes!, :hmac_verified] +# This Builder will create a contact and contact inbox with specified attributes. +# If an existing identified contact exisits, it will be returned. +# for contact inbox logic it uses the contact inbox builder + +class ContactInboxWithContactBuilder + pattr_initialize [:inbox!, :contact_attributes!, :source_id, :hmac_verified] def perform - contact_inbox = inbox.contact_inboxes.find_by(source_id: source_id) - return contact_inbox if contact_inbox + find_or_create_contact_and_contact_inbox + # in case of race conditions where contact is created by another thread + # we will try to find the contact and create a contact inbox + rescue ActiveRecord::RecordNotUnique + find_or_create_contact_and_contact_inbox + end - build_contact_inbox + def find_or_create_contact_and_contact_inbox + @contact_inbox = inbox.contact_inboxes.find_by(source_id: source_id) if source_id.present? + return @contact_inbox if @contact_inbox + + ActiveRecord::Base.transaction(requires_new: true) do + build_contact_with_contact_inbox + update_contact_avatar(@contact) unless @contact.avatar.attached? + @contact_inbox + end end private + def build_contact_with_contact_inbox + @contact = find_contact || create_contact + @contact_inbox = create_contact_inbox + end + def account @account ||= inbox.account end - def create_contact_inbox(contact) - ::ContactInbox.create_with(hmac_verified: hmac_verified || false).find_or_create_by!( - contact_id: contact.id, - inbox_id: inbox.id, - source_id: source_id - ) + def create_contact_inbox + ContactInboxBuilder.new( + contact: @contact, + inbox: @inbox, + source_id: @source_id, + hmac_verified: hmac_verified + ).perform end def update_contact_avatar(contact) @@ -61,16 +83,4 @@ class ContactBuilder account.contacts.find_by(phone_number: phone_number) end - - def build_contact_inbox - ActiveRecord::Base.transaction do - contact = find_contact || create_contact - contact_inbox = create_contact_inbox(contact) - update_contact_avatar(contact) - contact_inbox - rescue StandardError => e - Rails.logger.error e - raise e - end - end end diff --git a/app/builders/messages/facebook/message_builder.rb b/app/builders/messages/facebook/message_builder.rb index 9f670602a..42d567e54 100644 --- a/app/builders/messages/facebook/message_builder.rb +++ b/app/builders/messages/facebook/message_builder.rb @@ -22,10 +22,9 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder return if @inbox.channel.reauthorization_required? ActiveRecord::Base.transaction do - build_contact + build_contact_inbox build_message end - ensure_contact_avatar rescue Koala::Facebook::AuthenticationError @inbox.channel.authorization_error! rescue StandardError => e @@ -35,15 +34,12 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder private - def contact - @contact ||= @inbox.contact_inboxes.find_by(source_id: @sender_id)&.contact - end - - def build_contact - return if contact.present? - - @contact = Contact.create!(contact_params.except(:remote_avatar_url)) - @contact_inbox = ContactInbox.find_or_create_by!(contact: contact, inbox: @inbox, source_id: @sender_id) + def build_contact_inbox + @contact_inbox = ::ContactInboxWithContactBuilder.new( + source_id: @sender_id, + inbox: @inbox, + contact_attributes: contact_params + ).perform end def build_message @@ -54,19 +50,11 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder end end - def ensure_contact_avatar - return if contact_params[:remote_avatar_url].blank? - return if @contact.avatar.attached? - - Avatar::AvatarFromUrlJob.perform_later(@contact, contact_params[:remote_avatar_url]) - end - def conversation @conversation ||= Conversation.find_by(conversation_params) || build_conversation end def build_conversation - @contact_inbox ||= contact.contact_inboxes.find_by!(source_id: @sender_id) Conversation.create!(conversation_params.merge( contact_inbox_id: @contact_inbox.id )) @@ -94,7 +82,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder { account_id: @inbox.account_id, inbox_id: @inbox.id, - contact_id: contact.id + contact_id: @contact_inbox.contact_id } end @@ -105,7 +93,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder message_type: @message_type, content: response.content, source_id: response.identifier, - sender: @outgoing_echo ? nil : contact + sender: @outgoing_echo ? nil : @contact_inbox.contact } end @@ -113,7 +101,7 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder { name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}", account_id: @inbox.account_id, - remote_avatar_url: result['profile_pic'] || '' + avatar_url: result['profile_pic'] } end diff --git a/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb b/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb index fdcdcaf9e..b4287ae08 100644 --- a/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb +++ b/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb @@ -2,8 +2,11 @@ class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts: before_action :ensure_inbox, only: [:create] def create - source_id = params[:source_id] || SecureRandom.uuid - @contact_inbox = ContactInbox.create!(contact: @contact, inbox: @inbox, source_id: source_id) + @contact_inbox = ContactInboxBuilder.new( + contact: @contact, + inbox: @inbox, + source_id: params[:source_id] + ).perform end private diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb index 1c56e9c04..b86b973df 100644 --- a/app/controllers/api/v1/accounts/contacts_controller.rb +++ b/app/controllers/api/v1/accounts/contacts_controller.rb @@ -134,8 +134,11 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController return if params[:inbox_id].blank? inbox = Current.account.inboxes.find(params[:inbox_id]) - source_id = params[:source_id] || SecureRandom.uuid - ContactInbox.create!(contact: @contact, inbox: inbox, source_id: source_id) + ContactInboxBuilder.new( + contact: @contact, + inbox: inbox, + source_id: params[:source_id] + ).perform end def permitted_params diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb index 0515eabca..8734a3dd4 100644 --- a/app/controllers/api/v1/accounts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/conversations_controller.rb @@ -3,7 +3,7 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro include DateRangeHelper before_action :conversation, except: [:index, :meta, :search, :create, :filter] - before_action :contact_inbox, only: [:create] + before_action :inbox, :contact, :contact_inbox, only: [:create] def index result = conversation_finder.perform @@ -109,22 +109,35 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro authorize @conversation.inbox, :show? end + def inbox + return if params[:inbox_id].blank? + + @inbox = Current.account.inboxes.find(params[:inbox_id]) + authorize @inbox, :show? + end + + def contact + return if params[:contact_id].blank? + + @contact = Current.account.contacts.find(params[:contact_id]) + end + def contact_inbox @contact_inbox = build_contact_inbox + # fallback for the old case where we do look up only using source id + # In future we need to change this and make sure we do look up on combination of inbox_id and source_id + # and deprecate the support of passing only source_id as the param @contact_inbox ||= ::ContactInbox.find_by!(source_id: params[:source_id]) authorize @contact_inbox.inbox, :show? end def build_contact_inbox - return if params[:contact_id].blank? || params[:inbox_id].blank? - - inbox = Current.account.inboxes.find(params[:inbox_id]) - authorize inbox, :show? + return if @inbox.blank? || @contact.blank? ContactInboxBuilder.new( - contact_id: params[:contact_id], - inbox_id: inbox.id, + contact: @contact, + inbox: @inbox, source_id: params[:source_id] ).perform end diff --git a/app/controllers/concerns/request_exception_handler.rb b/app/controllers/concerns/request_exception_handler.rb index 6e9ed04cc..2f53fdc2b 100644 --- a/app/controllers/concerns/request_exception_handler.rb +++ b/app/controllers/concerns/request_exception_handler.rb @@ -13,6 +13,8 @@ module RequestExceptionHandler render_not_found_error('Resource could not be found') rescue Pundit::NotAuthorizedError render_unauthorized('You are not authorized to do this action') + rescue ActionController::ParameterMissing => e + render_could_not_create_error(e.message) ensure # to address the thread variable leak issues in Puma/Thin webserver Current.reset diff --git a/app/controllers/public/api/v1/inboxes/contacts_controller.rb b/app/controllers/public/api/v1/inboxes/contacts_controller.rb index eb794f2a0..1fde3051e 100644 --- a/app/controllers/public/api/v1/inboxes/contacts_controller.rb +++ b/app/controllers/public/api/v1/inboxes/contacts_controller.rb @@ -4,7 +4,7 @@ class Public::Api::V1::Inboxes::ContactsController < Public::Api::V1::InboxesCon def create source_id = params[:source_id] || SecureRandom.uuid - @contact_inbox = ::ContactBuilder.new( + @contact_inbox = ::ContactInboxWithContactBuilder.new( source_id: source_id, inbox: @inbox_channel.inbox, contact_attributes: permitted_params.except(:identifier, :identifier_hash) diff --git a/app/mailboxes/mailbox_helper.rb b/app/mailboxes/mailbox_helper.rb index 1519343ca..216d5c2c3 100644 --- a/app/mailboxes/mailbox_helper.rb +++ b/app/mailboxes/mailbox_helper.rb @@ -34,7 +34,7 @@ module MailboxHelper end def create_contact - @contact_inbox = ::ContactBuilder.new( + @contact_inbox = ::ContactInboxWithContactBuilder.new( source_id: processed_mail.original_sender, inbox: @inbox, contact_attributes: { diff --git a/app/models/channel/facebook_page.rb b/app/models/channel/facebook_page.rb index 2153708e2..bba45f326 100644 --- a/app/models/channel/facebook_page.rb +++ b/app/models/channel/facebook_page.rb @@ -37,16 +37,11 @@ class Channel::FacebookPage < ApplicationRecord end def create_contact_inbox(instagram_id, name) - ActiveRecord::Base.transaction do - contact = inbox.account.contacts.create!(name: name) - ::ContactInbox.create!( - contact_id: contact.id, - inbox_id: inbox.id, - source_id: instagram_id - ) - rescue StandardError => e - Rails.logger.error e - end + @contact_inbox = ::ContactInboxWithContactBuilder.new({ + source_id: instagram_id, + inbox: inbox, + contact_attributes: { name: name } + }).perform end def subscribe diff --git a/app/models/channel/twitter_profile.rb b/app/models/channel/twitter_profile.rb index 4f6fa7ba1..d0f765e9f 100644 --- a/app/models/channel/twitter_profile.rb +++ b/app/models/channel/twitter_profile.rb @@ -32,16 +32,11 @@ class Channel::TwitterProfile < ApplicationRecord end def create_contact_inbox(profile_id, name, additional_attributes) - ActiveRecord::Base.transaction do - contact = inbox.account.contacts.create!(additional_attributes: additional_attributes, name: name) - ::ContactInbox.create!( - contact_id: contact.id, - inbox_id: inbox.id, - source_id: profile_id - ) - rescue StandardError => e - Rails.logger.error e - end + ::ContactInboxWithContactBuilder.new({ + source_id: profile_id, + inbox: inbox, + contact_attributes: { name: name, additional_attributes: additional_attributes } + }).perform end def twitter_client diff --git a/app/models/channel/web_widget.rb b/app/models/channel/web_widget.rb index b85443633..59d392892 100644 --- a/app/models/channel/web_widget.rb +++ b/app/models/channel/web_widget.rb @@ -98,19 +98,9 @@ class Channel::WebWidget < ApplicationRecord end def create_contact_inbox(additional_attributes = {}) - ActiveRecord::Base.transaction do - contact = inbox.account.contacts.create!( - name: ::Haikunator.haikunate(1000), - additional_attributes: additional_attributes - ) - contact_inbox = ::ContactInbox.create!( - contact_id: contact.id, - inbox_id: inbox.id, - source_id: SecureRandom.uuid - ) - contact_inbox - rescue StandardError => e - Rails.logger.error e - end + ::ContactInboxWithContactBuilder.new({ + inbox: inbox, + contact_attributes: { additional_attributes: additional_attributes } + }).perform end end diff --git a/app/services/line/incoming_message_service.rb b/app/services/line/incoming_message_service.rb index 535c03dc7..48a52eb8f 100644 --- a/app/services/line/incoming_message_service.rb +++ b/app/services/line/incoming_message_service.rb @@ -81,7 +81,7 @@ class Line::IncomingMessageService end def set_contact - contact_inbox = ::ContactBuilder.new( + contact_inbox = ::ContactInboxWithContactBuilder.new( source_id: line_contact_info['userId'], inbox: inbox, contact_attributes: contact_attributes diff --git a/app/services/sms/incoming_message_service.rb b/app/services/sms/incoming_message_service.rb index 7ee6e3e63..7aa22b19e 100644 --- a/app/services/sms/incoming_message_service.rb +++ b/app/services/sms/incoming_message_service.rb @@ -37,7 +37,7 @@ class Sms::IncomingMessageService end def set_contact - contact_inbox = ::ContactBuilder.new( + contact_inbox = ::ContactInboxWithContactBuilder.new( source_id: params[:from], inbox: @inbox, contact_attributes: contact_attributes diff --git a/app/services/telegram/incoming_message_service.rb b/app/services/telegram/incoming_message_service.rb index a26bda14e..e8ab8a72c 100644 --- a/app/services/telegram/incoming_message_service.rb +++ b/app/services/telegram/incoming_message_service.rb @@ -31,7 +31,7 @@ class Telegram::IncomingMessageService end def set_contact - contact_inbox = ::ContactBuilder.new( + contact_inbox = ::ContactInboxWithContactBuilder.new( source_id: params[:message][:from][:id], inbox: inbox, contact_attributes: contact_attributes diff --git a/app/services/twilio/incoming_message_service.rb b/app/services/twilio/incoming_message_service.rb index 50c77111c..4473131df 100644 --- a/app/services/twilio/incoming_message_service.rb +++ b/app/services/twilio/incoming_message_service.rb @@ -47,7 +47,7 @@ class Twilio::IncomingMessageService end def set_contact - contact_inbox = ::ContactBuilder.new( + contact_inbox = ::ContactInboxWithContactBuilder.new( source_id: params[:From], inbox: inbox, contact_attributes: contact_attributes diff --git a/app/services/whatsapp/incoming_message_base_service.rb b/app/services/whatsapp/incoming_message_base_service.rb index 40dafaced..da08e02d6 100644 --- a/app/services/whatsapp/incoming_message_base_service.rb +++ b/app/services/whatsapp/incoming_message_base_service.rb @@ -48,7 +48,7 @@ class Whatsapp::IncomingMessageBaseService contact_params = @processed_params[:contacts]&.first return if contact_params.blank? - contact_inbox = ::ContactBuilder.new( + contact_inbox = ::ContactInboxWithContactBuilder.new( source_id: contact_params[:wa_id], inbox: inbox, contact_attributes: { name: contact_params.dig(:profile, :name), phone_number: "+#{@processed_params[:messages].first[:from]}" } diff --git a/db/seeds.rb b/db/seeds.rb index c4c7eda71..ead862669 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -46,8 +46,13 @@ unless Rails.env.production? inbox = Inbox.create!(channel: web_widget, account: account, name: 'Acme Support') InboxMember.create!(user: user, inbox: inbox) - contact = Contact.create!(name: 'jane', email: 'jane@example.com', phone_number: '+2320000', account: account) - contact_inbox = ContactInbox.create!(inbox: inbox, contact: contact, source_id: user.id, hmac_verified: true) + contact = ::ContactInboxWithContactBuilder.new( + source_id: user.id, + inbox: inbox, + hmac_verified: true, + contact_attributes: { name: 'jane', email: 'jane@example.com', phone_number: '+2320000' } + ).perform&.contact + conversation = Conversation.create!( account: account, inbox: inbox, diff --git a/spec/builders/contact_inbox_builder_spec.rb b/spec/builders/contact_inbox_builder_spec.rb index 47210f6ca..46b0d749c 100644 --- a/spec/builders/contact_inbox_builder_spec.rb +++ b/spec/builders/contact_inbox_builder_spec.rb @@ -12,8 +12,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with the source id provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twilio_inbox.id, + contact: contact, + inbox: twilio_inbox, source_id: contact.phone_number ).perform @@ -23,8 +23,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twilio_inbox.id + contact: contact, + inbox: twilio_inbox ).perform expect(contact_inbox.id).to eq(existing_contact_inbox.id) @@ -33,8 +33,8 @@ describe ::ContactInboxBuilder do it 'creates a new contact inbox when different source id is provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: contact.phone_number) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twilio_inbox.id, + contact: contact, + inbox: twilio_inbox, source_id: '+224213223422' ).perform @@ -44,12 +44,23 @@ describe ::ContactInboxBuilder do it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twilio_inbox.id + contact: contact, + inbox: twilio_inbox ).perform expect(contact_inbox.source_id).to eq(contact.phone_number) end + + it 'raises error when contact phone number is not present and no source id is provided' do + contact.update!(phone_number: nil) + + expect do + described_class.new( + contact: contact, + inbox: twilio_inbox + ).perform + end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact phone number') + end end describe 'twilio whatsapp inbox' do @@ -59,8 +70,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with the source id provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}") contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twilio_inbox.id, + contact: contact, + inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}" ).perform @@ -70,8 +81,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}") contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twilio_inbox.id + contact: contact, + inbox: twilio_inbox ).perform expect(contact_inbox.id).to eq(existing_contact_inbox.id) @@ -80,8 +91,8 @@ describe ::ContactInboxBuilder do it 'creates a new contact inbox when different source id is provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: twilio_inbox, source_id: "whatsapp:#{contact.phone_number}") contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twilio_inbox.id, + contact: contact, + inbox: twilio_inbox, source_id: 'whatsapp:+555555' ).perform @@ -91,12 +102,23 @@ describe ::ContactInboxBuilder do it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twilio_inbox.id + contact: contact, + inbox: twilio_inbox ).perform expect(contact_inbox.source_id).to eq("whatsapp:#{contact.phone_number}") end + + it 'raises error when contact phone number is not present and no source id is provided' do + contact.update!(phone_number: nil) + + expect do + described_class.new( + contact: contact, + inbox: twilio_inbox + ).perform + end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact phone number') + end end describe 'whatsapp inbox' do @@ -105,8 +127,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with the source id provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+')) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: whatsapp_inbox.id, + contact: contact, + inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+') ).perform @@ -116,8 +138,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+')) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: whatsapp_inbox.id + contact: contact, + inbox: whatsapp_inbox ).perform expect(contact_inbox.id).to be(existing_contact_inbox.id) @@ -126,8 +148,8 @@ describe ::ContactInboxBuilder do it 'creates a new contact inbox when different source id is provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+')) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: whatsapp_inbox.id, + contact: contact, + inbox: whatsapp_inbox, source_id: '555555' ).perform @@ -137,12 +159,23 @@ describe ::ContactInboxBuilder do it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: whatsapp_inbox.id + contact: contact, + inbox: whatsapp_inbox ).perform expect(contact_inbox.source_id).to eq(contact.phone_number&.delete('+')) end + + it 'raises error when contact phone number is not present and no source id is provided' do + contact.update!(phone_number: nil) + + expect do + described_class.new( + contact: contact, + inbox: whatsapp_inbox + ).perform + end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact phone number') + end end describe 'sms inbox' do @@ -152,8 +185,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with the source id provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: sms_inbox.id, + contact: contact, + inbox: sms_inbox, source_id: contact.phone_number ).perform @@ -163,8 +196,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: sms_inbox.id + contact: contact, + inbox: sms_inbox ).perform expect(contact_inbox.id).to eq(existing_contact_inbox.id) @@ -173,8 +206,8 @@ describe ::ContactInboxBuilder do it 'creates a new contact inbox when different source id is provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: sms_inbox.id, + contact: contact, + inbox: sms_inbox, source_id: '+224213223422' ).perform @@ -184,12 +217,23 @@ describe ::ContactInboxBuilder do it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: sms_inbox.id + contact: contact, + inbox: sms_inbox ).perform expect(contact_inbox.source_id).to eq(contact.phone_number) end + + it 'raises error when contact phone number is not present and no source id is provided' do + contact.update!(phone_number: nil) + + expect do + described_class.new( + contact: contact, + inbox: sms_inbox + ).perform + end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact phone number') + end end describe 'email inbox' do @@ -199,8 +243,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with the source id provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: email_inbox.id, + contact: contact, + inbox: email_inbox, source_id: contact.email ).perform @@ -210,8 +254,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with email and source id is not provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: email_inbox.id + contact: contact, + inbox: email_inbox ).perform expect(contact_inbox.id).to eq(existing_contact_inbox.id) @@ -220,8 +264,8 @@ describe ::ContactInboxBuilder do it 'creates a new contact inbox when different source id is provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: email_inbox, source_id: contact.email) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: email_inbox.id, + contact: contact, + inbox: email_inbox, source_id: 'xyc@xyc.com' ).perform @@ -231,12 +275,23 @@ describe ::ContactInboxBuilder do it 'creates a contact inbox with contact email when source id not provided and no contact inbox exists' do contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: email_inbox.id + contact: contact, + inbox: email_inbox ).perform expect(contact_inbox.source_id).to eq(contact.email) end + + it 'raises error when contact email is not present and no source id is provided' do + contact.update!(email: nil) + + expect do + described_class.new( + contact: contact, + inbox: email_inbox + ).perform + end.to raise_error(ActionController::ParameterMissing, 'param is missing or the value is empty: contact email') + end end describe 'api inbox' do @@ -246,8 +301,8 @@ describe ::ContactInboxBuilder do it 'does not create contact inbox when contact inbox already exists with the source id provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: api_inbox, source_id: 'test') contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: api_inbox.id, + contact: contact, + inbox: api_inbox, source_id: 'test' ).perform @@ -257,8 +312,8 @@ describe ::ContactInboxBuilder do it 'creates a new contact inbox when different source id is provided' do existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: api_inbox, source_id: SecureRandom.uuid) contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: api_inbox.id, + contact: contact, + inbox: api_inbox, source_id: 'test' ).perform @@ -268,61 +323,12 @@ describe ::ContactInboxBuilder do it 'creates a contact inbox with SecureRandom.uuid when source id not provided and no contact inbox exists' do contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: api_inbox.id + contact: contact, + inbox: api_inbox ).perform expect(contact_inbox.source_id).not_to be_nil end end - - describe 'web widget' do - let!(:website_channel) { create(:channel_widget, account: account) } - let!(:website_inbox) { create(:inbox, channel: website_channel, account: account) } - - it 'does not create contact inbox' do - contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: website_inbox.id, - source_id: 'test' - ).perform - - expect(contact_inbox).to be_nil - end - end - - describe 'facebook inbox' do - before do - stub_request(:post, /graph.facebook.com/) - end - - let!(:facebook_channel) { create(:channel_facebook_page, account: account) } - let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) } - - it 'does not create contact inbox' do - contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: facebook_inbox.id, - source_id: 'test' - ).perform - - expect(contact_inbox).to be_nil - end - end - - describe 'twitter inbox' do - let!(:twitter_channel) { create(:channel_twitter_profile, account: account) } - let!(:twitter_inbox) { create(:inbox, channel: twitter_channel, account: account) } - - it 'does not create contact inbox' do - contact_inbox = described_class.new( - contact_id: contact.id, - inbox_id: twitter_inbox.id, - source_id: 'test' - ).perform - - expect(contact_inbox).to be_nil - end - end end end diff --git a/spec/builders/contact_builder_spec.rb b/spec/builders/contact_inbox_with_contact_builder_spec.rb similarity index 98% rename from spec/builders/contact_builder_spec.rb rename to spec/builders/contact_inbox_with_contact_builder_spec.rb index 29df0da22..e76d199d4 100644 --- a/spec/builders/contact_builder_spec.rb +++ b/spec/builders/contact_inbox_with_contact_builder_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe ::ContactBuilder do +describe ::ContactInboxWithContactBuilder do let(:account) { create(:account) } let(:inbox) { create(:inbox, account: account) } let(:contact) { create(:contact, email: 'xyc@example.com', phone_number: '+23423424123', account: account, identifier: '123') } From a6960dc2d38f64f02eb17af88c59ca3f50a24c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Kube=C5=A1?= <46596180+KubesDavid@users.noreply.github.com> Date: Fri, 14 Oct 2022 05:43:11 +0200 Subject: [PATCH 06/35] chore: Refactor widget (#5621) --- app/javascript/widget/api/agent.js | 3 +-- app/javascript/widget/api/campaign.js | 3 +-- app/javascript/widget/api/conversation.js | 12 ++++-------- app/javascript/widget/assets/scss/woot.scss | 5 +---- app/javascript/widget/components/AgentMessage.vue | 3 +-- app/javascript/widget/components/ChatAttachment.vue | 4 ++-- .../widget/components/ChatHeaderExpanded.vue | 7 ++++++- app/javascript/widget/components/ChatInputWrap.vue | 4 +--- app/javascript/widget/components/FileBubble.vue | 3 +-- app/javascript/widget/components/PreChat/Form.vue | 7 ++----- .../widget/components/TeamAvailability.vue | 4 ++-- .../widget/components/UnreadMessageList.vue | 3 +-- app/javascript/widget/helpers/IframeEventHelper.js | 2 +- app/javascript/widget/helpers/utils.js | 11 +---------- .../widget/store/modules/conversation/getters.js | 6 ++---- .../widget/store/modules/conversation/helpers.js | 2 +- .../widget/store/modules/conversation/mutations.js | 3 +-- .../widget/store/modules/conversationLabels.js | 4 ++-- 18 files changed, 31 insertions(+), 55 deletions(-) diff --git a/app/javascript/widget/api/agent.js b/app/javascript/widget/api/agent.js index 0debeccaf..5dceecad7 100644 --- a/app/javascript/widget/api/agent.js +++ b/app/javascript/widget/api/agent.js @@ -3,6 +3,5 @@ import { API } from 'widget/helpers/axios'; export const getAvailableAgents = async websiteToken => { const urlData = endPoints.getAvailableAgents(websiteToken); - const result = await API.get(urlData.url, { params: urlData.params }); - return result; + return API.get(urlData.url, { params: urlData.params }); }; diff --git a/app/javascript/widget/api/campaign.js b/app/javascript/widget/api/campaign.js index 57d81e084..efa154f41 100644 --- a/app/javascript/widget/api/campaign.js +++ b/app/javascript/widget/api/campaign.js @@ -3,8 +3,7 @@ import { API } from 'widget/helpers/axios'; const getCampaigns = async websiteToken => { const urlData = endPoints.getCampaigns(websiteToken); - const result = await API.get(urlData.url, { params: urlData.params }); - return result; + return API.get(urlData.url, { params: urlData.params }); }; const triggerCampaign = async ({ diff --git a/app/javascript/widget/api/conversation.js b/app/javascript/widget/api/conversation.js index fdb3842fd..4cf4de25e 100755 --- a/app/javascript/widget/api/conversation.js +++ b/app/javascript/widget/api/conversation.js @@ -3,26 +3,22 @@ import { API } from 'widget/helpers/axios'; const createConversationAPI = async content => { const urlData = endPoints.createConversation(content); - const result = await API.post(urlData.url, urlData.params); - return result; + return API.post(urlData.url, urlData.params); }; const sendMessageAPI = async content => { const urlData = endPoints.sendMessage(content); - const result = await API.post(urlData.url, urlData.params); - return result; + return API.post(urlData.url, urlData.params); }; const sendAttachmentAPI = async attachment => { const urlData = endPoints.sendAttachment(attachment); - const result = await API.post(urlData.url, urlData.params); - return result; + return API.post(urlData.url, urlData.params); }; const getMessagesAPI = async ({ before }) => { const urlData = endPoints.getConversation({ before }); - const result = await API.get(urlData.url, { params: urlData.params }); - return result; + return API.get(urlData.url, { params: urlData.params }); }; const getConversationAPI = async () => { diff --git a/app/javascript/widget/assets/scss/woot.scss b/app/javascript/widget/assets/scss/woot.scss index 9a2a6a8e6..3f882eb38 100755 --- a/app/javascript/widget/assets/scss/woot.scss +++ b/app/javascript/widget/assets/scss/woot.scss @@ -61,10 +61,7 @@ body { .is-flat-design { .chat-bubble { - border-bottom-left-radius: 0 !important; - border-bottom-right-radius: 0 !important; - border-top-left-radius: 0 !important; - border-top-right-radius: 0 !important; + border-radius: 0 !important; box-shadow: none; } diff --git a/app/javascript/widget/components/AgentMessage.vue b/app/javascript/widget/components/AgentMessage.vue index c338dce83..6b1fb782b 100755 --- a/app/javascript/widget/components/AgentMessage.vue +++ b/app/javascript/widget/components/AgentMessage.vue @@ -104,8 +104,7 @@ export default { ) { return false; } - if (!this.message.content) return false; - return true; + return this.message.content; }, readableTime() { const { created_at: createdAt = '' } = this.message; diff --git a/app/javascript/widget/components/ChatAttachment.vue b/app/javascript/widget/components/ChatAttachment.vue index 87da3f5df..0412ca5fb 100755 --- a/app/javascript/widget/components/ChatAttachment.vue +++ b/app/javascript/widget/components/ChatAttachment.vue @@ -54,9 +54,9 @@ export default { }, async onFileUpload(file) { if (this.globalConfig.directUploadsEnabled) { - this.onDirectFileUpload(file); + await this.onDirectFileUpload(file); } else { - this.onIndirectFileUpload(file); + await this.onIndirectFileUpload(file); } }, async onDirectFileUpload(file) { diff --git a/app/javascript/widget/components/ChatHeaderExpanded.vue b/app/javascript/widget/components/ChatHeaderExpanded.vue index c1823f504..f82f66c46 100755 --- a/app/javascript/widget/components/ChatHeaderExpanded.vue +++ b/app/javascript/widget/components/ChatHeaderExpanded.vue @@ -7,7 +7,12 @@ class="flex items-start" :class="[avatarUrl ? 'justify-between' : 'justify-end']" > - + Avatar

{ - if ( + return !( (isUserEmailAvailable && field.name === 'emailAddress') || (isUserPhoneNumberAvailable && field.name === 'phoneNumber') - ) { - return false; - } - return true; + ); }); }, enabledPreChatFields() { diff --git a/app/javascript/widget/components/TeamAvailability.vue b/app/javascript/widget/components/TeamAvailability.vue index 1f4c5d046..a3a6b0c02 100644 --- a/app/javascript/widget/components/TeamAvailability.vue +++ b/app/javascript/widget/components/TeamAvailability.vue @@ -13,7 +13,7 @@ }}
- {{ replyWaitMeessage }} + {{ replyWaitMessage }}
@@ -75,7 +75,7 @@ export default { } return anyAgentOnline; }, - replyWaitMeessage() { + replyWaitMessage() { const { workingHoursEnabled } = this.channelConfig; if (this.isOnline) { diff --git a/app/javascript/widget/components/UnreadMessageList.vue b/app/javascript/widget/components/UnreadMessageList.vue index 19afe49d1..6683c585e 100644 --- a/app/javascript/widget/components/UnreadMessageList.vue +++ b/app/javascript/widget/components/UnreadMessageList.vue @@ -107,13 +107,12 @@ export default { .clear-button { background: transparent; color: $color-woot; - padding: 0; border: 0; font-weight: $font-weight-bold; font-size: $font-size-medium; transition: all 0.3s var(--ease-in-cubic); margin-left: $space-smaller; - padding-right: $space-one; + padding: 0 $space-one 0 0; &:hover { transform: translateX($space-smaller); diff --git a/app/javascript/widget/helpers/IframeEventHelper.js b/app/javascript/widget/helpers/IframeEventHelper.js index 953802df8..40c0e1f1c 100644 --- a/app/javascript/widget/helpers/IframeEventHelper.js +++ b/app/javascript/widget/helpers/IframeEventHelper.js @@ -10,7 +10,7 @@ export const loadedEventConfig = () => { export const getExtraSpaceToScroll = () => { // This function calculates the extra space needed for the view to - // accomodate the height of close button + height of + // accommodate the height of close button + height of // read messages button. So that scrollbar won't appear const unreadMessageWrap = document.querySelector('.unread-messages'); const unreadCloseWrap = document.querySelector('.close-unread-wrap'); diff --git a/app/javascript/widget/helpers/utils.js b/app/javascript/widget/helpers/utils.js index 1a4b2c1d6..a4d5dafa3 100755 --- a/app/javascript/widget/helpers/utils.js +++ b/app/javascript/widget/helpers/utils.js @@ -3,13 +3,6 @@ import { WOOT_PREFIX } from './constants'; export const isEmptyObject = obj => Object.keys(obj).length === 0 && obj.constructor === Object; -export const arrayToHashById = array => - array.reduce((map, obj) => { - const newMap = map; - newMap[obj.id] = obj; - return newMap; - }, {}); - export const sendMessage = msg => { window.parent.postMessage( `chatwoot-widget:${JSON.stringify({ ...msg })}`, @@ -22,9 +15,7 @@ export const IFrameHelper = { sendMessage, isAValidEvent: e => { const isDataAString = typeof e.data === 'string'; - const isAValidWootEvent = - isDataAString && e.data.indexOf(WOOT_PREFIX) === 0; - return isAValidWootEvent; + return isDataAString && e.data.indexOf(WOOT_PREFIX) === 0; }, getMessage: e => JSON.parse(e.data.replace(WOOT_PREFIX, '')), }; diff --git a/app/javascript/widget/store/modules/conversation/getters.js b/app/javascript/widget/store/modules/conversation/getters.js index 9d1c067f4..74e582348 100644 --- a/app/javascript/widget/store/modules/conversation/getters.js +++ b/app/javascript/widget/store/modules/conversation/getters.js @@ -32,7 +32,7 @@ export const getters = { }, getUnreadMessageCount: _state => { const { userLastSeenAt } = _state.meta; - const count = Object.values(_state.conversations).filter(chat => { + return Object.values(_state.conversations).filter(chat => { const { created_at: createdAt, message_type: messageType } = chat; const isOutGoing = messageType === MESSAGE_TYPE.OUTGOING; const hasNotSeen = userLastSeenAt @@ -40,7 +40,6 @@ export const getters = { : true; return hasNotSeen && isOutGoing; }).length; - return count; }, getUnreadTextMessages: (_state, _getters) => { const unreadCount = _getters.getUnreadMessageCount; @@ -50,7 +49,6 @@ export const getters = { return messageType === MESSAGE_TYPE.OUTGOING; }); const maxUnreadCount = Math.min(unreadCount, 3); - const allUnreadMessages = unreadAgentMessages.splice(-maxUnreadCount); - return allUnreadMessages; + return unreadAgentMessages.splice(-maxUnreadCount); }, }; diff --git a/app/javascript/widget/store/modules/conversation/helpers.js b/app/javascript/widget/store/modules/conversation/helpers.js index 44e2a6729..2ebac5242 100644 --- a/app/javascript/widget/store/modules/conversation/helpers.js +++ b/app/javascript/widget/store/modules/conversation/helpers.js @@ -29,7 +29,7 @@ const shouldShowAvatar = (message, nextMessage) => { export const groupConversationBySender = conversationsForADate => conversationsForADate.map((message, index) => { - let showAvatar = false; + let showAvatar; const isLastMessage = index === conversationsForADate.length - 1; if (isASubmittedFormMessage(message)) { showAvatar = false; diff --git a/app/javascript/widget/store/modules/conversation/mutations.js b/app/javascript/widget/store/modules/conversation/mutations.js index f47971f07..ca6dafada 100644 --- a/app/javascript/widget/store/modules/conversation/mutations.js +++ b/app/javascript/widget/store/modules/conversation/mutations.js @@ -88,8 +88,7 @@ export const mutations = { }, toggleAgentTypingStatus($state, { status }) { - const isTyping = status === 'on'; - $state.uiFlags.isAgentTyping = isTyping; + $state.uiFlags.isAgentTyping = status === 'on'; }, setMetaUserLastSeenAt($state, lastSeen) { diff --git a/app/javascript/widget/store/modules/conversationLabels.js b/app/javascript/widget/store/modules/conversationLabels.js index 3fbcd230d..3ae600082 100644 --- a/app/javascript/widget/store/modules/conversationLabels.js +++ b/app/javascript/widget/store/modules/conversationLabels.js @@ -9,14 +9,14 @@ export const actions = { try { await conversationLabels.create(label); } catch (error) { - // Ingore error + // Ignore error } }, destroy: async (_, label) => { try { await conversationLabels.destroy(label); } catch (error) { - // Ingore error + // Ignore error } }, }; From db53af91e740f9a0efecf855d9da63250d1be4d2 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Fri, 14 Oct 2022 12:46:41 -0700 Subject: [PATCH 07/35] chore (#5636) * New translations conversation.json (Latvian) * New translations bulkActions.json (Portuguese, Brazilian) * New translations agentMgmt.json (Latvian) * New translations teamsSettings.json (Latvian) * New translations contactFilters.json (Latvian) * New translations automation.json (Latvian) * New translations attributesMgmt.json (Latvian) * New translations labelsMgmt.json (Latvian) * New translations settings.json (Latvian) * New translations integrations.json (Latvian) * New translations inboxMgmt.json (Latvian) * New translations generalSettings.json (Latvian) * New translations contact.json (Latvian) * New translations helpCenter.json (Latvian) --- app/javascript/dashboard/i18n/locale/lv/agentMgmt.json | 6 +++--- .../dashboard/i18n/locale/lv/attributesMgmt.json | 2 +- .../dashboard/i18n/locale/lv/automation.json | 2 +- app/javascript/dashboard/i18n/locale/lv/contact.json | 2 +- .../dashboard/i18n/locale/lv/contactFilters.json | 2 +- .../dashboard/i18n/locale/lv/conversation.json | 2 +- .../dashboard/i18n/locale/lv/generalSettings.json | 2 +- .../dashboard/i18n/locale/lv/helpCenter.json | 10 +++++----- app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json | 6 ++++-- .../dashboard/i18n/locale/lv/integrations.json | 4 ++-- .../dashboard/i18n/locale/lv/labelsMgmt.json | 2 +- app/javascript/dashboard/i18n/locale/lv/settings.json | 2 ++ .../dashboard/i18n/locale/lv/teamsSettings.json | 2 +- .../dashboard/i18n/locale/pt_BR/bulkActions.json | 4 +++- 14 files changed, 27 insertions(+), 21 deletions(-) diff --git a/app/javascript/dashboard/i18n/locale/lv/agentMgmt.json b/app/javascript/dashboard/i18n/locale/lv/agentMgmt.json index 5b4319962..933a88bab 100644 --- a/app/javascript/dashboard/i18n/locale/lv/agentMgmt.json +++ b/app/javascript/dashboard/i18n/locale/lv/agentMgmt.json @@ -12,12 +12,12 @@ "404": "Šim kontam nav piesaistīts neviens aģents", "TITLE": "Pārvaldīt Jūsu komandas aģentus", "DESC": "Jūs varat pievienot/noņemt aģentus pie/no savas komandas.", - "NAME": "Vārds", - "EMAIL": "e-pasts", + "NAME": "Nosaukums", + "EMAIL": "Epasts", "STATUS": "Statuss", "ACTIONS": "Darbības", "VERIFIED": "Pārbaudīts", - "VERIFICATION_PENDING": "Gaida apstiprinājumu" + "VERIFICATION_PENDING": "Tiek gaidīta verifikācija" }, "ADD": { "TITLE": "Pievienot aģentu Jūsu komandai", diff --git a/app/javascript/dashboard/i18n/locale/lv/attributesMgmt.json b/app/javascript/dashboard/i18n/locale/lv/attributesMgmt.json index 716f98705..ef12fa631 100644 --- a/app/javascript/dashboard/i18n/locale/lv/attributesMgmt.json +++ b/app/javascript/dashboard/i18n/locale/lv/attributesMgmt.json @@ -81,7 +81,7 @@ }, "LIST": { "TABLE_HEADER": [ - "Vārds", + "Nosaukums", "Apraksts", "Tips", "Atslēga" diff --git a/app/javascript/dashboard/i18n/locale/lv/automation.json b/app/javascript/dashboard/i18n/locale/lv/automation.json index a7edf9c25..84d8beedb 100644 --- a/app/javascript/dashboard/i18n/locale/lv/automation.json +++ b/app/javascript/dashboard/i18n/locale/lv/automation.json @@ -40,7 +40,7 @@ }, "LIST": { "TABLE_HEADER": [ - "Vārds", + "Nosaukums", "Apraksts", "Aktīvs", "Izveidots" diff --git a/app/javascript/dashboard/i18n/locale/lv/contact.json b/app/javascript/dashboard/i18n/locale/lv/contact.json index 2e167ae2c..ffe81f9af 100644 --- a/app/javascript/dashboard/i18n/locale/lv/contact.json +++ b/app/javascript/dashboard/i18n/locale/lv/contact.json @@ -202,7 +202,7 @@ "404": "Neviena kontaktpersona neatbilst jūsu meklēšanas vaicājumam 🔍", "NO_CONTACTS": "Nav pieejamu kontaktpersonu", "TABLE_HEADER": { - "NAME": "Vārds", + "NAME": "Nosaukums", "PHONE_NUMBER": "Telefona Numurs", "CONVERSATIONS": "Sarunas", "LAST_ACTIVITY": "Pēdējās Darbības", diff --git a/app/javascript/dashboard/i18n/locale/lv/contactFilters.json b/app/javascript/dashboard/i18n/locale/lv/contactFilters.json index e86ac2152..5ec1c3f4b 100644 --- a/app/javascript/dashboard/i18n/locale/lv/contactFilters.json +++ b/app/javascript/dashboard/i18n/locale/lv/contactFilters.json @@ -26,7 +26,7 @@ "days_before": "Ir x dienas pirms" }, "ATTRIBUTES": { - "NAME": "Vārds", + "NAME": "Nosaukums", "EMAIL": "E-pasts", "PHONE_NUMBER": "Tālruņa numurs", "IDENTIFIER": "Identifikators", diff --git a/app/javascript/dashboard/i18n/locale/lv/conversation.json b/app/javascript/dashboard/i18n/locale/lv/conversation.json index bb6274007..088a5e8bb 100644 --- a/app/javascript/dashboard/i18n/locale/lv/conversation.json +++ b/app/javascript/dashboard/i18n/locale/lv/conversation.json @@ -151,7 +151,7 @@ "CONTEXT_MENU": { "COPY": "Kopēt", "DELETE": "Dzēst", - "CREATE_A_CANNED_RESPONSE": "Add to canned responses" + "CREATE_A_CANNED_RESPONSE": "Pievienot sagatavotajām atbildēm" } }, "EMAIL_TRANSCRIPT": { diff --git a/app/javascript/dashboard/i18n/locale/lv/generalSettings.json b/app/javascript/dashboard/i18n/locale/lv/generalSettings.json index f2a3c3b6f..5e5e39a98 100644 --- a/app/javascript/dashboard/i18n/locale/lv/generalSettings.json +++ b/app/javascript/dashboard/i18n/locale/lv/generalSettings.json @@ -71,7 +71,7 @@ "LOADING_MESSAGE": "Notiek paziņojumu ielāde...", "404": "Nav paziņojumu", "TABLE_HEADER": [ - "Vārds", + "Nosaukums", "Telefona numurs", "Sarunas", "Pēdējā Sazināšanās" diff --git a/app/javascript/dashboard/i18n/locale/lv/helpCenter.json b/app/javascript/dashboard/i18n/locale/lv/helpCenter.json index c877bdb20..39705cf26 100644 --- a/app/javascript/dashboard/i18n/locale/lv/helpCenter.json +++ b/app/javascript/dashboard/i18n/locale/lv/helpCenter.json @@ -92,7 +92,7 @@ "PORTAL_CONFIG": { "TITLE": "Portāla Konfigurācijas", "ITEMS": { - "NAME": "Vārds", + "NAME": "Nosaukums", "DOMAIN": "Pielāgots domēns", "SLUG": "Slug", "TITLE": "Portāla nosaukums", @@ -144,7 +144,7 @@ "TITLE": "Kategorijas iekš", "NEW_CATEGORY": "Jauna kategorija", "TABLE": { - "NAME": "Vārds", + "NAME": "Nosaukums", "DESCRIPTION": "Apraksts", "LOCALE": "Lokalizācija", "ARTICLE_COUNT": "Rakstu skaits", @@ -204,7 +204,7 @@ "HELP_TEXT": "Šis logotips tiks attēlots portāla galvenē." }, "NAME": { - "LABEL": "Vārds", + "LABEL": "Nosaukums", "PLACEHOLDER": "Portāla nosaukums", "HELP_TEXT": "Nosaukums tiks izmantots publiskajā portālā iekšēji.", "ERROR": "Nepieciešams nosaukums" @@ -344,7 +344,7 @@ "PORTAL": "Portāls", "LOCALE": "Lokalizācija", "NAME": { - "LABEL": "Vārds", + "LABEL": "Nosaukums", "PLACEHOLDER": "Kategorijas nosaukums", "HELP_TEXT": "Kategorijas nosaukums tiks izmantots publiskajā portālā, lai klasificētu rakstus.", "ERROR": "Nepieciešams nosaukums" @@ -375,7 +375,7 @@ "PORTAL": "Portāls", "LOCALE": "Lokalizācija", "NAME": { - "LABEL": "Vārds", + "LABEL": "Nosaukums", "PLACEHOLDER": "Kategorijas nosaukums", "HELP_TEXT": "Kategorijas nosaukums tiks izmantots publiskajā portālā, lai klasificētu rakstus.", "ERROR": "Nepieciešams nosaukums" diff --git a/app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json index 12f32f6f0..f62035e3c 100644 --- a/app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json @@ -239,7 +239,9 @@ }, "API_CALLBACK": { "TITLE": "Atzvanīšanas URL", - "SUBTITLE": "Jums ir facebook izstrādātāju portālā jānokonfigurē webhook URL, izmantojot šeit minēto URL." + "SUBTITLE": "Jums Facebook izstrādātāju portālā ir jānokonfigurē webhoot URL un verifikācijas token ar tālāk norādītajām vērtībām.", + "WEBHOOK_URL": "Webhook URL", + "WEBHOOK_VERIFICATION_TOKEN": "Webhook Verifikācijas Token" }, "SUBMIT_BUTTON": "Izveidot WhatsApp kanālu", "API": { @@ -357,7 +359,7 @@ }, "FINISH": { "TITLE": "Jūsu Iesūtne ir gatava!", - "MESSAGE": "Tagad Jūs varat izmantot savu jauno Kanālu lai sazinātos ar saviem klientiem. Priecīgu atbalstīšanu ", + "MESSAGE": "Tagad Jūs varat izmantot savu jauno Kanālu lai sazinātos ar saviem klientiem. Priecīgu atbalstīšanu", "BUTTON_TEXT": "Iet uz", "MORE_SETTINGS": "Papildu iestatījumi", "WEBSITE_SUCCESS": "Jūs esat veiksmīgi pabeidzis tīmekļa vietnes kanāla izveidi. Nokopējiet tālāk redzamo kodu un ievietojiet to savā tīmekļa vietnē. Nākamreiz, kad klients izmantos tiešsaistes tērzēšanu, saruna automātiski tiks parādīta Jūsu iesūtnē." diff --git a/app/javascript/dashboard/i18n/locale/lv/integrations.json b/app/javascript/dashboard/i18n/locale/lv/integrations.json index 372061132..48752ccce 100644 --- a/app/javascript/dashboard/i18n/locale/lv/integrations.json +++ b/app/javascript/dashboard/i18n/locale/lv/integrations.json @@ -94,14 +94,14 @@ "404": "Šajā kontā vēl nav nokonfigurēta neviena informācijas paneļa lietotne", "LOADING": "Notiek informācijas paneļa lietotņu iegūšana...", "TABLE_HEADER": [ - "Vārds", + "Nosaukums", "Endpoint" ], "EDIT_TOOLTIP": "Rediģēt lietotni", "DELETE_TOOLTIP": "Dzēst lietotni" }, "FORM": { - "TITLE_LABEL": "Vārds", + "TITLE_LABEL": "Nosaukums", "TITLE_PLACEHOLDER": "Ievadiet informācijas paneļa lietotnes nosaukumu", "TITLE_ERROR": "Informācijas paneļa lietotnei ir jānorāda nosaukums", "URL_LABEL": "Endpoint", diff --git a/app/javascript/dashboard/i18n/locale/lv/labelsMgmt.json b/app/javascript/dashboard/i18n/locale/lv/labelsMgmt.json index 81b975bbc..011b780c1 100644 --- a/app/javascript/dashboard/i18n/locale/lv/labelsMgmt.json +++ b/app/javascript/dashboard/i18n/locale/lv/labelsMgmt.json @@ -10,7 +10,7 @@ "TITLE": "Pārvaldīt Etiķetes", "DESC": "Etiķetes ļauj grupēt sarunas kopā.", "TABLE_HEADER": [ - "Vārds", + "Nosaukums", "Apraksts", "Krāsa" ] diff --git a/app/javascript/dashboard/i18n/locale/lv/settings.json b/app/javascript/dashboard/i18n/locale/lv/settings.json index effdabc47..e2b82e781 100644 --- a/app/javascript/dashboard/i18n/locale/lv/settings.json +++ b/app/javascript/dashboard/i18n/locale/lv/settings.json @@ -179,6 +179,7 @@ "CONTACTS": "Kontaktpersonas", "HOME": "Sākums", "AGENTS": "Aģenti", + "AGENT_BOTS": "Bots", "INBOXES": "Iesūtnes", "NOTIFICATIONS": "Paziņojumi", "CANNED_RESPONSES": "Sagatavotās Atbildes", @@ -189,6 +190,7 @@ "LABELS": "Etiķetes", "CUSTOM_ATTRIBUTES": "Pielāgotas Īpašības", "AUTOMATION": "Automatizācija", + "MACROS": "Macros", "TEAMS": "Komandas", "BILLING": "Norēķini", "CUSTOM_VIEWS_FOLDER": "Mapes", diff --git a/app/javascript/dashboard/i18n/locale/lv/teamsSettings.json b/app/javascript/dashboard/i18n/locale/lv/teamsSettings.json index 960b5b26d..8d6f4b4da 100644 --- a/app/javascript/dashboard/i18n/locale/lv/teamsSettings.json +++ b/app/javascript/dashboard/i18n/locale/lv/teamsSettings.json @@ -69,7 +69,7 @@ }, "AGENTS": { "AGENT": "AĢENTS", - "EMAIL": "e-pasts", + "EMAIL": "Epasts", "BUTTON_TEXT": "Pievienot aģentus", "ADD_AGENTS": "Notiek aģentu pievienošana Jūsu komandai...", "SELECT": "izvēlēties", diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/bulkActions.json b/app/javascript/dashboard/i18n/locale/pt_BR/bulkActions.json index f3f54ea9f..caf0ed3df 100644 --- a/app/javascript/dashboard/i18n/locale/pt_BR/bulkActions.json +++ b/app/javascript/dashboard/i18n/locale/pt_BR/bulkActions.json @@ -2,9 +2,11 @@ "BULK_ACTION": { "CONVERSATIONS_SELECTED": "%{conversationCount} conversas selecionadas", "AGENT_SELECT_LABEL": "Selecione Agente", - "ASSIGN_CONFIRMATION_LABEL": "Você tem certeza que deseja atribuir %{conversationCount} %{conversationLabel} para", + "ASSIGN_CONFIRMATION_LABEL": "Você tem certeza que quer atribuir %{conversationCount} %{conversationLabel} para", + "UNASSIGN_CONFIRMATION_LABEL": "Você tem certeza que quer remover a atribuição de %{conversationCount} %{conversationLabel}?", "GO_BACK_LABEL": "Voltar atrás", "ASSIGN_LABEL": "Atribua", + "YES": "Sim", "ASSIGN_AGENT_TOOLTIP": "Atribuir Agente", "ASSIGN_SUCCESFUL": "Conversas atribuídas com sucesso", "ASSIGN_FAILED": "Falha ao atribuir conversas, por favor, tente novamente", From ce3730d64078da7546a22db0a2749e0a84875b89 Mon Sep 17 00:00:00 2001 From: Simon Pankovski <46830637+simonpankovski@users.noreply.github.com> Date: Fri, 14 Oct 2022 22:38:08 +0200 Subject: [PATCH 08/35] fix: Avoid email icon getting distorted (#5633) Co-authored-by: Pranav Raj S --- .../routes/dashboard/conversation/contact/ContactInfoRow.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfoRow.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfoRow.vue index 33559e123..4e33bc7fa 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfoRow.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfoRow.vue @@ -10,10 +10,11 @@ From 252eda14c6e62cb8f91a8f7337d2dc05dc550bdf Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Mon, 17 Oct 2022 05:18:32 +0530 Subject: [PATCH 09/35] chore: Add woot button to message context menu (#5638) --- .../components/widgets/conversation/Message.vue | 2 ++ .../conversations/components/MessageContextMenu.vue | 12 +++++++----- .../shared/components/ui/dropdown/DropdownItem.vue | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/javascript/dashboard/components/widgets/conversation/Message.vue b/app/javascript/dashboard/components/widgets/conversation/Message.vue index 3481125ca..7fb24301b 100644 --- a/app/javascript/dashboard/components/widgets/conversation/Message.vue +++ b/app/javascript/dashboard/components/widgets/conversation/Message.vue @@ -420,6 +420,8 @@ export default { diff --git a/app/javascript/shared/components/ui/dropdown/DropdownItem.vue b/app/javascript/shared/components/ui/dropdown/DropdownItem.vue index c4da84234..893026451 100644 --- a/app/javascript/shared/components/ui/dropdown/DropdownItem.vue +++ b/app/javascript/shared/components/ui/dropdown/DropdownItem.vue @@ -35,6 +35,7 @@ export default { a, .button { display: inline-flex; + white-space: nowrap; width: 100%; text-align: left; color: var(--s-700); From 706ab872f36ffb116f2bb37eea840f6c30789ffa Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Mon, 17 Oct 2022 20:59:17 +0530 Subject: [PATCH 10/35] fix: Disable name in pre-chat form if the name is already provided in `setUser` (#5466) --- .../widget/components/PreChat/Form.vue | 20 +++++++++++++++---- .../api/v1/widget/contacts/show.json.jbuilder | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/javascript/widget/components/PreChat/Form.vue b/app/javascript/widget/components/PreChat/Form.vue index 69e543b21..1649068c6 100644 --- a/app/javascript/widget/components/PreChat/Form.vue +++ b/app/javascript/widget/components/PreChat/Form.vue @@ -117,11 +117,23 @@ export default { filteredPreChatFields() { const isUserEmailAvailable = !!this.currentUser.email; const isUserPhoneNumberAvailable = !!this.currentUser.phone_number; + const isUserIdentifierAvailable = !!this.currentUser.identifier; + const isUserNameAvailable = !!( + isUserIdentifierAvailable || + isUserEmailAvailable || + isUserPhoneNumberAvailable + ); return this.preChatFields.filter(field => { - return !( - (isUserEmailAvailable && field.name === 'emailAddress') || - (isUserPhoneNumberAvailable && field.name === 'phoneNumber') - ); + if (isUserEmailAvailable && field.name === 'emailAddress') { + return false; + } + if (isUserPhoneNumberAvailable && field.name === 'phoneNumber') { + return false; + } + if (isUserNameAvailable && field.name === 'fullName') { + return false; + } + return true; }); }, enabledPreChatFields() { diff --git a/app/views/api/v1/widget/contacts/show.json.jbuilder b/app/views/api/v1/widget/contacts/show.json.jbuilder index d6228cbfe..2e7a38277 100644 --- a/app/views/api/v1/widget/contacts/show.json.jbuilder +++ b/app/views/api/v1/widget/contacts/show.json.jbuilder @@ -2,3 +2,4 @@ json.id @contact.id json.name @contact.name json.email @contact.email json.phone_number @contact.phone_number +json.identifier @contact.identifier From 704554d453455bcec78ad2d5338a6e596c698794 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 17 Oct 2022 11:14:29 -0700 Subject: [PATCH 11/35] chore: Update translations (#5644) --- .../dashboard/i18n/locale/el/agentBots.json | 5 + .../dashboard/i18n/locale/el/bulkActions.json | 2 + .../dashboard/i18n/locale/el/contact.json | 4 +- .../i18n/locale/el/contactFilters.json | 2 +- .../i18n/locale/el/conversation.json | 40 +- .../dashboard/i18n/locale/el/helpCenter.json | 372 +++++++++--------- .../dashboard/i18n/locale/el/inboxMgmt.json | 52 +-- .../i18n/locale/el/integrations.json | 46 +-- .../dashboard/i18n/locale/el/macros.json | 5 + .../dashboard/i18n/locale/el/settings.json | 50 +-- .../dashboard/i18n/locale/he/chatlist.json | 4 +- .../dashboard/i18n/locale/he/contact.json | 8 +- .../i18n/locale/he/conversation.json | 60 +-- .../i18n/locale/he/generalSettings.json | 18 +- .../dashboard/i18n/locale/he/inboxMgmt.json | 8 +- .../dashboard/i18n/locale/he/settings.json | 4 +- config/locales/el.yml | 10 +- 17 files changed, 355 insertions(+), 335 deletions(-) create mode 100644 app/javascript/dashboard/i18n/locale/el/agentBots.json create mode 100644 app/javascript/dashboard/i18n/locale/el/macros.json diff --git a/app/javascript/dashboard/i18n/locale/el/agentBots.json b/app/javascript/dashboard/i18n/locale/el/agentBots.json new file mode 100644 index 000000000..708b82173 --- /dev/null +++ b/app/javascript/dashboard/i18n/locale/el/agentBots.json @@ -0,0 +1,5 @@ +{ + "AGENT_BOTS": { + "HEADER": "Bots" + } +} diff --git a/app/javascript/dashboard/i18n/locale/el/bulkActions.json b/app/javascript/dashboard/i18n/locale/el/bulkActions.json index acf4a2d10..1593857a6 100644 --- a/app/javascript/dashboard/i18n/locale/el/bulkActions.json +++ b/app/javascript/dashboard/i18n/locale/el/bulkActions.json @@ -3,8 +3,10 @@ "CONVERSATIONS_SELECTED": "%{conversationCount} σινομιλίες επιλέχθηκαν", "AGENT_SELECT_LABEL": "Επιλογή πράκτορα", "ASSIGN_CONFIRMATION_LABEL": "Είσαστε σίγουροι ότι θέλετε να αντιστοιχίσετε %{conversationCount} %{conversationLabel} στον", + "UNASSIGN_CONFIRMATION_LABEL": "Είσαστε σίγουροι ότι θέλετε να αφαιρέσετε την αντιστοίχιση %{conversationCount} %{conversationLabel} στον;", "GO_BACK_LABEL": "Πίσω", "ASSIGN_LABEL": "Αντιστοίχιση", + "YES": "Ναι", "ASSIGN_AGENT_TOOLTIP": "Ανάθεση σε πράκτορα", "ASSIGN_SUCCESFUL": "Οι σινομιλίες αντιστοιχήθηκαν επιτυχώς", "ASSIGN_FAILED": "Αποτυχία στην αντιστοίχιση σινομιλιών, παρακαλώ δοκιμάστε αργότερα", diff --git a/app/javascript/dashboard/i18n/locale/el/contact.json b/app/javascript/dashboard/i18n/locale/el/contact.json index fff494d7e..dc65bd7b9 100644 --- a/app/javascript/dashboard/i18n/locale/el/contact.json +++ b/app/javascript/dashboard/i18n/locale/el/contact.json @@ -152,8 +152,8 @@ }, "DELETE_AVATAR": { "API": { - "SUCCESS_MESSAGE": "Contact avatar deleted successfully", - "ERROR_MESSAGE": "Could not delete the contact avatar. Please try again later." + "SUCCESS_MESSAGE": "Το avatar της επαφής διαγράφηκε επιτυχώς", + "ERROR_MESSAGE": "Δεν ήταν δυνατή η διαγραφή του avatar της επαφής. Παρακαλώ προσπαθήστε ξανά αργότερα." } }, "SUCCESS_MESSAGE": "Η επαφή αποθηκεύτηκε με επιτυχία", diff --git a/app/javascript/dashboard/i18n/locale/el/contactFilters.json b/app/javascript/dashboard/i18n/locale/el/contactFilters.json index 7e897fe15..1bcbee2a5 100644 --- a/app/javascript/dashboard/i18n/locale/el/contactFilters.json +++ b/app/javascript/dashboard/i18n/locale/el/contactFilters.json @@ -39,7 +39,7 @@ "CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox", "CREATED_AT": "Δημιουργήθηκε στις", "LAST_ACTIVITY": "Τελευταία Δραστηριότητα", - "REFERER_LINK": "Referrer link" + "REFERER_LINK": "Σύνδεσμος αναφοράς" }, "GROUPS": { "STANDARD_FILTERS": "Τυπικά Φίλτρα", diff --git a/app/javascript/dashboard/i18n/locale/el/conversation.json b/app/javascript/dashboard/i18n/locale/el/conversation.json index c84a077ef..9bd7b8208 100644 --- a/app/javascript/dashboard/i18n/locale/el/conversation.json +++ b/app/javascript/dashboard/i18n/locale/el/conversation.json @@ -2,8 +2,8 @@ "CONVERSATION": { "SELECT_A_CONVERSATION": "Παρακαλώ επιλέξτε συζήτηση από το αριστερό τμήμα", "CSAT_REPLY_MESSAGE": "Παρακαλώ αξιολογήστε τη συνομιλία", - "404": "Sorry, we cannot find the conversation. Please try again", - "SWITCH_VIEW_LAYOUT": "Switch the layout", + "404": "Λυπούμαστε, δεν μπορούμε να βρούμε την συνομιλία. Παρακαλώ προσπαθήστε ξανά", + "SWITCH_VIEW_LAYOUT": "Εναλλαγή διάταξης", "DASHBOARD_APP_TAB_MESSAGES": "Μηνύματα", "UNVERIFIED_SESSION": "Η ταυτότητα αυτού του χρήστη δεν επαληθεύεται", "NO_MESSAGE_1": "Ωχ ωχ! Φαίνεται ότι δεν υπάρχουν μηνύματα από τους πελάτες στα εισερχόμενά σας.", @@ -63,30 +63,30 @@ }, "CARD_CONTEXT_MENU": { "PENDING": "Σήμανση ως εκκρεμής", - "RESOLVED": "Mark as resolved", + "RESOLVED": "Σήμανση ως επιλυμένου", "REOPEN": "Άνοιγμα συνομιλίας", "SNOOZE": { - "TITLE": "Snooze", + "TITLE": "Αναβολή", "NEXT_REPLY": "Μέχρι την επόμενη απάντηση", "TOMORROW": "Μέχρι αύριο", "NEXT_WEEK": "Έως την επόμενη εβδομάδα" }, - "ASSIGN_AGENT": "Assign agent", - "ASSIGN_LABEL": "Assign label", - "AGENTS_LOADING": "Loading agents...", - "ASSIGN_TEAM": "Assign team", + "ASSIGN_AGENT": "Ανάθεση σε πράκτορα", + "ASSIGN_LABEL": "Εκχώρηση ετικέτας", + "AGENTS_LOADING": "Φόρτωση πρακτόρων...", + "ASSIGN_TEAM": "Ανάθεση ομάδας", "API": { "AGENT_ASSIGNMENT": { - "SUCCESFUL": "Conversation id %{conversationId} assigned to \"%{agentName}\"", - "FAILED": "Couldn't assign agent. Please try again." + "SUCCESFUL": "Η συνομιλία με αριθμό %{conversationId} ανατέθηκε στον \"%{agentName}\"", + "FAILED": "Αδυναμία αντιστοίχισης σε πράκτορα. Παρακαλώ δοκιμάστε ξανά." }, "LABEL_ASSIGNMENT": { - "SUCCESFUL": "Assigned label #%{labelName} to conversation id %{conversationId}", - "FAILED": "Couldn't assign label. Please try again." + "SUCCESFUL": "Εκχώρηση ετικέτας #%{labelName} στην συνομιλία με αριθμό %{conversationId}", + "FAILED": "Αποτυχία στην εκχώρηση ετικέτας, παρακαλώ δοκιμάστε αργότερα." }, "TEAM_ASSIGNMENT": { - "SUCCESFUL": "Assigned team \"%{team}\" to conversation id %{conversationId}", - "FAILED": "Couldn't assign team. Please try again." + "SUCCESFUL": "Η συνομιλία με αριθμό %{conversationId} ανατέθηκε στην ομάδα \"%{team}\"", + "FAILED": "Αδυναμία αντιστοίχισης ομάδας. Παρακαλώ δοκιμάστε ξανά." } } }, @@ -131,13 +131,13 @@ }, "VISIBLE_TO_AGENTS": "Ιδιωτική Σημείωση: Ορατή μόνο σε σας και την ομάδα σας", "CHANGE_STATUS": "Η κατάσταση της συνομιλίας άλλαξε", - "CHANGE_STATUS_FAILED": "Conversation status change failed", + "CHANGE_STATUS_FAILED": "Η αλλαγή κατάστασης συνομιλίας απέτυχε", "CHANGE_AGENT": "Η εκπροσώπηση για την συνομιλία άλλαξε", - "CHANGE_AGENT_FAILED": "Assignee change failed", - "ASSIGN_LABEL_SUCCESFUL": "Label assigned successfully", - "ASSIGN_LABEL_FAILED": "Label assignment failed", + "CHANGE_AGENT_FAILED": "Η αλλαγή της ανάθεσης απέτυχε", + "ASSIGN_LABEL_SUCCESFUL": "Επιτυχής εκχώρηση ετικέτας", + "ASSIGN_LABEL_FAILED": "Η εκχώρηση ετικέτας απέτυχε", "CHANGE_TEAM": "Η ομάδα συνομιλίας άλλαξε", - "FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE} MB attachment limit", + "FILE_SIZE_LIMIT": "Το αρχείο υπερβαίνει το όριο συνημμένου {MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE}", "MESSAGE_ERROR": "Δεν είναι δυνατή η αποστολή του μηνύματος, παρακαλώ προσπαθήστε ξανά αργότερα", "SENT_BY": "Αποστολή από:", "BOT": "Bot", @@ -151,7 +151,7 @@ "CONTEXT_MENU": { "COPY": "Αντιγραφή", "DELETE": "Διαγραφή", - "CREATE_A_CANNED_RESPONSE": "Add to canned responses" + "CREATE_A_CANNED_RESPONSE": "Προσθήκη στις έτοιμες απαντήσεις" } }, "EMAIL_TRANSCRIPT": { diff --git a/app/javascript/dashboard/i18n/locale/el/helpCenter.json b/app/javascript/dashboard/i18n/locale/el/helpCenter.json index 2f6be5952..859e8373a 100644 --- a/app/javascript/dashboard/i18n/locale/el/helpCenter.json +++ b/app/javascript/dashboard/i18n/locale/el/helpCenter.json @@ -1,409 +1,409 @@ { "HELP_CENTER": { "HEADER": { - "FILTER": "Filter by", - "SORT": "Sort by", + "FILTER": "Φιλτράρισμα κατά", + "SORT": "Ταξινόμηση κατά", "SETTINGS_BUTTON": "Ρυθμίσεις", - "NEW_BUTTON": "New Article", + "NEW_BUTTON": "Νέο Άρθρο", "DROPDOWN_OPTIONS": { - "PUBLISHED": "Published", - "DRAFT": "Draft", - "ARCHIVED": "Archived" + "PUBLISHED": "Δημοσιευμένο", + "DRAFT": "Πρόχειρο", + "ARCHIVED": "Αρχειοθετημένο" }, "TITLES": { - "ALL_ARTICLES": "All Articles", - "MINE": "My Articles", - "DRAFT": "Draft Articles", - "ARCHIVED": "Archived Articles" + "ALL_ARTICLES": "Όλα Τα Άρθρα", + "MINE": "Τα Άρθρα Μου", + "DRAFT": "Πρόχειρα Άρθρα", + "ARCHIVED": "Αρχειοθετημένα Άρθρα" } }, "EDIT_HEADER": { - "ALL_ARTICLES": "All Articles", - "PUBLISH_BUTTON": "Publish", - "MOVE_TO_ARCHIVE_BUTTON": "Move to archived", - "PREVIEW": "Preview", - "ADD_TRANSLATION": "Add translation", - "OPEN_SIDEBAR": "Open sidebar", - "CLOSE_SIDEBAR": "Close sidebar", - "SAVING": "Saving...", - "SAVED": "Saved" + "ALL_ARTICLES": "Όλα Τα Άρθρα", + "PUBLISH_BUTTON": "Δημοσιευμένο", + "MOVE_TO_ARCHIVE_BUTTON": "Μετακίνηση στα αρχειοθετημένα", + "PREVIEW": "Προεπισκόπηση", + "ADD_TRANSLATION": "Προσθήκη μετάφρασης", + "OPEN_SIDEBAR": "Άνοιγμα πλευρικής μπάρας", + "CLOSE_SIDEBAR": "Κλείσιμο πλευρικής μπάρας", + "SAVING": "Αποθηκεύεται...", + "SAVED": "Αποθηκεύτηκε" }, "ARTICLE_SETTINGS": { - "TITLE": "Article Settings", + "TITLE": "Ρυθμίσεις Άρθρου", "FORM": { "CATEGORY": { "LABEL": "Κατηγορία", - "TITLE": "Select category", - "PLACEHOLDER": "Select category", - "NO_RESULT": "No category found", - "SEARCH_PLACEHOLDER": "Search category" + "TITLE": "Επιλογή κατηγορίας", + "PLACEHOLDER": "Επιλογή κατηγορίας", + "NO_RESULT": "Δεν βρέθηκε καμία κατηγορία", + "SEARCH_PLACEHOLDER": "Αναζήτηση κατηγορίας" }, "AUTHOR": { - "LABEL": "Author", - "TITLE": "Select author", - "PLACEHOLDER": "Select author", - "NO_RESULT": "No authors found", - "SEARCH_PLACEHOLDER": "Search author" + "LABEL": "Συγγραφέας", + "TITLE": "Επιλογή συγγραφέα", + "PLACEHOLDER": "Επιλογή συγγραφέα", + "NO_RESULT": "Δεν βρέθηκαν συγγραφείς", + "SEARCH_PLACEHOLDER": "Αναζήτηση συγγραφέα" }, "META_TITLE": { - "LABEL": "Meta title", - "PLACEHOLDER": "Add a meta title" + "LABEL": "Μετα-τίτλος", + "PLACEHOLDER": "Προσθήκη meta Τίτλου" }, "META_DESCRIPTION": { - "LABEL": "Meta description", - "PLACEHOLDER": "Add your meta description for better SEO results..." + "LABEL": "Meta περιγραφή", + "PLACEHOLDER": "Προσθέστε τη meta περιγραφή σας για καλύτερα αποτελέσματα SEO..." }, "META_TAGS": { "LABEL": "Meta tags", - "PLACEHOLDER": "Add meta tags separated by comma..." + "PLACEHOLDER": "Προσθήκη μετα-ετικετών διαχωρισμένων με κόμμα..." } }, "BUTTONS": { - "ARCHIVE": "Archive article", - "DELETE": "Delete article" + "ARCHIVE": "Αρχειοθέτηση άρθρου", + "DELETE": "Διαγραφή άρθρου" } }, "PORTAL": { - "HEADER": "Portals", - "DEFAULT": "Default", - "NEW_BUTTON": "New Portal", + "HEADER": "Πύλες", + "DEFAULT": "Προεπιλογή", + "NEW_BUTTON": "Νέα Πύλη", "ACTIVE_BADGE": "ενεργή", - "CHOOSE_LOCALE_LABEL": "Choose a locale", - "LOADING_MESSAGE": "Loading portals...", - "ARTICLES_LABEL": "articles", - "NO_PORTALS_MESSAGE": "There are no available portals", - "ADD_NEW_LOCALE": "Add a new locale", + "CHOOSE_LOCALE_LABEL": "Επιλέξτε μια γλώσσα", + "LOADING_MESSAGE": "Φόρτωση πυλών...", + "ARTICLES_LABEL": "άρθρα", + "NO_PORTALS_MESSAGE": "Δεν υπάρχουν διαθέσιμες πύλες", + "ADD_NEW_LOCALE": "Προσθέστε μια νέα γλώσσα", "POPOVER": { - "TITLE": "Portals", - "PORTAL_SETTINGS": "Portal settings", - "SUBTITLE": "You have multiple portals and can have different locales for each portal.", + "TITLE": "Πύλες", + "PORTAL_SETTINGS": "Ρυθμίσεις πύλης", + "SUBTITLE": "Έχετε πολλαπλές πύλες με διαφορετικές γλώσσες για κάθε πύλη.", "CANCEL_BUTTON_LABEL": "Άκυρο", - "CHOOSE_LOCALE_BUTTON": "Choose Locale" + "CHOOSE_LOCALE_BUTTON": "Επιλέξτε γλώσσα" }, "PORTAL_SETTINGS": { "LIST_ITEM": { "HEADER": { - "COUNT_LABEL": "articles", - "ADD": "Add locale", - "VISIT": "Visit site", + "COUNT_LABEL": "άρθρα", + "ADD": "Προσθήκη γλώσσας", + "VISIT": "Επίσκεψη site", "SETTINGS": "Ρυθμίσεις", "DELETE": "Διαγραφή" }, "PORTAL_CONFIG": { - "TITLE": "Portal Configurations", + "TITLE": "Ρυθμίσεις Πύλης", "ITEMS": { "NAME": "Όνομα", - "DOMAIN": "Custom domain", + "DOMAIN": "Προσαρμοσμένο Domain", "SLUG": "Slug", - "TITLE": "Portal title", - "THEME": "Theme color", - "SUB_TEXT": "Portal sub text" + "TITLE": "Τίτλος πύλης", + "THEME": "Χρώμα θέματος", + "SUB_TEXT": "Υποκείμενο πύλης" } }, "AVAILABLE_LOCALES": { - "TITLE": "Available locales", + "TITLE": "Διαθέσιμες γλώσσες", "TABLE": { - "NAME": "Locale name", - "CODE": "Locale code", - "ARTICLE_COUNT": "No. of articles", - "CATEGORIES": "No. of categories", - "SWAP": "Swap", + "NAME": "Όνομα γλώσσας", + "CODE": "Κωδικός γλώσσας", + "ARTICLE_COUNT": "Αριθμός άρθρων", + "CATEGORIES": "Αριθμός κατηγοριών", + "SWAP": "Εναλλαγή", "DELETE": "Διαγραφή", - "DEFAULT_LOCALE": "Default" + "DEFAULT_LOCALE": "Προεπιλογή" } } }, "DELETE_PORTAL": { - "TITLE": "Delete portal", - "MESSAGE": "Are you sure you want to delete this portal", - "YES": "Yes, delete portal", - "NO": "No, keep portal", + "TITLE": "Διαγραφή πύλης", + "MESSAGE": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν την πύλη", + "YES": "Ναι, διαγραφή πύλης", + "NO": "Όχι, διατήρηση πύλης", "API": { - "DELETE_SUCCESS": "Portal deleted successfully", - "DELETE_ERROR": "Error while deleting portal" + "DELETE_SUCCESS": "Η πύλη διαγράφηκε επιτυχώς", + "DELETE_ERROR": "Σφάλμα κατά τη διαγραφή της πύλης" } } }, "EDIT": { - "HEADER_TEXT": "Edit portal", + "HEADER_TEXT": "Επεξεργασία πύλης", "TABS": { "BASIC_SETTINGS": { - "TITLE": "Basic information" + "TITLE": "Βασικές πληροφορίες" }, "CUSTOMIZATION_SETTINGS": { - "TITLE": "Portal customization" + "TITLE": "Προσαρμογή πύλης" }, "CATEGORY_SETTINGS": { - "TITLE": "Categories" + "TITLE": "Κατηγορίες" }, "LOCALE_SETTINGS": { - "TITLE": "Locales" + "TITLE": "Γλώσσες" } }, "CATEGORIES": { - "TITLE": "Categories in", - "NEW_CATEGORY": "New category", + "TITLE": "Κατηγορίες στο", + "NEW_CATEGORY": "Νέα κατηγορία", "TABLE": { "NAME": "Όνομα", "DESCRIPTION": "Περιγραφή", - "LOCALE": "Locale", - "ARTICLE_COUNT": "No. of articles", + "LOCALE": "Γλώσσα", + "ARTICLE_COUNT": "Αριθμός άρθρων", "ACTION_BUTTON": { - "EDIT": "Edit category", - "DELETE": "Delete category" + "EDIT": "Επεξεργασία κατηγορίας", + "DELETE": "Διαγραφή κατηγορίας" }, - "EMPTY_TEXT": "No categories found" + "EMPTY_TEXT": "Δεν βρέθηκαν κατηγορίες" } }, "EDIT_BASIC_INFO": { - "BUTTON_TEXT": "Update basic settings" + "BUTTON_TEXT": "Ενημέρωση βασικών ρυθμίσεων" } }, "ADD": { "CREATE_FLOW": [ { - "title": "Help center information", + "title": "Πληροφορίες κέντρου βοήθειας", "route": "new_portal_information", - "body": "Basic information about portal", - "CREATE_BASIC_SETTING_BUTTON": "Create portal basic settings" + "body": "Βασικές πληροφορίες σχετικά με την πύλη", + "CREATE_BASIC_SETTING_BUTTON": "Δημιουργία βασικών ρυθμίσεων πύλης" }, { - "title": "Help center customization", + "title": "Προσαρμογή του κέντρου βοήθειας", "route": "portal_customization", - "body": "Customize portal", - "UPDATE_PORTAL_BUTTON": "Update portal settings" + "body": "Προσαρμογή πύλης", + "UPDATE_PORTAL_BUTTON": "Ενημέρωση ρυθμίσεων πύλης" }, { - "title": "Voila! 🎉", + "title": "Έξοχα! 🎉", "route": "portal_finish", - "body": "You're all set!", + "body": "Είναι όλα έτοιμα!", "FINISH": "Τέλος" } ], "CREATE_FLOW_PAGE": { "BACK_BUTTON": "Πίσω", "BASIC_SETTINGS_PAGE": { - "HEADER": "Create Portal", - "TITLE": "Help center information", - "CREATE_BASIC_SETTING_BUTTON": "Create portal basic settings" + "HEADER": "Δημιουργία Πύλης", + "TITLE": "Πληροφορίες κέντρου βοήθειας", + "CREATE_BASIC_SETTING_BUTTON": "Δημιουργία βασικών ρυθμίσεων πύλης" }, "CUSTOMIZATION_PAGE": { - "HEADER": "Portal customisation", - "TITLE": "Help center customization", - "UPDATE_PORTAL_BUTTON": "Update portal settings" + "HEADER": "Προσαρμογή πύλης", + "TITLE": "Προσαρμογή του κέντρου βοήθειας", + "UPDATE_PORTAL_BUTTON": "Ενημέρωση ρυθμίσεων πύλης" }, "FINISH_PAGE": { - "TITLE": "Voila!🎉 You're all set up!", - "MESSAGE": "You can now see this created portal on your all portals page.", - "FINISH": "Go to all portals page" + "TITLE": "Έξοχα!🎉 Έχετε ρυθμιστεί!", + "MESSAGE": "Τώρα μπορείτε να δείτε την πύλη που δημιουργήθηκε στη σελίδα με όλες τις πύλες.", + "FINISH": "Μετάβαση στις πύλες" } }, "LOGO": { - "LABEL": "Logo", - "UPLOAD_BUTTON": "Upload logo", - "HELP_TEXT": "This logo will be displayed on the portal header." + "LABEL": "Λογότυπο", + "UPLOAD_BUTTON": "Μεταφόρτωση λογότυπου", + "HELP_TEXT": "Το λογότυπο θα εμφανιστεί στην κεφαλίδα της πύλης." }, "NAME": { "LABEL": "Όνομα", - "PLACEHOLDER": "Portal name", - "HELP_TEXT": "The name will be used in the public facing portal internally.", + "PLACEHOLDER": "Όνομα πύλης", + "HELP_TEXT": "Το όνομα θα χρησιμοποιηθεί στο κοινό που βλέπει την πύλη εσωτερικά.", "ERROR": "Απαιτείται όνομα" }, "SLUG": { "LABEL": "Slug", - "PLACEHOLDER": "Portal slug for urls", - "ERROR": "Slug is required" + "PLACEHOLDER": "Slug Πύλης για τα urls", + "ERROR": "Το Slug είναι απαραίτητο" }, "DOMAIN": { - "LABEL": "Custom Domain", - "PLACEHOLDER": "Portal custom domain", - "HELP_TEXT": "Add only If you want to use a custom domain for your portals.", - "ERROR": "Custom Domain is required" + "LABEL": "Προσαρμοσμένο Domain", + "PLACEHOLDER": "Προσαρμοσμένος τομέας πύλης", + "HELP_TEXT": "Προσθήκη μόνο Αν θέλετε να χρησιμοποιήσετε ένα προσαρμοσμένο τομέα για τις πύλες σας. (Automatic Translation).", + "ERROR": "Ο Προσαρμοσμένος Τομέας απαιτείται" }, "HOME_PAGE_LINK": { - "LABEL": "Home Page Link", - "PLACEHOLDER": "Portal home page link", - "HELP_TEXT": "The link used to return from the portal to the home page.", - "ERROR": "Home Page Link is required" + "LABEL": "Σύνδεσμος Αρχικής Σελίδας", + "PLACEHOLDER": "Σύνδεσμος αρχικής σελίδας πύλης", + "HELP_TEXT": "Ο σύνδεσμος που χρησιμοποιείται για την επιστροφή από την πύλη στην αρχική σελίδα.", + "ERROR": "Ο σύνδεσμος αρχικής σελίδας απαιτείται" }, "THEME_COLOR": { - "LABEL": "Portal theme color", - "HELP_TEXT": "This color will show as the theme color for the portal." + "LABEL": "Χρώμα θέματος πύλης", + "HELP_TEXT": "Αυτό το χρώμα θα εμφανίζεται ως το χρώμα θέματος της πύλης. " }, "PAGE_TITLE": { - "LABEL": "Page Title", - "PLACEHOLDER": "Portal page title", - "HELP_TEXT": "The page title will be used in the public facing portal.", - "ERROR": "Page title is required" + "LABEL": "Τίτλος Σελίδας", + "PLACEHOLDER": "Τίτλος σελίδας πύλης", + "HELP_TEXT": "Ο τίτλος της σελίδας θα χρησιμοποιηθεί στην πύλη που βλέπει το κοινό.", + "ERROR": "Ο τίτλος είναι απαραίτητος" }, "HEADER_TEXT": { - "LABEL": "Header Text", - "PLACEHOLDER": "Portal header text", - "HELP_TEXT": "The Portal header text will be used in the public facing portal.", - "ERROR": "Portal header text is required" + "LABEL": "Κείμενο Κεφαλίδας", + "PLACEHOLDER": "Κείμενο κεφαλίδας πύλης", + "HELP_TEXT": "Το κείμενο κεφαλίδας πύλης θα χρησιμοποιηθεί στο κοινό που βλέπει πύλη.", + "ERROR": "Απαιτείται κείμενο κεφαλίδας πύλης" }, "API": { - "SUCCESS_MESSAGE_FOR_BASIC": "Portal created successfully.", - "ERROR_MESSAGE_FOR_BASIC": "Couldn't create the portal. Try again.", - "SUCCESS_MESSAGE_FOR_UPDATE": "Portal updated successfully.", - "ERROR_MESSAGE_FOR_UPDATE": "Couldn't update the portal. Try again." + "SUCCESS_MESSAGE_FOR_BASIC": "Ο φάκελος δημιουργήθηκε με επιτυχία.", + "ERROR_MESSAGE_FOR_BASIC": "Δεν ήταν δυνατή η δημιουργία της πύλης. Δοκιμάστε ξανά.", + "SUCCESS_MESSAGE_FOR_UPDATE": "Η πύλη ενημερώθηκε με επιτυχία.", + "ERROR_MESSAGE_FOR_UPDATE": "Δεν ήταν δυνατή η ενημέρωση της πύλης. Δοκιμάστε ξανά." } }, "ADD_LOCALE": { - "TITLE": "Add a new locale", - "SUB_TITLE": "This adds a new locale to your available translation list.", - "PORTAL": "Portal", + "TITLE": "Προσθέστε μια νέα γλώσσα", + "SUB_TITLE": "Προσθέτει μια νέα γλώσσα στη διαθέσιμη λίστα μεταφράσεών σας.", + "PORTAL": "Πύλη", "LOCALE": { - "LABEL": "Locale", - "PLACEHOLDER": "Choose a locale", - "ERROR": "Locale is required" + "LABEL": "Γλώσσα", + "PLACEHOLDER": "Επιλέξτε μια γλώσσα", + "ERROR": "Η γλώσσα απαιτείται" }, "BUTTONS": { - "CREATE": "Create locale", + "CREATE": "Δημιουργία γλώσσας", "CANCEL": "Άκυρο" }, "API": { - "SUCCESS_MESSAGE": "Locale added successfully", - "ERROR_MESSAGE": "Unable to add locale. Try again." + "SUCCESS_MESSAGE": "Η γλώσσα προστέθηκε επιτυχώς", + "ERROR_MESSAGE": "Δεν είναι δυνατή η προσθήκη γλώσσας. Δοκιμάστε ξανά." } }, "CHANGE_DEFAULT_LOCALE": { "API": { - "SUCCESS_MESSAGE": "Default locale updated successfully", - "ERROR_MESSAGE": "Unable to update default locale. Try again." + "SUCCESS_MESSAGE": "Η προεπιλεγμένη γλώσσα ενημερώθηκε επιτυχώς", + "ERROR_MESSAGE": "Δεν είναι δυνατή η ενημέρωση της προεπιλεγμένης γλώσσας. Δοκιμάστε ξανά." } }, "DELETE_LOCALE": { "API": { - "SUCCESS_MESSAGE": "Locale removed from portal successfully", - "ERROR_MESSAGE": "Unable to remove locale from portal. Try again." + "SUCCESS_MESSAGE": "Η γλώσσα αφαιρέθηκε επιτυχώς από την πύλη", + "ERROR_MESSAGE": "Δεν είναι δυνατή η αφαίρεση γλώσσας από την πύλη. Δοκιμάστε ξανά." } } }, "TABLE": { - "LOADING_MESSAGE": "Loading articles...", - "404": "No articles matches your search 🔍", - "NO_ARTICLES": "There are no available articles", + "LOADING_MESSAGE": "Φόρτωση άρθρων...", + "404": "Δεν υπάρχουν άρθρα που να ταιριάζουν στην αναζήτησή σας 🔍", + "NO_ARTICLES": "Δεν υπάρχουν διαθέσιμα άρθρα", "HEADERS": { "TITLE": "Τίτλος", "CATEGORY": "Κατηγορία", - "READ_COUNT": "Read count", + "READ_COUNT": "Πλήθος ανάγνωσεων", "STATUS": "Κατάσταση", - "LAST_EDITED": "Last edited" + "LAST_EDITED": "Τελευταία επεξεργασία" }, "COLUMNS": { - "BY": "by" + "BY": "από" } }, "EDIT_ARTICLE": { - "LOADING": "Loading article...", - "TITLE_PLACEHOLDER": "Article title goes here", - "CONTENT_PLACEHOLDER": "Write your article here", + "LOADING": "Φόρτωση άρθρου...", + "TITLE_PLACEHOLDER": "Ο τίτλος του άρθρου εμφανίζεται εδώ", + "CONTENT_PLACEHOLDER": "Γράψτε το άρθρο σας εδώ", "API": { - "ERROR": "Error while saving article" + "ERROR": "Σφάλμα κατά την αποθήκευση άρθρου" } }, "PUBLISH_ARTICLE": { "API": { - "ERROR": "Error while publishing article", - "SUCCESS": "Article publishied successfully" + "ERROR": "Σφάλμα κατά τη δημοσίευση του άρθρου", + "SUCCESS": "Το Άρθρο δημοσιεύθηκε με επιτυχία" } }, "ARCHIVE_ARTICLE": { "API": { - "ERROR": "Error while archiving article", - "SUCCESS": "Article archived successfully" + "ERROR": "Σφάλμα κατά την αρχειοθέτηση άρθρου", + "SUCCESS": "Το άρθρο αρχειοθετήθηκε επιτυχώς" } }, "DELETE_ARTICLE": { "MODAL": { "CONFIRM": { "TITLE": "Επιβεβαίωση Διαγραφής", - "MESSAGE": "Are you sure to delete the article?", + "MESSAGE": "Είστε βέβαιοι να διαγράψετε το άρθρο;", "YES": "Ναι, Διέγραψε το", "NO": "Όχι, Κράτησε τον/την" } }, "API": { - "SUCCESS_MESSAGE": "Article deleted successfully", - "ERROR_MESSAGE": "Error while deleting article" + "SUCCESS_MESSAGE": "Η επαφή διαγράφηκε επιτυχώς", + "ERROR_MESSAGE": "Σφάλμα κατά τη διαγραφή άρθρου" } }, "CREATE_ARTICLE": { - "ERROR_MESSAGE": "Please add the article heading and content then only you can update the settings" + "ERROR_MESSAGE": "Παρακαλώ προσθέστε την επικεφαλίδα και το περιεχόμενο του άρθρου για να μπορείτε να ενημερώσετε τις ρυθμίσεις" }, "SIDEBAR": { "SEARCH": { - "PLACEHOLDER": "Search for articles" + "PLACEHOLDER": "Αναζήτηση άρθρων" } }, "CATEGORY": { "ADD": { - "TITLE": "Create a category", - "SUB_TITLE": "The category will be used in the public facing portal to categorize articles.", - "PORTAL": "Portal", - "LOCALE": "Locale", + "TITLE": "Δημιουργία κατηγορίας", + "SUB_TITLE": "Η κατηγορία θα χρησιμοποιηθεί στην πύλη που βλέπει το κοινό για την κατηγοριοποίηση των άρθρων.", + "PORTAL": "Πύλη", + "LOCALE": "Γλώσσα", "NAME": { "LABEL": "Όνομα", - "PLACEHOLDER": "Category name", - "HELP_TEXT": "The category name will be used in the public facing portal to categorize articles.", + "PLACEHOLDER": "Όνομα κατηγορίας", + "HELP_TEXT": "Η κατηγορία θα χρησιμοποιηθεί στην πύλη που βλέπει το κοινό για την κατηγοριοποίηση των άρθρων.", "ERROR": "Απαιτείται όνομα" }, "SLUG": { "LABEL": "Slug", - "PLACEHOLDER": "Category slug for urls", + "PLACEHOLDER": "Slug κατηγορίας για urls", "HELP_TEXT": "app.chatwoot.com/hc/my-portal/en-US/categories/my-slug", - "ERROR": "Slug is required" + "ERROR": "Το Slug είναι απαραίτητο" }, "DESCRIPTION": { "LABEL": "Περιγραφή", - "PLACEHOLDER": "Give a short description about the category.", + "PLACEHOLDER": "Δώστε μια σύντομη περιγραφή της κατηγορίας.", "ERROR": "Η περιγραφή απαιτείται" }, "BUTTONS": { - "CREATE": "Create category", + "CREATE": "Δημιουργία κατηγορίας", "CANCEL": "Άκυρο" }, "API": { - "SUCCESS_MESSAGE": "Category created successfully", - "ERROR_MESSAGE": "Unable to create category" + "SUCCESS_MESSAGE": "Η κατηγορία δημιουργήθηκε με επιτυχία", + "ERROR_MESSAGE": "Αδυναμία δημιουργίας κατηγορίας" } }, "EDIT": { - "TITLE": "Edit a category", - "SUB_TITLE": "Editing a category will update the category in the public facing portal.", - "PORTAL": "Portal", - "LOCALE": "Locale", + "TITLE": "Επεξεργασία κατηγορίας", + "SUB_TITLE": "Η επεξεργασία μιας κατηγορίας θα ενημερώσει την κατηγορία στην πύλη που βλέπει το κοινό.", + "PORTAL": "Πύλη", + "LOCALE": "Γλώσσα", "NAME": { "LABEL": "Όνομα", - "PLACEHOLDER": "Category name", - "HELP_TEXT": "The category name will be used in the public facing portal to categorize articles.", + "PLACEHOLDER": "Όνομα κατηγορίας", + "HELP_TEXT": "Η κατηγορία θα χρησιμοποιηθεί στην πύλη που βλέπει το κοινό για την κατηγοριοποίηση των άρθρων.", "ERROR": "Απαιτείται όνομα" }, "SLUG": { "LABEL": "Slug", - "PLACEHOLDER": "Category slug for urls", + "PLACEHOLDER": "Slug κατηγορίας για urls", "HELP_TEXT": "app.chatwoot.com/hc/my-portal/en-US/categories/my-slug", - "ERROR": "Slug is required" + "ERROR": "Το Slug είναι απαραίτητο" }, "DESCRIPTION": { "LABEL": "Περιγραφή", - "PLACEHOLDER": "Give a short description about the category.", + "PLACEHOLDER": "Δώστε μια σύντομη περιγραφή της κατηγορίας.", "ERROR": "Η περιγραφή απαιτείται" }, "BUTTONS": { - "CREATE": "Update category", + "CREATE": "Επεξεργασία κατηγορίας", "CANCEL": "Άκυρο" }, "API": { - "SUCCESS_MESSAGE": "Category updated successfully", - "ERROR_MESSAGE": "Unable to update category" + "SUCCESS_MESSAGE": "Η ετικέτα ενημερώθηκε επιτυχώς", + "ERROR_MESSAGE": "Αδύνατη η ενημέρωση της κατηγορίας" } }, "DELETE": { "API": { - "SUCCESS_MESSAGE": "Category deleted successfully", - "ERROR_MESSAGE": "Unable to delete category" + "SUCCESS_MESSAGE": "Η καμπάνια διαγράφηκε επιτυχώς", + "ERROR_MESSAGE": "Δεν είναι δυνατή η διαγραφή κατηγορίας" } } } diff --git a/app/javascript/dashboard/i18n/locale/el/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/el/inboxMgmt.json index e92a89ebf..07baaba4b 100644 --- a/app/javascript/dashboard/i18n/locale/el/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/el/inboxMgmt.json @@ -112,10 +112,10 @@ "ERROR": "Το πεδίο είναι απαραίτητο" }, "MESSAGING_SERVICE_SID": { - "LABEL": "Messaging Service SID", - "PLACEHOLDER": "Please enter your Twilio Messaging Service SID", + "LABEL": "SID Υπηρεσίας Μηνυμάτων", + "PLACEHOLDER": "Παρακαλώ εισάγετε το Twilio Messaging Service SID σας", "ERROR": "Το πεδίο είναι απαραίτητο", - "USE_MESSAGING_SERVICE": "Use a Twilio Messaging Service" + "USE_MESSAGING_SERVICE": "Χρήση μιας υπηρεσίας μηνυμάτων Twilio" }, "CHANNEL_TYPE": { "LABEL": "Τύπος Καναλιού", @@ -239,7 +239,9 @@ }, "API_CALLBACK": { "TITLE": "URL επανάκλησης", - "SUBTITLE": "Θα πρέπει να ρυθμίσετε το URL webhook στο facebook developer portal με το URL που αναφέρεται εδώ." + "SUBTITLE": "Πρέπει να ρυθμίσετε τη διεύθυνση URL του webhook και το διακριτικό επαλήθευσης στην πύλη Προγραμματιστή Facebook με τις τιμές που εμφανίζονται παρακάτω.", + "WEBHOOK_URL": "Σύνδεσμος Webhook", + "WEBHOOK_VERIFICATION_TOKEN": "Token Επαλήθευσης Webhook" }, "SUBMIT_BUTTON": "Δημιουργία Καναλιού WhatsApp", "API": { @@ -357,7 +359,7 @@ }, "FINISH": { "TITLE": "Το κιβώτιο σας είναι έτοιμο!", - "MESSAGE": "Μπορείτε να συνομιλείτε με τους πελάτες σας από το νέο κανάλι. Καλή υποστήριξη ", + "MESSAGE": "Μπορείτε να συνομιλείτε με τους πελάτες σας από το νέο κανάλι. Καλή υποστήριξη", "BUTTON_TEXT": "Μετάβαση", "MORE_SETTINGS": "Περισσότερες ρυθμίσεις", "WEBSITE_SUCCESS": "Επιτυχής δημιουργία του καναλιού ιστοσελίδας. Αντιγράψτε τον κώδικα που παρουσιάζεται παρακάτω, και τοποθετήστε τον στην ιστοσελίδα σας. Την επόμενη φορά που κάποιος πελάτης χρησιμοποιήσει το 'live chat', η συνομιλία θα εμφανιστεί στο κιβώτιο εισερχομένων σας." @@ -414,7 +416,7 @@ "CAMPAIGN": "Καμπάνιες", "PRE_CHAT_FORM": "Φόρμα Προ-Συνομιλίας", "BUSINESS_HOURS": "Ώρες Εργασίας", - "WIDGET_BUILDER": "Widget Builder" + "WIDGET_BUILDER": "Δημιουργός Widget" }, "SETTINGS": "Ρυθμίσεις", "FEATURES": { @@ -579,10 +581,10 @@ "WIDGET_BUILDER": { "WIDGET_OPTIONS": { "AVATAR": { - "LABEL": "Website Avatar", + "LABEL": "Avatar Ιστοσελίδας", "DELETE": { "API": { - "SUCCESS_MESSAGE": "Avatar deleted successfully", + "SUCCESS_MESSAGE": "Το Avatar διαγράφηκε επιτυχώς", "ERROR_MESSAGE": "Υπήρξε ένα σφάλμα, παρακαλώ προσπαθήστε ξανά" } } @@ -590,7 +592,7 @@ "WEBSITE_NAME": { "LABEL": "Όνομα Ιστοσελίδας", "PLACE_HOLDER": "Συμπληρώστε την ονομασία της ιστοσελίδας σας (π.χ: Ελληνικό Μεσογειακό Πανεπιστήμιο)", - "ERROR": "Please enter a valid website name" + "ERROR": "Παρακαλώ δώστε ένα έγκυρο όνομα ιστοσελίδας" }, "WELCOME_HEADING": { "LABEL": "Καλώς ήλθατε (Heading)", @@ -601,42 +603,42 @@ "PLACE_HOLDER": "Είναι απλό να συνδεθείτε μαζί μας. Ζητήστε μας οτιδήποτε, ή μοιραστείτε την εμπειρία σας." }, "REPLY_TIME": { - "LABEL": "Reply Time", + "LABEL": "Χρόνος Απάντησης", "IN_A_FEW_MINUTES": "Σε μερικά λεπτά", "IN_A_FEW_HOURS": "Σε μερικές ώρες", "IN_A_DAY": "Σε μία ημέρα" }, "WIDGET_COLOR_LABEL": "Χρώμα Widget", - "WIDGET_BUBBLE_POSITION_LABEL": "Widget Bubble Position", - "WIDGET_BUBBLE_TYPE_LABEL": "Widget Bubble Type", + "WIDGET_BUBBLE_POSITION_LABEL": "Θέση Φυσαλίδας Widget", + "WIDGET_BUBBLE_TYPE_LABEL": "Τύπος Φυσαλίδας Widget", "WIDGET_BUBBLE_LAUNCHER_TITLE": { "DEFAULT": "Συνομιλήστε μαζί μας", - "LABEL": "Widget Bubble Launcher Title", + "LABEL": "Τίτλος Εκκίνησης Φυσαλίδας Widget", "PLACE_HOLDER": "Συνομιλήστε μαζί μας" }, "UPDATE": { - "BUTTON_TEXT": "Update Widget Settings", + "BUTTON_TEXT": "Ενημέρωση Ρυθμίσεων Widget", "API": { - "SUCCESS_MESSAGE": "Widget settings updated successfully", - "ERROR_MESSAGE": "Unable to update widget settings" + "SUCCESS_MESSAGE": "Οι ρυθμίσεις widget ενημερώθηκαν με επιτυχία", + "ERROR_MESSAGE": "Δεν είναι δυνατή η ενημέρωση ρυθμίσεων widget" } }, "WIDGET_VIEW_OPTION": { - "PREVIEW": "Preview", + "PREVIEW": "Προεπισκόπηση", "SCRIPT": "Script" }, "WIDGET_BUBBLE_POSITION": { - "LEFT": "Left", - "RIGHT": "Right" + "LEFT": "Αριστερά", + "RIGHT": "Δεξιά" }, "WIDGET_BUBBLE_TYPE": { "STANDARD": "Standard", - "EXPANDED_BUBBLE": "Expanded Bubble" + "EXPANDED_BUBBLE": "Εκτεταμένη Φυσαλίδα" } }, "WIDGET_SCREEN": { - "DEFAULT": "Default", - "CHAT": "Chat" + "DEFAULT": "Προεπιλογή", + "CHAT": "Συνομιλία" }, "REPLY_TIME": { "IN_A_FEW_MINUTES": "Τυπικά έχετε απάντηση σε μερικά λεπτά", @@ -649,11 +651,11 @@ }, "BODY": { "TEAM_AVAILABILITY": { - "ONLINE": "We are Online", + "ONLINE": "Είμαστε online", "OFFLINE": "Προς το παρόν, είμαστε εκτός" }, - "USER_MESSAGE": "Hi", - "AGENT_MESSAGE": "Hello" + "USER_MESSAGE": "Γειά", + "AGENT_MESSAGE": "Γειά σας" }, "BRANDING_TEXT": "με την δύναμη του Chatwoot", "SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};" diff --git a/app/javascript/dashboard/i18n/locale/el/integrations.json b/app/javascript/dashboard/i18n/locale/el/integrations.json index 5302f0bf5..b4a9e2c3f 100644 --- a/app/javascript/dashboard/i18n/locale/el/integrations.json +++ b/app/javascript/dashboard/i18n/locale/el/integrations.json @@ -86,49 +86,49 @@ "BUTTON_TEXT": "Σύνδεση" }, "DASHBOARD_APPS": { - "TITLE": "Dashboard Apps", - "HEADER_BTN_TXT": "Add a new dashboard app", - "SIDEBAR_TXT": "

Dashboard Apps

Dashboard Apps allow organizations to embed an application inside the Chatwoot dashboard to provide the context for customer support agents. This feature allows you to create an application independently and embed that inside the dashboard to provide user information, their orders, or their previous payment history.

When you embed your application using the dashboard in Chatwoot, your application will get the context of the conversation and contact as a window event. Implement a listener for the message event on your page to receive the context.

To add a new dashboard app, click on the button 'Add a new dashboard app'.

", - "DESCRIPTION": "Dashboard Apps allow organizations to embed an application inside the dashboard to provide the context for customer support agents. This feature allows you to create an application independently and embed that to provide user information, their orders, or their previous payment history.", + "TITLE": "Εφαρμογές Dashboard", + "HEADER_BTN_TXT": "Προσθήκη νέας εφαρμογής Dashboard", + "SIDEBAR_TXT": "

Εφαρμογές Dashboard

Οι εφαρμογές Dashboard επιτρέπουν σε οργανισμούς να ενσωματώσουν μια εφαρμογή μέσα στο ταμπλό Chatwoot για να παρέχουν το πλαίσιο για τους πράκτορες υποστήριξης πελατών. Αυτό το χαρακτηριστικό σας επιτρέπει να δημιουργήσετε μια εφαρμογή ανεξάρτητα και ενσωματωμένη που μέσα στον πίνακα ελέγχου για να παρέχει πληροφορίες χρήστη, τις παραγγελίες τους, ή το ιστορικό προηγούμενων πληρωμών τους.

Όταν ενσωματώσετε την εφαρμογή σας χρησιμοποιώντας το Dashboard στο Chatwoot, η εφαρμογή σας θα πάρει το πλαίσιο της συνομιλίας και θα επικοινωνήσει ως ένα παράθυρο εκδήλωσης. Εφαρμόστε έναν ακροατή για το γεγονός του μηνύματος στη σελίδα σας για να λάβετε το πλαίσιο.

Για να προσθέσετε μια νέα εφαρμογή ταμπλό, κάντε κλικ στο κουμπί 'Προσθήκη μιας νέας εφαρμογής Dashboard'.

", + "DESCRIPTION": "Οι εφαρμογές Dashboard επιτρέπουν στους οργανισμούς να ενσωματώσουν μια εφαρμογή μέσα στον πίνακα ελέγχου για να παρέχουν το περιεχόμενο για τους πράκτορες υποστήριξης πελατών. Αυτή η λειτουργία σας επιτρέπει να δημιουργήσετε μια εφαρμογή ανεξάρτητα και ενσωματωμένη που θα παρέχει πληροφορίες χρήστη, τις παραγγελίες τους, ή το ιστορικό προηγούμενων πληρωμών τους.", "LIST": { - "404": "There are no dashboard apps configured on this account yet", - "LOADING": "Fetching dashboard apps...", + "404": "Δεν έχουν δημιουργηθεί εφαρμογές Dashboard για αυτόν το λογαριασμό", + "LOADING": "Λήψη εφαρμογών dashboard ...", "TABLE_HEADER": [ "Όνομα", "Endpoint" ], - "EDIT_TOOLTIP": "Edit app", - "DELETE_TOOLTIP": "Delete app" + "EDIT_TOOLTIP": "Επεξεργασία εφαρμογής", + "DELETE_TOOLTIP": "Διαγραφή εφαρμογής" }, "FORM": { "TITLE_LABEL": "Όνομα", - "TITLE_PLACEHOLDER": "Enter a name for your dashboard app", - "TITLE_ERROR": "A name for the dashboard app is required", + "TITLE_PLACEHOLDER": "Εισάγετε όνομα για την εφαρμογή dashboard", + "TITLE_ERROR": "Απαιτείται ένα όνομα για την εφαρμογή dashboard", "URL_LABEL": "Endpoint", - "URL_PLACEHOLDER": "Enter the endpoint URL where your app is hosted", - "URL_ERROR": "A valid URL is required" + "URL_PLACEHOLDER": "Εισάγετε το URL του endpoint όπου φιλοξενείται η εφαρμογή σας", + "URL_ERROR": "Απαιτείται ένα έγκυρο URL" }, "CREATE": { - "HEADER": "Add a new dashboard app", + "HEADER": "Προσθήκη νέας εφαρμογής Dashboard", "FORM_SUBMIT": "Καταχώρηση", "FORM_CANCEL": "Άκυρο", - "API_SUCCESS": "Dashboard app configured successfully", - "API_ERROR": "We couldn't create an app. Please try again later" + "API_SUCCESS": "Η εφαρμογή dashboard ρυθμίστηκε επιτυχώς", + "API_ERROR": "Δεν μπορούμε να δημιουργήσουμε μια εφαρμογή. Παρακαλώ δοκιμάστε ξανά αργότερα" }, "UPDATE": { - "HEADER": "Edit dashboard app", + "HEADER": "Επεξεργασία εφαρμογής dashboard", "FORM_SUBMIT": "Ενημέρωση", "FORM_CANCEL": "Άκυρο", - "API_SUCCESS": "Dashboard app updated successfully", - "API_ERROR": "We couldn't update the app. Please try again later" + "API_SUCCESS": "Η εφαρμογή dashboard ενημερώθηκε με επιτυχία", + "API_ERROR": "Δεν ήταν δυνατή η ενημέρωση της εφαρμογής. Παρακαλώ δοκιμάστε ξανά αργότερα" }, "DELETE": { - "CONFIRM_YES": "Yes, delete it", - "CONFIRM_NO": "No, keep it", + "CONFIRM_YES": "Ναι, Διέγραψε την", + "CONFIRM_NO": "Όχι, Κράτησε την", "TITLE": "Επιβεβαίωση Διαγραφής", - "MESSAGE": "Are you sure to delete the app - %{appName}?", - "API_SUCCESS": "Dashboard app deleted successfully", - "API_ERROR": "We couldn't delete the app. Please try again later" + "MESSAGE": "Είστε βέβαιοι να διαγράψετε την εφαρμογή - %{appName};", + "API_SUCCESS": "Η εφαρμογή dashboard διαγράφηκε επιτυχώς", + "API_ERROR": "Δεν μπορούμε να διαγράψουμε την εφαρμογή. Παρακαλώ δοκιμάστε ξανά αργότερα" } } } diff --git a/app/javascript/dashboard/i18n/locale/el/macros.json b/app/javascript/dashboard/i18n/locale/el/macros.json new file mode 100644 index 000000000..439241619 --- /dev/null +++ b/app/javascript/dashboard/i18n/locale/el/macros.json @@ -0,0 +1,5 @@ +{ + "MACROS": { + "HEADER": "Μακροεντολές" + } +} diff --git a/app/javascript/dashboard/i18n/locale/el/settings.json b/app/javascript/dashboard/i18n/locale/el/settings.json index 69cd0a401..651e2de5d 100644 --- a/app/javascript/dashboard/i18n/locale/el/settings.json +++ b/app/javascript/dashboard/i18n/locale/el/settings.json @@ -20,17 +20,17 @@ "NOTE": "Η διεύθυνση email είναι η ταυτότητά σας και χρησιμοποιείται για την είσοδο (login) σας." }, "SEND_MESSAGE": { - "TITLE": "Hotkey to send messages", - "NOTE": "You can select a hotkey (either Enter or Cmd/Ctrl+Enter) based on your preference of writing.", - "UPDATE_SUCCESS": "Your settings have been updated successfully", + "TITLE": "Πλήκτρο συντόμευσης για αποστολή μηνυμάτων", + "NOTE": "Μπορείτε να επιλέξετε μια συντόμευση (είτε εισάγετε είτε Cmd/Ctrl+Enter) με βάση την προτίμηση για το γράψιμο.", + "UPDATE_SUCCESS": "Οι ρυθμίσεις σας έχουν ενημερωθεί με επιτυχία", "CARD": { "ENTER_KEY": { "HEADING": "Enter (↵)", - "CONTENT": "Send messages by pressing Enter key instead of clicking the send button." + "CONTENT": "Αποστολή μηνυμάτων πατώντας το πλήκτρο Enter αντί να πατήσετε το κουμπί αποστολής." }, "CMD_ENTER_KEY": { "HEADING": "Cmd/Ctrl + Enter (⌘ + ↵)", - "CONTENT": "Send messages by pressing Cmd/Ctrl + enter key instead of clicking the send button." + "CONTENT": "Αποστολή μηνυμάτων πατώντας το πλήκτρο Enter αντί να πατήσετε το κουμπί αποστολής." } } }, @@ -141,8 +141,8 @@ "TRAIL_BUTTON": "Αγόρασε τώρα", "DELETED_USER": "Διαγραμμένος Χρήστης", "ACCOUNT_SUSPENDED": { - "TITLE": "Account Suspended", - "MESSAGE": "Your account is suspended. Please reach out to the support team for more information." + "TITLE": "Αναστολή Λογαριασμού", + "MESSAGE": "Ο λογαριασμός σας έχει ανασταλεί. Επικοινωνήστε με την ομάδα υποστήριξης για περισσότερες πληροφορίες." } }, "COMPONENTS": { @@ -179,6 +179,7 @@ "CONTACTS": "Επαφές", "HOME": "Αρχική", "AGENTS": "Πράκτορες", + "AGENT_BOTS": "Bots", "INBOXES": "Κιβώτια Εισερχομένων", "NOTIFICATIONS": "Ειδοποιήσεις", "CANNED_RESPONSES": "Έτοιμες Απαντήσεις", @@ -189,8 +190,9 @@ "LABELS": "Ετικέτες", "CUSTOM_ATTRIBUTES": "Προσαρμοζόμενες Ιδιότητες", "AUTOMATION": "Αυτοματισμός", + "MACROS": "Μακροεντολές", "TEAMS": "Ομάδες", - "BILLING": "Billing", + "BILLING": "Χρεώσεις", "CUSTOM_VIEWS_FOLDER": "Φάκελοι", "CUSTOM_VIEWS_SEGMENTS": "Τμήματα", "ALL_CONTACTS": "Όλες Οι Επαφές", @@ -212,33 +214,33 @@ "REPORTS_OVERVIEW": "Επισκόπηση", "FACEBOOK_REAUTHORIZE": "Η σύνδεση Facebook έχει λήξει, παρακαλώ ξανασυνδεθείτε στο Facebook για να συνεχίσετε", "HELP_CENTER": { - "TITLE": "Help Center (Beta)", - "ALL_ARTICLES": "All Articles", - "MY_ARTICLES": "My Articles", - "DRAFT": "Draft", - "ARCHIVED": "Archived", + "TITLE": "Κέντρο Βοήθειας (Beta)", + "ALL_ARTICLES": "Όλα Τα Άρθρα", + "MY_ARTICLES": "Τα Άρθρα Μου", + "DRAFT": "Πρόχειρο", + "ARCHIVED": "Αρχειοθετημένο", "CATEGORY": "Κατηγορία", - "CATEGORY_EMPTY_MESSAGE": "No categories found" + "CATEGORY_EMPTY_MESSAGE": "Δεν βρέθηκαν κατηγορίες" }, - "DOCS": "Read docs" + "DOCS": "Ανάγνωση εγγράφων" }, "BILLING_SETTINGS": { - "TITLE": "Billing", + "TITLE": "Χρεώσεις", "CURRENT_PLAN": { - "TITLE": "Current Plan", - "PLAN_NOTE": "You are currently subscribed to the **%{plan}** plan with **%{quantity}** licenses" + "TITLE": "Τρέχον Πλάνο", + "PLAN_NOTE": "Αυτή τη στιγμή έχετε εγγραφεί στο πλάνο **%{plan}** με **%{quantity}** άδειες" }, "MANAGE_SUBSCRIPTION": { - "TITLE": "Manage your subscription", - "DESCRIPTION": "View your previous invoices, edit your billing details, or cancel your subscription.", - "BUTTON_TXT": "Go to the billing portal" + "TITLE": "Διαχειριστείτε τη συνδρομή σας", + "DESCRIPTION": "Δείτε τα προηγούμενα τιμολόγια σας, επεξεργαστείτε τα στοιχεία χρέωσης ή ακυρώστε τη συνδρομή σας.", + "BUTTON_TXT": "Μετάβαση στην πύλη χρέωσης" }, "CHAT_WITH_US": { - "TITLE": "Need help?", - "DESCRIPTION": "Do you face any issues in billing? We are here to help.", + "TITLE": "Χρειάζεστε βοήθεια;", + "DESCRIPTION": "Αντιμετωπίζετε οποιαδήποτε προβλήματα στην τιμολόγηση? Είμαστε εδώ για να βοηθήσουμε.", "BUTTON_TXT": "Συνομιλήστε μαζί μας" }, - "NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again." + "NO_BILLING_USER": "Ο λογαριασμός χρέωσης έχει ρυθμιστεί. Παρακαλώ ανανεώστε τη σελίδα και προσπαθήστε ξανά." }, "CREATE_ACCOUNT": { "NO_ACCOUNT_WARNING": "Ωχ! Δεν μπορέσαμε να βρούμε κανένα λογαριασμό Chatwoot. Παρακαλούμε δημιουργήστε ένα νέο λογαριασμό για να συνεχίσετε.", diff --git a/app/javascript/dashboard/i18n/locale/he/chatlist.json b/app/javascript/dashboard/i18n/locale/he/chatlist.json index e669054ac..7f25908b8 100644 --- a/app/javascript/dashboard/i18n/locale/he/chatlist.json +++ b/app/javascript/dashboard/i18n/locale/he/chatlist.json @@ -54,12 +54,12 @@ "RECEIVED_VIA_EMAIL": "התקבל בדואר אלקטרוני", "VIEW_TWEET_IN_TWITTER": "צפה בציוץ בטוויטר", "REPLY_TO_TWEET": "הגב לציוץ זה", - "LINK_TO_STORY": "Go to instagram story", + "LINK_TO_STORY": "מעבר לסטורי באינסטגרם", "SENT": "נשלח בהצלחה", "NO_MESSAGES": "אין הודעות", "NO_CONTENT": "אין תוכן זמין", "HIDE_QUOTED_TEXT": "הסתר טקסט מצוטט", "SHOW_QUOTED_TEXT": "הצג טקסט מצוטט", - "MESSAGE_READ": "Read" + "MESSAGE_READ": "קרא" } } diff --git a/app/javascript/dashboard/i18n/locale/he/contact.json b/app/javascript/dashboard/i18n/locale/he/contact.json index c4f12e24c..236066ce7 100644 --- a/app/javascript/dashboard/i18n/locale/he/contact.json +++ b/app/javascript/dashboard/i18n/locale/he/contact.json @@ -3,11 +3,11 @@ "NOT_AVAILABLE": "לא זמין", "EMAIL_ADDRESS": "כתובת מייל", "PHONE_NUMBER": "מספר טלפון", - "IDENTIFIER": "Identifier", + "IDENTIFIER": "מזהה", "COPY_SUCCESSFUL": "הועתק ללוח בהצלחה", "COMPANY": "חברה", "LOCATION": "מיקום", - "BROWSER_LANGUAGE": "Browser Language", + "BROWSER_LANGUAGE": "שפת דפדפן", "CONVERSATION_TITLE": "פרטי שיחה", "VIEW_PROFILE": "צפה בפרופיל", "BROWSER": "דפדפן", @@ -75,8 +75,8 @@ "DELETE_NOTE": { "CONFIRM": { "TITLE": "אשר מחיקה", - "MESSAGE": "Are you want sure to delete this note?", - "YES": "Yes, Delete it", + "MESSAGE": "אתה בטוח שתרצה למחוק את ההערה הזו?", + "YES": "כן, מחק", "NO": "לא, השאר" } }, diff --git a/app/javascript/dashboard/i18n/locale/he/conversation.json b/app/javascript/dashboard/i18n/locale/he/conversation.json index 310f127f9..3a121e019 100644 --- a/app/javascript/dashboard/i18n/locale/he/conversation.json +++ b/app/javascript/dashboard/i18n/locale/he/conversation.json @@ -1,10 +1,10 @@ { "CONVERSATION": { "SELECT_A_CONVERSATION": "אנא בחר שיחה מהחלונית השמאלית", - "CSAT_REPLY_MESSAGE": "Please rate the conversation", + "CSAT_REPLY_MESSAGE": "נא דרג שיחה זו", "404": "Sorry, we cannot find the conversation. Please try again", - "SWITCH_VIEW_LAYOUT": "Switch the layout", - "DASHBOARD_APP_TAB_MESSAGES": "Messages", + "SWITCH_VIEW_LAYOUT": "החלף תצוגה", + "DASHBOARD_APP_TAB_MESSAGES": "הודעות", "UNVERIFIED_SESSION": "זהות המשתמש לא מְאוּמָתת", "NO_MESSAGE_1": "או - או! נראה שאין הודעות מלקוחות בתיבת הדואר הנכנס שלך.", "NO_MESSAGE_2": " לשלוח הודעה לעמוד שלך!", @@ -34,7 +34,7 @@ "REPLYING_TO": "אתה משיב ל:", "REMOVE_SELECTION": "הסר בחירה", "DOWNLOAD": "הורד", - "UNKNOWN_FILE_TYPE": "Unknown File", + "UNKNOWN_FILE_TYPE": "קובץ לא ידוע", "UPLOADING_ATTACHMENTS": "מעלה קובץ מצורף...", "SUCCESS_DELETE_MESSAGE": "ההודעה נמחקה בהצלחה", "FAIL_DELETE_MESSSAGE": "לא ניתן למחוק את ההודעה! נסה שוב", @@ -63,18 +63,18 @@ }, "CARD_CONTEXT_MENU": { "PENDING": "סמן כממתין", - "RESOLVED": "Mark as resolved", + "RESOLVED": "סמן כפתור", "REOPEN": "פתח מחדש את השיחה", "SNOOZE": { "TITLE": "Snooze", - "NEXT_REPLY": "Until next reply", - "TOMORROW": "Until tomorrow", - "NEXT_WEEK": "Until next week" + "NEXT_REPLY": "עד תגובה הבאה", + "TOMORROW": "עד מחר", + "NEXT_WEEK": "עד שבוע הבא" }, - "ASSIGN_AGENT": "Assign agent", + "ASSIGN_AGENT": "שייך סוכן", "ASSIGN_LABEL": "Assign label", - "AGENTS_LOADING": "Loading agents...", - "ASSIGN_TEAM": "Assign team", + "AGENTS_LOADING": "טוען סוכנים...", + "ASSIGN_TEAM": "שייך צוות", "API": { "AGENT_ASSIGNMENT": { "SUCCESFUL": "Conversation id %{conversationId} assigned to \"%{agentName}\"", @@ -86,18 +86,18 @@ }, "TEAM_ASSIGNMENT": { "SUCCESFUL": "Assigned team \"%{team}\" to conversation id %{conversationId}", - "FAILED": "Couldn't assign team. Please try again." + "FAILED": "השמה לצוות לא הצליחה, בבקשה נסה שנית." } } }, "FOOTER": { "MESSAGE_SIGN_TOOLTIP": "Message signature", - "ENABLE_SIGN_TOOLTIP": "Enable signature", - "DISABLE_SIGN_TOOLTIP": "Disable signature", + "ENABLE_SIGN_TOOLTIP": "אפשר חתימה", + "DISABLE_SIGN_TOOLTIP": "נטרל חתימה", "MSG_INPUT": "Shift + Enter עבור שורה חדשה. התחל עם '/' כדי לבחור תגובה מוכנה.", "PRIVATE_MSG_INPUT": "Shift + Enter עבור שורה חדשה. זה יהיה גלוי רק לסוכנים", "MESSAGE_SIGNATURE_NOT_CONFIGURED": "Message signature is not configured, please configure it in profile settings.", - "CLICK_HERE": "Click here to update" + "CLICK_HERE": "לחץ כאן כדי לעדכן" }, "REPLYBOX": { "REPLY": "הגב", @@ -108,12 +108,12 @@ "TIP_FORMAT_ICON": "הצג עורך טקסט עשיר", "TIP_EMOJI_ICON": "הצג בחירת אימוג'ים", "TIP_ATTACH_ICON": "הוסף קבצים", - "TIP_AUDIORECORDER_ICON": "Record audio", - "TIP_AUDIORECORDER_PERMISSION": "Allow access to audio", - "TIP_AUDIORECORDER_ERROR": "Could not open the audio", + "TIP_AUDIORECORDER_ICON": "הקלט אודיו", + "TIP_AUDIORECORDER_PERMISSION": "אפשר גישה לאודיו", + "TIP_AUDIORECORDER_ERROR": "לא יכול לפתוח אודיו", "DRAG_DROP": "גרור ושחרר כאן להוספת קובץ מצורף", - "START_AUDIO_RECORDING": "Start audio recording", - "STOP_AUDIO_RECORDING": "Stop audio recording", + "START_AUDIO_RECORDING": "התחל הקלטת אודיו", + "STOP_AUDIO_RECORDING": "עצור הקלטת אודיו", "": "", "EMAIL_HEAD": { "ADD_BCC": "הוסף bcc", @@ -131,11 +131,11 @@ }, "VISIBLE_TO_AGENTS": "פתקים פרטיים: רק אתה והצוות שלך יכולים לראות", "CHANGE_STATUS": "סטטוס השיחה השתנה", - "CHANGE_STATUS_FAILED": "Conversation status change failed", + "CHANGE_STATUS_FAILED": "סטטוס השיחה השתנה לנכשלה", "CHANGE_AGENT": "שיוך שיחה השתנתה", "CHANGE_AGENT_FAILED": "Assignee change failed", - "ASSIGN_LABEL_SUCCESFUL": "Label assigned successfully", - "ASSIGN_LABEL_FAILED": "Label assignment failed", + "ASSIGN_LABEL_SUCCESFUL": "סמן משימה כבוצעה בהצלחה", + "ASSIGN_LABEL_FAILED": "סמן משימה כנכשלה", "CHANGE_TEAM": "שיחת קבוצה השתנתה", "FILE_SIZE_LIMIT": "File exceeds the {MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE} MB attachment limit", "MESSAGE_ERROR": "לא ניתן לשלוח הודעה, אנא נסה שוב מאוחר יותר", @@ -151,7 +151,7 @@ "CONTEXT_MENU": { "COPY": "העתק", "DELETE": "מחק", - "CREATE_A_CANNED_RESPONSE": "Add to canned responses" + "CREATE_A_CANNED_RESPONSE": "הוסף לתגובות מוכנות" } }, "EMAIL_TRANSCRIPT": { @@ -182,17 +182,17 @@ "TEAM_MEMBERS": { "TITLE": "הזמן את חברי הצוות שלך", "DESCRIPTION": "Since you are getting ready to talk to your customer, bring in your teammates to assist you. You can invite your teammates by adding their email addresses to the agent list.", - "NEW_LINK": "Click here to invite a team member" + "NEW_LINK": "לחץ כאן כדי להזמין חבר צוות" }, "INBOXES": { - "TITLE": "Connect Inboxes", + "TITLE": "התחבר לתיבות", "DESCRIPTION": "Connect various channels through which your customers would be talking to you. It can be a website live-chat, your Facebook or Twitter page or even your WhatsApp number.", - "NEW_LINK": "Click here to create an inbox" + "NEW_LINK": "לחץ כאן כדי ליצור תיבה" }, "LABELS": { "TITLE": "Organize conversations with labels", "DESCRIPTION": "Labels provide an easier way to categorize your conversation. Create some labels like #support-enquiry, #billing-question etc., so that you can use them in a conversation later.", - "NEW_LINK": "Click here to create tags" + "NEW_LINK": "לחץ כאן כדי ליצור תגיות" } }, "CONVERSATION_SIDEBAR": { @@ -200,7 +200,7 @@ "SELF_ASSIGN": "Assign to me", "TEAM_LABEL": "Assigned Team", "SELECT": { - "PLACEHOLDER": "None" + "PLACEHOLDER": "כלום" }, "ACCORDION": { "CONTACT_DETAILS": "Contact Details", @@ -233,7 +233,7 @@ } }, "EMAIL_HEADER": { - "FROM": "From", + "FROM": "מאת", "TO": "אל", "BCC": "Bcc", "CC": "עותק", diff --git a/app/javascript/dashboard/i18n/locale/he/generalSettings.json b/app/javascript/dashboard/i18n/locale/he/generalSettings.json index dfda9965a..2f7b4d580 100644 --- a/app/javascript/dashboard/i18n/locale/he/generalSettings.json +++ b/app/javascript/dashboard/i18n/locale/he/generalSettings.json @@ -14,7 +14,7 @@ "NOTE": "" }, "ACCOUNT_ID": { - "TITLE": "Account ID", + "TITLE": "מזהה חשבון", "NOTE": "This ID is required if you are building an API based integration" }, "NAME": { @@ -60,12 +60,12 @@ "NOTIFICATIONS_PAGE": { "HEADER": "התראות", "MARK_ALL_DONE": "סמן הכל כבוצע", - "DELETE_TITLE": "deleted", + "DELETE_TITLE": "נמחק", "UNREAD_NOTIFICATION": { - "TITLE": "Unread Notifications", - "ALL_NOTIFICATIONS": "View all notifications", - "LOADING_UNREAD_MESSAGE": "Loading unread notifications...", - "EMPTY_MESSAGE": "You have no unread notifications" + "TITLE": "התראות שלא נקראו", + "ALL_NOTIFICATIONS": "הצג את כל ההתראות", + "LOADING_UNREAD_MESSAGE": "טוען התראות שלא נקראו...", + "EMPTY_MESSAGE": "אין לך התראות שלא נקראו" }, "LIST": { "LOADING_MESSAGE": "טוען הודעות...", @@ -93,10 +93,10 @@ } }, "COMMAND_BAR": { - "SEARCH_PLACEHOLDER": "Search or jump to", + "SEARCH_PLACEHOLDER": "חפש או קפוץ ל", "SECTIONS": { - "GENERAL": "General", - "REPORTS": "Reports", + "GENERAL": "כללי", + "REPORTS": "דוחות", "CONVERSATION": "שיחה", "CHANGE_ASSIGNEE": "Change Assignee", "CHANGE_TEAM": "Change Team", diff --git a/app/javascript/dashboard/i18n/locale/he/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/he/inboxMgmt.json index 8dcdf0131..1b04432f9 100644 --- a/app/javascript/dashboard/i18n/locale/he/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/he/inboxMgmt.json @@ -158,7 +158,7 @@ }, "BANDWIDTH": { "ACCOUNT_ID": { - "LABEL": "Account ID", + "LABEL": "מזהה חשבון", "PLACEHOLDER": "Please enter your Bandwidth Account ID", "ERROR": "שדה חובה" }, @@ -239,7 +239,9 @@ }, "API_CALLBACK": { "TITLE": "כתובת אתר להתקשרות חוזרת", - "SUBTITLE": "You have to configure the webhook URL in facebook developer portal with the URL mentioned here." + "SUBTITLE": "You have to configure the webhook URL and the verification token in the Facebook Developer portal with the values shown below.", + "WEBHOOK_URL": "Webhook URL", + "WEBHOOK_VERIFICATION_TOKEN": "Webhook Verification Token" }, "SUBMIT_BUTTON": "צור ערוץ וואטסאפ", "API": { @@ -357,7 +359,7 @@ }, "FINISH": { "TITLE": "תיבת הדואר הנכנס שלך מוכנה!", - "MESSAGE": "כעת תוכל ליצור קשר עם הלקוחות שלך דרך הערוץ החדש שלך. תמיכה שמחה ", + "MESSAGE": "כעת תוכל ליצור קשר עם הלקוחות שלך דרך הערוץ החדש שלך. תמיכה שמחה", "BUTTON_TEXT": "קח אותי לשם", "MORE_SETTINGS": "הגדרות נוספות", "WEBSITE_SUCCESS": "סיימת בהצלחה ליצור ערוץ אתר אינטרנט. העתק את הקוד המוצג למטה והדבק אותו באתר שלך. בפעם הבאה שלקוח ישתמש בצ'אט החי, השיחה תופיע אוטומטית בתיבת הדואר הנכנס שלך." diff --git a/app/javascript/dashboard/i18n/locale/he/settings.json b/app/javascript/dashboard/i18n/locale/he/settings.json index 380b13761..3216cf128 100644 --- a/app/javascript/dashboard/i18n/locale/he/settings.json +++ b/app/javascript/dashboard/i18n/locale/he/settings.json @@ -58,7 +58,7 @@ "AUDIO_NOTIFICATIONS_SECTION": { "TITLE": "Audio Notifications", "NOTE": "Enable audio notifications in dashboard for new messages and conversations.", - "NONE": "None", + "NONE": "כלום", "ASSIGNED": "Assigned Conversations", "ALL_CONVERSATIONS": "All Conversations" }, @@ -179,6 +179,7 @@ "CONTACTS": "איש קשר", "HOME": "בית", "AGENTS": "סוכנים", + "AGENT_BOTS": "Bots", "INBOXES": "תיבות דואר נכנס", "NOTIFICATIONS": "התראות", "CANNED_RESPONSES": "תגובות מוכנות", @@ -189,6 +190,7 @@ "LABELS": "Labels", "CUSTOM_ATTRIBUTES": "מאפיינים בהתאמה אישית", "AUTOMATION": "Automation", + "MACROS": "Macros", "TEAMS": "Teams", "BILLING": "Billing", "CUSTOM_VIEWS_FOLDER": "Folders", diff --git a/config/locales/el.yml b/config/locales/el.yml index df0cac548..d53161c16 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -23,7 +23,7 @@ el: reset_password_failure: Ωχ όχι! Δεν υπάρχει κάποιος χρήστης με το συγκεκριμένο email. errors: validations: - presence: must not be blank + presence: δεν πρέπει να είναι κενό webhook: invalid: Μη έγκυρα συμβάντα signup: @@ -33,17 +33,17 @@ el: failed: Η εγγραφή απέτυχε data_import: data_type: - invalid: Invalid data type + invalid: Μη έγκυρος τύπος δεδομένων contacts: import: failed: Το αρχείο είναι κενό email: - invalid: Invalid email + invalid: Ακατάλληλο email phone_number: - invalid: should be in e164 format + invalid: πρέπει να είναι σε μορφή e164 categories: locale: - unique: should be unique in the category and portal + unique: πρέπει να είναι μοναδικό στην κατηγορία και την πύλη inboxes: imap: socket_error: Παρακαλώ ελέγξτε τη σύνδεση δικτύου, τη διεύθυνση IMAP και προσπαθήστε ξανά. From 73f5595762877bcc1d15144cdf4bd7a3db34069d Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Tue, 18 Oct 2022 01:43:02 +0530 Subject: [PATCH 12/35] chore: Sync colors from dashboard to tailwind config (#5656) --- .../sidebarComponents/SecondaryNavItem.vue | 2 +- .../widgets/conversation/MessagesView.vue | 2 +- .../widgets/conversation/ReplyBox.vue | 2 +- .../conversationBulkActions/Index.vue | 2 +- .../widget-preview/components/Widget.vue | 6 +- .../helpcenter/components/PortalPopover.vue | 2 +- .../pages/categories/CategoryListItem.vue | 2 +- .../shared/assets/stylesheets/colors.scss | 28 +++---- tailwind.config.js | 74 ++++++++++--------- 9 files changed, 62 insertions(+), 58 deletions(-) diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue index a7a6761cf..cd155bce8 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue @@ -321,7 +321,7 @@ export default { .beta { padding-right: var(--space-smaller) !important; padding-left: var(--space-smaller) !important; - margin-left: var(--space-half) !important; + margin-left: var(--space-smaller) !important; display: inline-block; font-size: var(--font-size-micro); font-weight: var(--font-weight-medium); diff --git a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue index 52a43091c..5b2d322e3 100644 --- a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue +++ b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue @@ -434,7 +434,7 @@ export default { &::before { transform: rotate(0deg); - left: var(--space-half); + left: var(--space-smaller); bottom: var(--space-minus-slab); } } diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue index bbc2f017e..b5bc55235 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue @@ -970,7 +970,7 @@ export default { &::before { transform: rotate(0deg); - left: var(--space-half); + left: var(--space-smaller); bottom: var(--space-minus-slab); } } diff --git a/app/javascript/dashboard/components/widgets/conversation/conversationBulkActions/Index.vue b/app/javascript/dashboard/components/widgets/conversation/conversationBulkActions/Index.vue index 70b0464cc..600ff4393 100644 --- a/app/javascript/dashboard/components/widgets/conversation/conversationBulkActions/Index.vue +++ b/app/javascript/dashboard/components/widgets/conversation/conversationBulkActions/Index.vue @@ -181,7 +181,7 @@ export default { color: var(--y-700); font-size: var(--font-size-mini); margin-top: var(--space-small); - padding: var(--space-half) var(--space-one); + padding: var(--space-smaller) var(--space-small); } .popover-animation-enter-active, diff --git a/app/javascript/dashboard/modules/widget-preview/components/Widget.vue b/app/javascript/dashboard/modules/widget-preview/components/Widget.vue index d2e5622a2..8ec864042 100644 --- a/app/javascript/dashboard/modules/widget-preview/components/Widget.vue +++ b/app/javascript/dashboard/modules/widget-preview/components/Widget.vue @@ -249,8 +249,8 @@ export default { display: flex; align-items: center; border-radius: calc(var(--border-radius-small) * 10); - height: calc(var(--space-three) * 2); - width: calc(var(--space-three) * 2); + height: calc(var(--space-large) * 2); + width: calc(var(--space-large) * 2); position: relative; overflow-wrap: anywhere; cursor: pointer; @@ -274,7 +274,7 @@ export default { display: inline; height: var(--space-medium); width: var(--space-micro); - left: var(--space-three); + left: var(--space-large); position: absolute; } diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/PortalPopover.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/PortalPopover.vue index 1f5eebe0e..67d1b33f1 100644 --- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/PortalPopover.vue +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/PortalPopover.vue @@ -104,7 +104,7 @@ export default { .new-popover-link { display: flex; align-items: center; - padding: var(--space-half) var(--space-one); + padding: var(--space-smaller) var(--space-small); background-color: var(--s-25); font-size: var(--font-size-mini); color: var(--s-500); diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/categories/CategoryListItem.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/categories/CategoryListItem.vue index 3a8b7a850..45b23e5ed 100644 --- a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/categories/CategoryListItem.vue +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/categories/CategoryListItem.vue @@ -118,6 +118,6 @@ table { justify-content: center; color: var(--s-500); font-size: var(--font-size-default); - margin-top: var(--space-three); + margin-top: var(--space-large); } diff --git a/app/javascript/shared/assets/stylesheets/colors.scss b/app/javascript/shared/assets/stylesheets/colors.scss index 7c13fc39c..64b7f3f94 100644 --- a/app/javascript/shared/assets/stylesheets/colors.scss +++ b/app/javascript/shared/assets/stylesheets/colors.scss @@ -26,16 +26,16 @@ --g-800: #009000; --g-900: #007000; - --y-50: #fefde8; - --y-100: #fdfcc4; - --y-200: #fcf68c; - --y-300: #f9e736; - --y-400: #f6d819; - --y-500: #e6c00c; - --y-600: #c69608; - --y-700: #9e6b0a; + --y-50: #FEFDE8; + --y-100: #FDFCC4; + --y-200: #FCF68C; + --y-300: #F9E736; + --y-400: #F6D819; + --y-500: #E6C00C; + --y-600: #C69608; + --y-700: #9E6b0A; --y-800: #835510; - --y-900: #6f4514; + --y-900: #6F4514; --s-25: #F8FAFC; --s-50: #F1F5F8; @@ -50,11 +50,11 @@ --s-800: #293F51; --s-900: #1B2836; - --b-50: #f7f7f7; - --b-100: #ececed; - --b-200: #dddde0; - --b-300: #c6c7ca; - --b-400: #abacaf; + --b-50: #F7F7F7; + --b-100: #ECECED; + --b-200: #DDDDE0; + --b-300: #C6C7CA; + --b-400: #ABACAF; --b-500: #96979C; --b-600: #6E6F73; --b-700: #5A5B5F; diff --git a/tailwind.config.js b/tailwind.config.js index e839d00ec..90408a779 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -14,16 +14,18 @@ module.exports = { colors: { white: colors.white, woot: { - 50: '#E3F2FF', - 100: '#BBDDFF', - 200: '#8FC9FF', - 300: '#61B3FF', - 400: '#3FA3FF', + 25: '#F5FAFF', + 50: '#EBF5FF', + 75: '#D6EBFF', + 100: '#C2E1FF', + 200: '#99CEFF', + 300: '#70BAFF', + 400: '#47A6FF', 500: '#1F93FF', - 600: '#2284F0', - 700: '#2272DC', - 800: '#2161CA', - 900: '#1F41AB', + 600: '#1976CC', + 700: '#135899', + 800: '#0C3B66', + 900: '#061D33', }, green: { 50: '#E6F8E6', @@ -38,38 +40,40 @@ module.exports = { 900: '#007000', }, yellow: { - 50: '#FFFEE8', - 100: '#FFFAC5', - 200: '#FFF69E', - 300: '#FEF176', - 400: '#FCEC56', - 500: '#F9E736', - 600: '#FFDD3A', - 700: '#FFC532', - 800: '#FDAD2A', - 900: '#F9841B', + 50: '#FEFDE8', + 100: '#FDFCC4', + 200: '#FCF68C', + 300: '#F9E736', + 400: '#F6D819', + 500: '#E6C00C', + 600: '#C69608', + 700: '#9E6b0A', + 800: '#835510', + 900: '#6F4514', }, slate: { - 50: '#F4F6FB', - 100: '#C8D6E6', - 200: '#ABBACE', - 300: '#8C9EB6', - 400: '#7489A4', - 500: '#5D7592', - 600: '#506781', - 700: '#40546B', - 800: '#314155', - 900: '#1F2D3D', + 25: '#F8FAFC', + 50: '#F1F5F8', + 75: '#EBF0F5', + 100: ' #E4EBF1', + 200: ' #C9D7E3', + 300: ' #AEC3D5', + 400: ' #93AFC8', + 500: ' #779BBB', + 600: ' #446888', + 700: ' #37546D', + 800: ' #293F51', + 900: ' #1B2836', }, black: { - 50: '#F8F9FE', - 100: '#F2F3F7', - 200: '#E9EAEF', - 300: '#DADBDF', - 400: '#B6B7BB', + 50: '#F7F7F7', + 100: '#ECECED', + 200: '#DDDDE0', + 300: '#C6C7CA', + 400: '#ABACAF', 500: '#96979C', 600: '#6E6F73', - 700: '#3C4858', + 700: '#5A5B5F', 800: '#3C3D40', 900: '#1B1C1F', }, From 20b4a91122e9b5db5ccc0546c06783a6c08fa247 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Mon, 17 Oct 2022 14:59:44 -0700 Subject: [PATCH 13/35] chore: Add feature flags in the settings console (#5657) --- .../dashboard/components/layout/Sidebar.vue | 10 +++---- .../layout/config/sidebarItems/settings.js | 28 +++++++++++++------ .../layout/sidebarComponents/Secondary.vue | 17 ++++++++--- .../sidebarComponents/SecondaryNavItem.vue | 6 ++-- .../widgets/WootWriter/ReplyBottomPanel.vue | 15 ++++++++-- app/javascript/dashboard/featureFlags.js | 13 +++++++++ .../dashboard/commands/goToCommandHotKeys.js | 22 ++++++++++++++- .../settings/account/account.routes.js | 2 +- .../dashboard/settings/settings.routes.js | 2 +- .../FluentIcon/dashboard-icons.json | 1 + .../account_features_field/_form.html.erb | 1 + config/features.yml | 18 ++++++++++++ ...20221017201914_add_features_to_accounts.rb | 20 +++++++++++++ db/schema.rb | 2 +- 14 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 app/javascript/dashboard/featureFlags.js create mode 100644 db/migrate/20221017201914_add_features_to_accounts.rb diff --git a/app/javascript/dashboard/components/layout/Sidebar.vue b/app/javascript/dashboard/components/layout/Sidebar.vue index 314b594d2..b919be2cd 100644 --- a/app/javascript/dashboard/components/layout/Sidebar.vue +++ b/app/javascript/dashboard/components/layout/Sidebar.vue @@ -73,14 +73,14 @@ export default { computed: { ...mapGetters({ - currentUser: 'getCurrentUser', - globalConfig: 'globalConfig/get', - isACustomBrandedInstance: 'globalConfig/isACustomBrandedInstance', - isOnChatwootCloud: 'globalConfig/isOnChatwootCloud', - inboxes: 'inboxes/getInboxes', accountId: 'getCurrentAccountId', currentRole: 'getCurrentRole', + currentUser: 'getCurrentUser', + globalConfig: 'globalConfig/get', + inboxes: 'inboxes/getInboxes', + isACustomBrandedInstance: 'globalConfig/isACustomBrandedInstance', isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount', + isOnChatwootCloud: 'globalConfig/isOnChatwootCloud', labels: 'labels/getLabelsOnSidebar', teams: 'teams/getMyTeams', }), diff --git a/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js b/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js index 83a0c2309..768e42ea5 100644 --- a/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js +++ b/app/javascript/dashboard/components/layout/config/sidebarItems/settings.js @@ -1,3 +1,4 @@ +import { FEATURE_FLAGS } from '../../../../featureFlags'; import { frontendURL } from '../../../../helper/URLHelper'; const settings = accountId => ({ @@ -38,12 +39,20 @@ const settings = accountId => ({ 'settings_teams_new', ], menuItems: [ + { + icon: 'briefcase', + label: 'ACCOUNT_SETTINGS', + hasSubMenu: false, + toState: frontendURL(`accounts/${accountId}/settings/general`), + toStateName: 'general_settings_index', + }, { icon: 'people', label: 'AGENTS', hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/agents/list`), toStateName: 'agent_list', + featureFlag: FEATURE_FLAGS.AGENT_MANAGEMENT, }, { icon: 'people-team', @@ -51,6 +60,7 @@ const settings = accountId => ({ hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/teams/list`), toStateName: 'settings_teams_list', + featureFlag: FEATURE_FLAGS.TEAM_MANAGEMENT, }, { icon: 'mail-inbox-all', @@ -58,6 +68,7 @@ const settings = accountId => ({ hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/inboxes/list`), toStateName: 'settings_inbox_list', + featureFlag: FEATURE_FLAGS.INBOX_MANAGEMENT, }, { icon: 'tag', @@ -65,6 +76,7 @@ const settings = accountId => ({ hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/labels/list`), toStateName: 'labels_list', + featureFlag: FEATURE_FLAGS.LABELS, }, { icon: 'code', @@ -74,6 +86,7 @@ const settings = accountId => ({ `accounts/${accountId}/settings/custom-attributes/list` ), toStateName: 'attributes_list', + featureFlag: FEATURE_FLAGS.CUSTOM_ATTRIBUTES, }, { icon: 'automation', @@ -82,6 +95,7 @@ const settings = accountId => ({ hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/automation/list`), toStateName: 'automation_list', + featureFlag: FEATURE_FLAGS.AUTOMATIONS, }, { icon: 'bot', @@ -90,7 +104,7 @@ const settings = accountId => ({ hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/agent-bots`), toStateName: 'agent_bots', - featureFlagKey: 'agent_bots', + featureFlag: FEATURE_FLAGS.AGENT_BOTS, }, { icon: 'flash-settings', @@ -99,7 +113,7 @@ const settings = accountId => ({ toState: frontendURL(`accounts/${accountId}/settings/macros`), toStateName: 'macros_wrapper', beta: true, - featureFlagKey: 'macros', + featureFlag: FEATURE_FLAGS.MACROS, }, { icon: 'chat-multiple', @@ -109,6 +123,7 @@ const settings = accountId => ({ `accounts/${accountId}/settings/canned-response/list` ), toStateName: 'canned_list', + featureFlag: FEATURE_FLAGS.CANNED_RESPONSES, }, { icon: 'flash-on', @@ -116,6 +131,7 @@ const settings = accountId => ({ hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/integrations`), toStateName: 'settings_integrations', + featureFlag: FEATURE_FLAGS.INTEGRATIONS, }, { icon: 'star-emphasis', @@ -123,6 +139,7 @@ const settings = accountId => ({ hasSubMenu: false, toState: frontendURL(`accounts/${accountId}/settings/applications`), toStateName: 'settings_applications', + featureFlag: FEATURE_FLAGS.INTEGRATIONS, }, { icon: 'credit-card-person', @@ -132,13 +149,6 @@ const settings = accountId => ({ toStateName: 'billing_settings_index', showOnlyOnCloud: true, }, - { - icon: 'settings', - label: 'ACCOUNT_SETTINGS', - hasSubMenu: false, - toState: frontendURL(`accounts/${accountId}/settings/general`), - toStateName: 'general_settings_index', - }, ], }); diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue index e68fcc3a4..b8aa21b30 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue @@ -20,6 +20,8 @@ import { frontendURL } from '../../../helper/URLHelper'; import SecondaryNavItem from './SecondaryNavItem.vue'; import AccountContext from './AccountContext.vue'; +import { mapGetters } from 'vuex'; +import { FEATURE_FLAGS } from '../../../featureFlags'; export default { components: { @@ -61,6 +63,10 @@ export default { }, }, computed: { + ...mapGetters({ + accountId: 'getCurrentAccountId', + isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount', + }), hasSecondaryMenu() { return this.menuConfig.menuItems && this.menuConfig.menuItems.length; }, @@ -89,7 +95,7 @@ export default { icon: 'folder', label: 'INBOXES', hasSubMenu: true, - newLink: true, + newLink: this.showNewLink(FEATURE_FLAGS.INBOX_MANAGEMENT), newLinkTag: 'NEW_INBOX', key: 'inbox', toState: frontendURL(`accounts/${this.accountId}/settings/inboxes/new`), @@ -117,7 +123,7 @@ export default { icon: 'number-symbol', label: 'LABELS', hasSubMenu: true, - newLink: true, + newLink: this.showNewLink(FEATURE_FLAGS.TEAM_MANAGEMENT), newLinkTag: 'NEW_LABEL', key: 'label', toState: frontendURL(`accounts/${this.accountId}/settings/labels`), @@ -141,7 +147,7 @@ export default { label: 'TAGGED_WITH', hasSubMenu: true, key: 'label', - newLink: true, + newLink: this.showNewLink(FEATURE_FLAGS.TEAM_MANAGEMENT), newLinkTag: 'NEW_LABEL', toState: frontendURL(`accounts/${this.accountId}/settings/labels`), toStateName: 'labels_list', @@ -163,7 +169,7 @@ export default { icon: 'people-team', label: 'TEAMS', hasSubMenu: true, - newLink: true, + newLink: this.showNewLink(FEATURE_FLAGS.TEAM_MANAGEMENT), newLinkTag: 'NEW_TEAM', key: 'team', toState: frontendURL(`accounts/${this.accountId}/settings/teams/new`), @@ -238,6 +244,9 @@ export default { toggleAccountModal() { this.$emit('toggle-accounts'); }, + showNewLink(featureFlag) { + return this.isFeatureEnabledonAccount(this.accountId, featureFlag); + }, }, }; diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue index cd155bce8..8661a488f 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue @@ -123,12 +123,12 @@ export default { return !!this.menuItem.children; }, isMenuItemVisible() { - if (!this.menuItem.featureFlagKey) { + if (!this.menuItem.featureFlag) { return true; } return this.isFeatureEnabledonAccount( this.accountId, - this.menuItem.featureFlagKey + this.menuItem.featureFlag ); }, isInboxConversation() { @@ -217,7 +217,7 @@ export default { } }, showItem(item) { - return this.isAdmin && item.newLink !== undefined; + return this.isAdmin && !!item.newLink; }, onClickOpen() { this.$emit('open'); diff --git a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue index 9ace1ceb2..d26264cc2 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue @@ -110,13 +110,15 @@ import { hasPressedAltAndAKey } from 'shared/helpers/KeyboardHelpers'; import eventListenerMixins from 'shared/mixins/eventListenerMixins'; import uiSettingsMixin from 'dashboard/mixins/uiSettings'; import inboxMixin from 'shared/mixins/inboxMixin'; - +import { FEATURE_FLAGS } from 'dashboard/featureFlags'; import { ALLOWED_FILE_TYPES, ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP, } from 'shared/constants/messages'; import { REPLY_EDITOR_MODES } from './constants'; +import { mapGetters } from 'vuex'; + export default { name: 'ReplyBottomPanel', components: { FileUpload }, @@ -200,6 +202,10 @@ export default { }, }, computed: { + ...mapGetters({ + accountId: 'getCurrentAccountId', + isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount', + }), isNote() { return this.mode === REPLY_EDITOR_MODES.NOTE; }, @@ -217,7 +223,12 @@ export default { return this.showFileUpload || this.isNote; }, showAudioRecorderButton() { - return this.showAudioRecorder; + return ( + this.isFeatureEnabledonAccount( + this.accountId, + FEATURE_FLAGS.VOICE_RECORDER + ) && this.showAudioRecorder + ); }, showAudioPlayStopButton() { return this.showAudioRecorder && this.isRecordingAudio; diff --git a/app/javascript/dashboard/featureFlags.js b/app/javascript/dashboard/featureFlags.js new file mode 100644 index 000000000..c6bc736cf --- /dev/null +++ b/app/javascript/dashboard/featureFlags.js @@ -0,0 +1,13 @@ +export const FEATURE_FLAGS = { + AGENT_BOTS: 'agent_bots', + AGENT_MANAGEMENT: 'agent_management', + AUTOMATIONS: 'automations', + CANNED_RESPONSES: 'canned_responses', + CUSTOM_ATTRIBUTES: 'custom_attributes', + INBOX_MANAGEMENT: 'inbox_management', + INTEGRATIONS: 'integrations', + LABELS: 'labels', + MACROS: 'macros', + TEAM_MANAGEMENT: 'team_management', + VOICE_RECORDER: 'voice_recorder', +}; diff --git a/app/javascript/dashboard/routes/dashboard/commands/goToCommandHotKeys.js b/app/javascript/dashboard/routes/dashboard/commands/goToCommandHotKeys.js index 9a5b09f2e..b1d17dcbb 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/goToCommandHotKeys.js +++ b/app/javascript/dashboard/routes/dashboard/commands/goToCommandHotKeys.js @@ -16,6 +16,8 @@ import { ICON_CONVERSATION_REPORTS, } from './CommandBarIcons'; import { frontendURL } from '../../../helper/URLHelper'; +import { mapGetters } from 'vuex'; +import { FEATURE_FLAGS } from '../../../featureFlags'; const GO_TO_COMMANDS = [ { @@ -86,6 +88,7 @@ const GO_TO_COMMANDS = [ id: 'open_agent_settings', section: 'COMMAND_BAR.SECTIONS.SETTINGS', title: 'COMMAND_BAR.COMMANDS.GO_TO_SETTINGS_AGENTS', + featureFlag: FEATURE_FLAGS.AGENT_MANAGEMENT, icon: ICON_AGENT_REPORTS, path: accountId => `accounts/${accountId}/settings/agents/list`, role: ['administrator'], @@ -93,6 +96,7 @@ const GO_TO_COMMANDS = [ { id: 'open_team_settings', title: 'COMMAND_BAR.COMMANDS.GO_TO_SETTINGS_TEAMS', + featureFlag: FEATURE_FLAGS.TEAM_MANAGEMENT, section: 'COMMAND_BAR.SECTIONS.SETTINGS', icon: ICON_TEAM_REPORTS, path: accountId => `accounts/${accountId}/settings/teams/list`, @@ -101,6 +105,7 @@ const GO_TO_COMMANDS = [ { id: 'open_inbox_settings', title: 'COMMAND_BAR.COMMANDS.GO_TO_SETTINGS_INBOXES', + featureFlag: FEATURE_FLAGS.INBOX_MANAGEMENT, section: 'COMMAND_BAR.SECTIONS.SETTINGS', icon: ICON_INBOXES, path: accountId => `accounts/${accountId}/settings/inboxes/list`, @@ -109,6 +114,7 @@ const GO_TO_COMMANDS = [ { id: 'open_label_settings', title: 'COMMAND_BAR.COMMANDS.GO_TO_SETTINGS_LABELS', + featureFlag: FEATURE_FLAGS.LABELS, section: 'COMMAND_BAR.SECTIONS.SETTINGS', icon: ICON_LABELS, path: accountId => `accounts/${accountId}/settings/labels/list`, @@ -117,6 +123,7 @@ const GO_TO_COMMANDS = [ { id: 'open_canned_response_settings', title: 'COMMAND_BAR.COMMANDS.GO_TO_SETTINGS_CANNED_RESPONSES', + featureFlag: FEATURE_FLAGS.CANNED_RESPONSES, section: 'COMMAND_BAR.SECTIONS.SETTINGS', icon: ICON_CANNED_RESPONSE, path: accountId => `accounts/${accountId}/settings/canned-response/list`, @@ -125,6 +132,7 @@ const GO_TO_COMMANDS = [ { id: 'open_applications_settings', title: 'COMMAND_BAR.COMMANDS.GO_TO_SETTINGS_APPLICATIONS', + featureFlag: FEATURE_FLAGS.INTEGRATIONS, section: 'COMMAND_BAR.SECTIONS.SETTINGS', icon: ICON_APPS, path: accountId => `accounts/${accountId}/settings/applications`, @@ -158,8 +166,20 @@ const GO_TO_COMMANDS = [ export default { computed: { + ...mapGetters({ + accountId: 'getCurrentAccountId', + isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount', + }), goToCommandHotKeys() { - let commands = GO_TO_COMMANDS; + let commands = GO_TO_COMMANDS.filter(cmd => { + if (cmd.featureFlag) { + return this.isFeatureEnabledonAccount( + this.accountId, + cmd.featureFlag + ); + } + return true; + }); if (!this.isAdmin) { commands = commands.filter(command => command.role.includes('agent')); diff --git a/app/javascript/dashboard/routes/dashboard/settings/account/account.routes.js b/app/javascript/dashboard/routes/dashboard/settings/account/account.routes.js index c2ef17129..5e3df43b4 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/account/account.routes.js +++ b/app/javascript/dashboard/routes/dashboard/settings/account/account.routes.js @@ -10,7 +10,7 @@ export default { component: SettingsContent, props: { headerTitle: 'GENERAL_SETTINGS.TITLE', - icon: 'settings', + icon: 'briefcase', showNewButton: false, }, children: [ diff --git a/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js b/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js index 7c8cc11fa..794451e79 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js +++ b/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js @@ -25,7 +25,7 @@ export default { roles: ['administrator', 'agent'], redirect: () => { if (store.getters.getCurrentRole === 'administrator') { - return frontendURL('accounts/:accountId/settings/agents'); + return frontendURL('accounts/:accountId/settings/general'); } return frontendURL('accounts/:accountId/settings/canned-response'); }, diff --git a/app/javascript/shared/components/FluentIcon/dashboard-icons.json b/app/javascript/shared/components/FluentIcon/dashboard-icons.json index 9a5eaa893..bf6f4127d 100644 --- a/app/javascript/shared/components/FluentIcon/dashboard-icons.json +++ b/app/javascript/shared/components/FluentIcon/dashboard-icons.json @@ -31,6 +31,7 @@ "book-open-globe-outline": "M3.5 5.75a.25.25 0 0 1 .25-.25H10c.69 0 1.25.56 1.25 1.25v8.959a6.49 6.49 0 0 1 1.5-2.646V6.75c0-.69.56-1.25 1.25-1.25h6.25a.25.25 0 0 1 .25.25v5.982A6.518 6.518 0 0 1 22 12.81V5.75A1.75 1.75 0 0 0 20.25 4H14c-.788 0-1.499.331-2 .863A2.742 2.742 0 0 0 10 4H3.75A1.75 1.75 0 0 0 2 5.75v12.5c0 .966.784 1.75 1.75 1.75H10c.495 0 .96-.13 1.36-.36a6.473 6.473 0 0 1-.343-1.663A1.248 1.248 0 0 1 10 18.5H3.75a.25.25 0 0 1-.25-.25V5.75ZM16.007 17c.04-1.415.248-2.669.553-3.585.171-.513.364-.893.554-1.134.195-.247.329-.281.386-.281.057 0 .192.034.386.281.19.241.383.62.554 1.134.305.916.513 2.17.553 3.585h-2.986Zm-.396-3.9c.108-.323.23-.622.368-.887A5.504 5.504 0 0 0 12.023 17h2.984c.04-1.5.26-2.866.604-3.9Zm3.778 0a6.133 6.133 0 0 0-.368-.887A5.504 5.504 0 0 1 22.978 17h-2.985c-.04-1.5-.26-2.866-.604-3.9Zm.604 4.9h2.985a5.504 5.504 0 0 1-3.957 4.787c.138-.265.26-.564.368-.886.345-1.035.564-2.4.604-3.901Zm-2.107 4.719c-.194.247-.329.281-.386.281-.057 0-.191-.034-.386-.281-.19-.241-.383-.62-.554-1.135-.305-.915-.513-2.17-.553-3.584h2.986c-.04 1.415-.248 2.669-.553 3.584-.171.514-.364.894-.554 1.135ZM12.023 18a5.504 5.504 0 0 0 3.956 4.787 6.133 6.133 0 0 1-.367-.886c-.346-1.035-.565-2.4-.605-3.901h-2.984Z", "bot-outline": "M17.753 14a2.25 2.25 0 0 1 2.25 2.25v.905a3.75 3.75 0 0 1-1.307 2.846C17.13 21.345 14.89 22 12 22c-2.89 0-5.128-.656-6.691-2a3.75 3.75 0 0 1-1.306-2.843v-.908A2.25 2.25 0 0 1 6.253 14h11.5Zm0 1.5h-11.5a.75.75 0 0 0-.75.75v.908c0 .655.286 1.278.784 1.706C7.545 19.945 9.44 20.502 12 20.502c2.56 0 4.458-.557 5.719-1.64a2.25 2.25 0 0 0 .784-1.706v-.906a.75.75 0 0 0-.75-.75ZM11.898 2.008 12 2a.75.75 0 0 1 .743.648l.007.102V3.5h3.5a2.25 2.25 0 0 1 2.25 2.25v4.505a2.25 2.25 0 0 1-2.25 2.25h-8.5a2.25 2.25 0 0 1-2.25-2.25V5.75A2.25 2.25 0 0 1 7.75 3.5h3.5v-.749a.75.75 0 0 1 .648-.743L12 2l-.102.007ZM16.25 5h-8.5a.75.75 0 0 0-.75.75v4.505c0 .414.336.75.75.75h8.5a.75.75 0 0 0 .75-.75V5.75a.75.75 0 0 0-.75-.75Zm-6.5 1.5a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5Zm4.492 0a1.25 1.25 0 1 1 0 2.499 1.25 1.25 0 0 1 0-2.499Z", "building-bank-outline": "M13.032 2.325a1.75 1.75 0 0 0-2.064 0L3.547 7.74c-.978.713-.473 2.26.736 2.26H4.5v5.8A2.75 2.75 0 0 0 3 18.25v1.5c0 .413.336.75.75.75h16.5a.75.75 0 0 0 .75-.75v-1.5a2.75 2.75 0 0 0-1.5-2.45V10h.217c1.21 0 1.713-1.547.736-2.26l-7.421-5.416Zm-1.18 1.211a.25.25 0 0 1 .295 0L18.95 8.5H5.05l6.803-4.964ZM18 10v5.5h-2V10h2Zm-3.5 0v5.5h-1.75V10h1.75Zm-3.25 0v5.5H9.5V10h1.75Zm-5.5 7h12.5c.69 0 1.25.56 1.25 1.25V19h-15v-.75c0-.69.56-1.25 1.25-1.25ZM6 15.5V10h2v5.5H6Z", + "briefcase-outline": "M8.75 3h6.5a.75.75 0 0 1 .743.648L16 3.75V7h1.75A3.25 3.25 0 0 1 21 10.25v6.5A3.25 3.25 0 0 1 17.75 20H6.25A3.25 3.25 0 0 1 3 16.75v-6.5A3.25 3.25 0 0 1 6.25 7H8V3.75a.75.75 0 0 1 .648-.743L8.75 3h6.5-6.5Zm9 5.5H6.25a1.75 1.75 0 0 0-1.75 1.75v6.5c0 .966.784 1.75 1.75 1.75h11.5a1.75 1.75 0 0 0 1.75-1.75v-6.5a1.75 1.75 0 0 0-1.75-1.75Zm-3.25-4h-5V7h5V4.5Z", "calendar-clock-outline": [ "M21 6.25A3.25 3.25 0 0 0 17.75 3H6.25A3.25 3.25 0 0 0 3 6.25v11.5A3.25 3.25 0 0 0 6.25 21h5.772a6.471 6.471 0 0 1-.709-1.5H6.25a1.75 1.75 0 0 1-1.75-1.75V8.5h15v2.813a6.471 6.471 0 0 1 1.5.709V6.25ZM6.25 4.5h11.5c.966 0 1.75.784 1.75 1.75V7h-15v-.75c0-.966.784-1.75 1.75-1.75Z", "M23 17.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Zm-5.5 0h2a.5.5 0 0 1 0 1H17a.5.5 0 0 1-.5-.491v-3.01a.5.5 0 0 1 1 0V17.5Z" diff --git a/app/views/fields/account_features_field/_form.html.erb b/app/views/fields/account_features_field/_form.html.erb index 823a043a6..c067e81c1 100644 --- a/app/views/fields/account_features_field/_form.html.erb +++ b/app/views/fields/account_features_field/_form.html.erb @@ -4,5 +4,6 @@
<% field.data.each do |key,val| %> <%= key %>: <%= check_box "enabled_features", "feature_#{key}", { checked: val }, true, false %> +
<% end %>
diff --git a/config/features.yml b/config/features.yml index ec779cd08..77d594fcf 100644 --- a/config/features.yml +++ b/config/features.yml @@ -19,3 +19,21 @@ enabled: false - name: macros enabled: false +- name: agent_management + enabled: true +- name: team_management + enabled: true +- name: inbox_management + enabled: true +- name: labels + enabled: true +- name: custom_attributes + enabled: true +- name: automations + enabled: true +- name: canned_responses + enabled: true +- name: integrations + enabled: true +- name: voice_recorder + enabled: true diff --git a/db/migrate/20221017201914_add_features_to_accounts.rb b/db/migrate/20221017201914_add_features_to_accounts.rb new file mode 100644 index 000000000..60f47f7d3 --- /dev/null +++ b/db/migrate/20221017201914_add_features_to_accounts.rb @@ -0,0 +1,20 @@ +class AddFeaturesToAccounts < ActiveRecord::Migration[6.1] + def change + Account.find_in_batches do |account_batch| + account_batch.each do |account| + account.enable_features( + 'agent_management', + 'automations', + 'canned_responses', + 'custom_attributes', + 'inbox_management', + 'integrations', + 'labels', + 'team_management', + 'voice_recorder' + ) + account.save! + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index d719bc9f7..d989b754b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_10_10_212946) do +ActiveRecord::Schema.define(version: 2022_10_17_201914) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" From 444809cc683b4b107569d5194440cc59c38b17a8 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Tue, 18 Oct 2022 03:30:02 +0530 Subject: [PATCH 14/35] fix: Added validation for check box in the pre-chat form (#5648) --- app/javascript/widget/components/PreChat/Form.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/widget/components/PreChat/Form.vue b/app/javascript/widget/components/PreChat/Form.vue index 1649068c6..3da347966 100644 --- a/app/javascript/widget/components/PreChat/Form.vue +++ b/app/javascript/widget/components/PreChat/Form.vue @@ -245,6 +245,7 @@ export default { text: null, select: null, number: null, + checkbox: false, }; const validationKeys = Object.keys(validations); const validation = 'bail|required'; From 1c44e43c437ebc6dba1dab92a710c08ccf2594bd Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Tue, 18 Oct 2022 04:29:18 +0530 Subject: [PATCH 15/35] fix: Fix overflow issue for category name in article list (#5658) Co-authored-by: Pranav Raj S --- .../helpcenter/components/ArticleItem.vue | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue index b602baaa8..254302074 100644 --- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue @@ -4,7 +4,7 @@
-
+
{{ title }}
@@ -20,7 +20,12 @@ class="fs-small button clear link secondary" :to="getCategoryRoute(category.slug)" > - {{ category.name }} + + {{ category.name }} + @@ -155,4 +160,9 @@ td { } } } + +.category-link-content { + max-width: 16rem; + line-height: 1.5; +} From 2423def8e8c62e885d72da9eb9812e8cdc624d64 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 17 Oct 2022 17:36:56 -0700 Subject: [PATCH 16/35] chore: Add attachments key to `message_created` webhook payload (#5659) - Add attachments key to `message_created` webhook payload --- app/listeners/webhook_listener.rb | 2 +- app/models/message.rb | 4 +++- app/models/webhook.rb | 4 ++-- spec/models/message_spec.rb | 15 +++++++++++++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/listeners/webhook_listener.rb b/app/listeners/webhook_listener.rb index 11a86e639..81d2c6b0a 100644 --- a/app/listeners/webhook_listener.rb +++ b/app/listeners/webhook_listener.rb @@ -54,7 +54,7 @@ class WebhookListener < BaseListener private def deliver_account_webhooks(payload, inbox) - inbox.account.webhooks.account.each do |webhook| + inbox.account.webhooks.account_type.each do |webhook| next unless webhook.subscriptions.include?(payload[:event]) WebhookJob.perform_later(webhook.url, payload) diff --git a/app/models/message.rb b/app/models/message.rb index 2930786f9..a3f63dbe9 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -116,7 +116,7 @@ class Message < ApplicationRecord end def webhook_data - { + data = { account: account.webhook_data, additional_attributes: additional_attributes, content_attributes: content_attributes, @@ -131,6 +131,8 @@ class Message < ApplicationRecord sender: sender.try(:webhook_data), source_id: source_id } + data.merge!(attachments: attachments.map(&:push_event_data)) if attachments.present? + data end def content diff --git a/app/models/webhook.rb b/app/models/webhook.rb index fe97fe583..5b0095093 100644 --- a/app/models/webhook.rb +++ b/app/models/webhook.rb @@ -5,7 +5,7 @@ # id :bigint not null, primary key # subscriptions :jsonb # url :string -# webhook_type :integer default("account") +# webhook_type :integer default("account_type") # created_at :datetime not null # updated_at :datetime not null # account_id :integer @@ -23,7 +23,7 @@ class Webhook < ApplicationRecord validates :account_id, presence: true validates :url, uniqueness: { scope: [:account_id] }, format: URI::DEFAULT_PARSER.make_regexp(%w[http https]) validate :validate_webhook_subscriptions - enum webhook_type: { account: 0, inbox: 1 } + enum webhook_type: { account_type: 0, inbox_type: 1 } ALLOWED_WEBHOOK_EVENTS = %w[conversation_status_changed conversation_updated conversation_created message_created message_updated webwidget_triggered].freeze diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index 4940d704a..aeff941da 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -33,6 +33,21 @@ RSpec.describe Message, type: :model do end end + context 'with webhook_data' do + it 'contains the message attachment when attachment is present' do + message = create(:message) + attachment = message.attachments.new(account_id: message.account_id, file_type: :image) + attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png') + attachment.save! + expect(message.webhook_data.key?(:attachments)).to be true + end + + it 'does not contain the message attachment when attachment is not present' do + message = create(:message) + expect(message.webhook_data.key?(:attachments)).to be false + end + end + context 'when message is created' do let(:message) { build(:message, account: create(:account)) } From e19c6d567137a9620801233854d2ade13c2d4017 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Mon, 17 Oct 2022 18:52:51 -0700 Subject: [PATCH 17/35] chore: Add editor toggle for API inbox (#5660) --- .../layout/sidebarComponents/Secondary.vue | 1 - .../widgets/WootWriter/ReplyBottomPanel.vue | 13 +++++++++++-- .../widgets/conversation/ReplyBox.vue | 17 +++++++++++++++-- .../dashboard/store/modules/inboxes.js | 4 ++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue index b8aa21b30..1d8ced445 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue @@ -64,7 +64,6 @@ export default { }, computed: { ...mapGetters({ - accountId: 'getCurrentAccountId', isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount', }), hasSecondaryMenu() { diff --git a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue index d26264cc2..a8c26201e 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue @@ -11,7 +11,6 @@ size="small" @click="toggleEmojiPicker" /> - + { return !template.components.some( i => i.format === 'IMAGE' || i.format === 'VIDEO' From 71ca530292738e460563f57dc18bb550a6e60768 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Mon, 17 Oct 2022 18:59:22 -0700 Subject: [PATCH 18/35] fix: Fix typo in help center (#5661) --- app/javascript/dashboard/i18n/locale/en/helpCenter.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/dashboard/i18n/locale/en/helpCenter.json b/app/javascript/dashboard/i18n/locale/en/helpCenter.json index 84ba9b8c9..1f22ba5bd 100644 --- a/app/javascript/dashboard/i18n/locale/en/helpCenter.json +++ b/app/javascript/dashboard/i18n/locale/en/helpCenter.json @@ -307,7 +307,7 @@ "PUBLISH_ARTICLE": { "API": { "ERROR": "Error while publishing article", - "SUCCESS": "Article publishied successfully" + "SUCCESS": "Article published successfully" } }, "ARCHIVE_ARTICLE": { From 2f7a16ae16848449f30cbd9ecb2ff85d7c822371 Mon Sep 17 00:00:00 2001 From: Sojan Date: Mon, 17 Oct 2022 19:31:53 -0700 Subject: [PATCH 19/35] Bump version to 2.10.0 --- config/app.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/app.yml b/config/app.yml index d5b65c1fc..518c981ac 100644 --- a/config/app.yml +++ b/config/app.yml @@ -1,5 +1,5 @@ shared: &shared - version: '2.9.1' + version: '2.10.0' development: <<: *shared diff --git a/package.json b/package.json index a62526801..d3476875b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chatwoot/chatwoot", - "version": "2.9.1", + "version": "2.10.0", "license": "MIT", "scripts": { "eslint": "eslint app/**/*.{js,vue}", From e34e975776f52068c797221b0e4bb935f06dd21a Mon Sep 17 00:00:00 2001 From: smartdev58 <30475578+smartdev58@users.noreply.github.com> Date: Tue, 18 Oct 2022 10:05:28 +0200 Subject: [PATCH 20/35] chore: ability to delete user in super admin console fixes: #4164 --- app/models/user.rb | 2 +- config/routes.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 6ed95ddb3..743471049 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -96,7 +96,7 @@ class User < ApplicationRecord class_name: :PortalMember, dependent: :destroy_async has_many :portals, - through: :portals_members, + through: :portal_members, class_name: :Portal, dependent: :nullify, source: :portal diff --git a/config/routes.rb b/config/routes.rb index 8b9b43863..b5a5cbdc7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -346,7 +346,7 @@ Rails.application.routes.draw do resources :accounts, only: [:index, :new, :create, :show, :edit, :update] do post :seed, on: :member end - resources :users, only: [:index, :new, :create, :show, :edit, :update] + resources :users, only: [:index, :new, :create, :show, :edit, :update, :destroy] resources :access_tokens, only: [:index, :show] resources :installation_configs, only: [:index, :new, :create, :show, :edit, :update] resources :agent_bots, only: [:index, :new, :create, :show, :edit, :update] From 2e7ab484bd9d28c1f37b0d77cb92f8ff6bf947ad Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Wed, 19 Oct 2022 01:46:29 +0530 Subject: [PATCH 21/35] fix: Discard invalid contact attributes in widget conversation end point (#5664) Fixes: chatwoot/product#601 --- .../api/v1/widget/conversations_controller.rb | 3 ++- .../widget/conversations_controller_spec.rb | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/widget/conversations_controller.rb b/app/controllers/api/v1/widget/conversations_controller.rb index 83206de90..9b8c4da81 100644 --- a/app/controllers/api/v1/widget/conversations_controller.rb +++ b/app/controllers/api/v1/widget/conversations_controller.rb @@ -17,7 +17,8 @@ class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController @contact = ContactIdentifyAction.new( contact: @contact, params: { email: contact_email, phone_number: contact_phone_number, name: contact_name }, - retain_original_contact_name: true + retain_original_contact_name: true, + discard_invalid_attrs: true ).perform end diff --git a/spec/controllers/api/v1/widget/conversations_controller_spec.rb b/spec/controllers/api/v1/widget/conversations_controller_spec.rb index 6f6f30e8e..cf386a65e 100644 --- a/spec/controllers/api/v1/widget/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/widget/conversations_controller_spec.rb @@ -102,6 +102,29 @@ RSpec.describe '/api/v1/widget/conversations/toggle_typing', type: :request do expect(json_response['custom_attributes']['order_id']).to eq '12345' expect(json_response['messages'][0]['content']).to eq 'This is a test message' end + + it 'doesnt not add phone number if the invalid phone number is provided' do + existing_contact = create(:contact, account: account) + + post '/api/v1/widget/conversations', + headers: { 'X-Auth-Token' => token }, + params: { + website_token: web_widget.website_token, + contact: { + name: 'contact-name-1', + email: existing_contact.email, + phone_number: '13456' + }, + message: { + content: 'This is a test message' + } + }, + as: :json + + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(json_response['contact']['phone_number']).to be_nil + end end describe 'POST /api/v1/widget/conversations/toggle_typing' do From 3de8f256cb0affd03a78279f4720b6cfc01ad87f Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Date: Thu, 20 Oct 2022 00:53:53 +0530 Subject: [PATCH 22/35] fix: Avoid overflowing submenus in conversation context menu (#5113) * Scroll overflowing content inside submenus * Disable submenus if options are not available --- .../conversation/contextMenu/Index.vue | 15 ++++++++++--- .../conversation/contextMenu/menuItem.vue | 3 +-- .../contextMenu/menuItemWithSubmenu.vue | 21 +++++++++++++++++-- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue b/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue index 4a1713165..a8a5f3cb3 100644 --- a/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue +++ b/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue @@ -17,7 +17,10 @@ @click="snoozeConversation(option.snoozedUntil)" /> - + - + - + - +
@@ -50,7 +50,6 @@ export default { padding: var(--space-smaller); border-radius: var(--border-radius-small); overflow: hidden; - .menu-label { margin: 0; font-size: var(--font-size-mini); diff --git a/app/javascript/dashboard/components/widgets/conversation/contextMenu/menuItemWithSubmenu.vue b/app/javascript/dashboard/components/widgets/conversation/contextMenu/menuItemWithSubmenu.vue index 08922bc37..04870ccc1 100644 --- a/app/javascript/dashboard/components/widgets/conversation/contextMenu/menuItemWithSubmenu.vue +++ b/app/javascript/dashboard/components/widgets/conversation/contextMenu/menuItemWithSubmenu.vue @@ -1,11 +1,14 @@