Browse Source

Fixes #5224 - WhatsApp Feedback - 24h Window Reminder Message / Emoji-Reactions.
Fixes #5163 - Display Issue - empty article and missing picture when sending multiple pictures over WhatsApp to Zammad.
Fixes #5164 - Duplicate articles created when sending multiple pictures over WhatsApp.
Fixes #5187 - Updated by information gets overwritten by handling of 'Sent' event in a WhatsApp Business ticket.

Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Florian Liebe <fl@zammad.com>
Co-authored-by: Tobias Schäfer <ts@zammad.com>

Dusan Vuckovic 5 months ago
parent
commit
c8ff2f0b2a

+ 7 - 0
app/assets/javascripts/app/controllers/_application_controller/_modal_generic_history.coffee

@@ -126,6 +126,13 @@ class App.GenericHistory extends App.ControllerModal
           App.i18n.translatePlain("checked checklist item '%s'",  item.value_from)
         else
           App.i18n.translatePlain("unchecked checklist item '%s'", item.value_from)
+      else if item.attribute is 'reaction'
+        article_body = App.TicketArticle.find(item.o_id)?.body
+        truncated_article_body = App.Utils.truncate(article_body) or '-'
+        content = if item.type is 'created' or item.type is 'updated'
+          App.i18n.translatePlain("reacted with a %s to %s message '%s'",  item.value_to, item.value_from, truncated_article_body)
+        else if item.type is 'removed'
+          App.i18n.translatePlain("removed reaction to %s message '%s'",  item.value_from, truncated_article_body)
       else
         content = "#{ @T( item.type ) } #{ @T(item.object) } "
         if item.attribute

+ 12 - 3
app/assets/javascripts/app/controllers/_channel/whatsapp.coffee

@@ -164,9 +164,12 @@ class WhatsappAccountPhoneNumberModal extends App.ControllerModal
   small: true
 
   content: =>
+    reminder_active = if _.isUndefined(@channel?.options?.reminder_active) then true else @channel.options.reminder_active
+
     content = $(App.view('whatsapp/account_phone_number')(
-      channel: @channel
-      params:  @params
+      channel:         @channel
+      params:          @params
+      reminder_active: reminder_active
     ))
 
     preselected_group_id = if @channel then @channel.group_id else 1
@@ -176,9 +179,15 @@ class WhatsappAccountPhoneNumberModal extends App.ControllerModal
       null: false
       default: true
       display: __('Automatic reminders')
-      value: if _.isUndefined(@channel?.options?.reminder_active) then true else @channel.options.reminder_active
+      value: reminder_active
     )
 
+    content.find('.js-switch input[name="reminder_active"]')
+      .off('change.reminder')
+      .on('change.reminder', (e) ->
+        $('.js-reminderMessage').toggle(e.target.checked)
+      )
+
     content.find('.js-messagesGroup').replaceWith App.UiElement.tree_select.render(
       name: 'group_id'
       multiple: false

+ 10 - 0
app/assets/javascripts/app/lib/app_post/utils.coffee

@@ -1611,3 +1611,13 @@ class App.Utils
       display_name = '"' + display_name.replace(/([\\"])/g, '\\$1') + '"'
 
     return display_name + ' <' + email + '>'
+
+  # Truncate the passed text to desired length
+  @truncate: (input, length = 100) ->
+    return input if not input
+
+    string = input.replace(/<([^>]+)>/g, '')
+
+    return string if string.length < length
+
+    string.substring(0, length) + '…'

+ 2 - 0
app/assets/javascripts/app/models/ticket_article.coffee

@@ -49,6 +49,8 @@ class App.TicketArticle extends App.Model
       return App.i18n.translateContent('%s created article for |%s|', item.created_by.displayName(), item.title)
     else if item.type is 'update'
       return App.i18n.translateContent('%s updated article for |%s|', item.created_by.displayName(), item.title)
+    else if item.type is 'update.reaction'
+      return App.i18n.translateContent('%s reacted with a %s to %s message |%s|', item.objectNative.preferences?.whatsapp?.reaction?.author, item.objectNative.preferences?.whatsapp?.reaction?.emoji, item.created_by.displayName(), App.Utils.truncate(item.objectNative.body) or '-')
     return "Unknow action for (#{@objectDisplayName()}/#{item.type}), extend activityMessage() of model."
 
   @contentAttachments: (article) ->

+ 5 - 0
app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco

@@ -141,6 +141,11 @@
           </div>
         <% end %>
         <%- @attachments %>
+        <% if @article.preferences?.whatsapp?.reaction?.emoji: %>
+          <div class="article-reaction">
+            <span><%= @article.preferences?.whatsapp?.reaction?.emoji %></span>
+          </div>
+        <% end %>
       </div>
     </div>
   </div>

+ 5 - 0
app/assets/javascripts/app/views/ticket_zoom/article_view_system.jst.eco

@@ -1,3 +1,8 @@
 <div class="small task-subline">
 "<%= @article.subject %>" -&gt; "<%= @article.to %>"
+<% if @article.preferences?.whatsapp?.reaction?.emoji: %>
+  <div class="article-reaction relative">
+    <span><%= @article.preferences?.whatsapp?.reaction?.emoji %></span>
+  </div>
+<% end %>
 </div>

+ 1 - 1
app/assets/javascripts/app/views/whatsapp/account_cloud_api.jst.eco

@@ -10,7 +10,7 @@
       <label for="business_id"><%- @T('WhatsApp Business Account ID') %><% if !@channel: %> <span>*</span><% end %></label>
     </div>
     <div class="controls">
-      <input id="business_id" type="text" name="business_id" value="<%= @params?.business_id || @channel?.options?.business_id %>" class="form-control">
+      <input id="business_id" type="text" name="business_id" value="<%= @params?.business_id || @channel?.options?.business_id %>" class="form-control" autocomplete="off">
     </div>
   </div>
 

+ 10 - 1
app/assets/javascripts/app/views/whatsapp/account_phone_number.jst.eco

@@ -38,10 +38,19 @@
     </div>
     <p class="help-text">
       <%- @T('Send out automatic reminders to customers asking them for reply if the 24-hour window is about to expire.') %>
-      <%- @T('To find out more about the 24-hour customer service window see %l.', 'https://admin-docs.zammad.org/en/latest/channels/whatsapp/index.html#limitations') %>
+      <%- @T('To find out more about the 24-hour customer service window see %l.', 'https://admin-docs.zammad.org/en/latest/channels/whatsapp/limitations.html') %>
     </p>
   </div>
 
+  <div class="input form-group js-reminderMessage" <% if not @reminder_active: %>style="display: none;"<% end %>>
+    <div class="formGroup-label">
+      <label for="reminder_message"><%- @T('Reminder message') %></label>
+    </div>
+    <div class="controls">
+      <textarea id="reminder_message" name="reminder_message" rows="3" maxlength="4096" placeholder="<%- @Ti('Hello, the customer service window for this conversation is about to expire, please reply to keep it open.') %>" class="form-control" autocomplete="off"><%= @params?.reminder_message || @channel?.options?.reminder_message %></textarea>
+    </div>
+  </div>
+
   <div class="input form-group">
     <div class="formGroup-label">
       <label for=""><%- @T('Group') %> <span>*</span></label>

+ 24 - 0
app/assets/stylesheets/zammad.scss

@@ -7066,6 +7066,30 @@ a.list-group-item.active > .badge,
   }
 }
 
+.article-reaction {
+  position: absolute;
+  bottom: -12px;
+  width: 24px;
+  height: 24px;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  background-color: var(--background-article-customer-meta);
+  border: 1px solid var(--border-alt);
+  border-radius: 5px;
+  cursor: default;
+
+  @include ltr(right, 7px);
+  @include rtl(left, 7px);
+
+  span {
+    width: 14px;
+    height: 14px;
+    font-size: 14px;
+    line-height: 14px;
+  }
+}
+
 .article-content-meta {
   position: absolute;
   width: 100%;

+ 6 - 0
app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/ArticleBubble/ArticleBubble.vue

@@ -20,6 +20,7 @@ import ArticleBubbleSecurityStatusBar from '#desktop/pages/ticket/components/Tic
 import ArticleBubbleSecurityWarning from '#desktop/pages/ticket/components/TicketDetailView/ArticleBubble/ArticleBubbleSecurityWarning.vue'
 import { useBubbleHeader } from '#desktop/pages/ticket/components/TicketDetailView/ArticleBubble/useBubbleHeader.ts'
 import { useBubbleStyleGuide } from '#desktop/pages/ticket/components/TicketDetailView/ArticleBubble/useBubbleStyleGuide.ts'
+import ArticleReactionBadge from '#desktop/pages/ticket/components/TicketDetailView/ArticleReactionBadge.vue'
 
 const ArticleBubbleHeader = defineAsyncComponent(
   () =>
@@ -185,5 +186,10 @@ const { showPreview } = useFilePreviewViewer(
     </div>
 
     <ArticleBubbleActionList :article="article" :position="position" />
+
+    <ArticleReactionBadge
+      :position="position"
+      :reaction="article.preferences?.whatsapp?.reaction?.emoji"
+    />
   </div>
 </template>

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