Implemented feature to transfer chat sessions to another chat topic.
This commit is contained in:
parent
3c788ca426
commit
133b570fba
15 changed files with 491 additions and 160 deletions
|
@ -4,11 +4,12 @@ class App.CustomerChat extends App.Controller
|
|||
'click .js-settings': 'settings'
|
||||
|
||||
elements:
|
||||
'.js-acceptChat': 'acceptChatElement'
|
||||
'.js-badgeWaitingCustomers': 'badgeWaitingCustomers'
|
||||
'.js-acceptChat': 'acceptChatElement'
|
||||
'.js-badgeWaitingCustomers': 'badgeWaitingCustomers'
|
||||
'.js-totalInfo': 'totalInfo'
|
||||
'.js-badgeChattingCustomers': 'badgeChattingCustomers'
|
||||
'.js-badgeActiveAgents': 'badgeActiveAgents'
|
||||
'.chat-workspace': 'workspace'
|
||||
'.js-badgeActiveAgents': 'badgeActiveAgents'
|
||||
'.chat-workspace': 'workspace'
|
||||
|
||||
sounds:
|
||||
chat_new: new Audio('assets/sounds/chat_new.mp3')
|
||||
|
@ -28,7 +29,9 @@ class App.CustomerChat extends App.Controller
|
|||
@meta =
|
||||
active: false
|
||||
waiting_chat_count: 0
|
||||
waiting_chat_count_by_chat: {}
|
||||
waiting_chat_session_list: []
|
||||
waiting_chat_session_list_by_chat: {}
|
||||
running_chat_count: 0
|
||||
running_chat_session_list: []
|
||||
active_agent_count: 0
|
||||
|
@ -99,65 +102,23 @@ class App.CustomerChat extends App.Controller
|
|||
|
||||
@html App.view('customer_chat/index')()
|
||||
|
||||
chatSessionList = (list) ->
|
||||
for chat_session in list
|
||||
chat = App.Chat.find(chat_session.chat_id)
|
||||
chat_session.name = "#{chat.displayName()} [##{chat_session.id}]"
|
||||
chat_session.geo_data = ''
|
||||
if chat_session.preferences && chat_session.preferences.geo_ip
|
||||
if chat_session.preferences.geo_ip.country_name
|
||||
chat_session.geo_data += chat_session.preferences.geo_ip.country_name
|
||||
if chat_session.preferences.geo_ip.city_name
|
||||
chat_session.geo_data += " #{chat_session.preferences.geo_ip.city_name}"
|
||||
if chat_session.user_id
|
||||
chat_session.user = App.User.find(chat_session.user_id)
|
||||
App.view('customer_chat/chat_list')(
|
||||
chat_sessions: list
|
||||
)
|
||||
|
||||
@el.find('.js-waitingCustomers .js-info').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
delay: 0
|
||||
placement: 'bottom'
|
||||
container: 'body' # place in body do prevent it from animating
|
||||
title: ->
|
||||
App.i18n.translateContent('Waiting Customers')
|
||||
content: =>
|
||||
chatSessionList(@meta.waiting_chat_session_list)
|
||||
chatSessionList: (list) ->
|
||||
list = [] if !list
|
||||
for chat_session in list
|
||||
chat = App.Chat.find(chat_session.chat_id)
|
||||
chat_session.name = "#{chat.displayName()} [##{chat_session.id}]"
|
||||
chat_session.geo_data = ''
|
||||
if chat_session.preferences && chat_session.preferences.geo_ip
|
||||
if chat_session.preferences.geo_ip.country_name
|
||||
chat_session.geo_data += chat_session.preferences.geo_ip.country_name
|
||||
if chat_session.preferences.geo_ip.city_name
|
||||
chat_session.geo_data += " #{chat_session.preferences.geo_ip.city_name}"
|
||||
if chat_session.user_id
|
||||
chat_session.user = App.User.find(chat_session.user_id)
|
||||
App.view('customer_chat/chat_list')(
|
||||
chat_sessions: list
|
||||
)
|
||||
|
||||
@el.find('.js-chattingCustomers .js-info').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
delay: 0
|
||||
placement: 'bottom'
|
||||
container: 'body'
|
||||
title: ->
|
||||
App.i18n.translateContent('Chatting Customers')
|
||||
content: =>
|
||||
chatSessionList(@meta.running_chat_session_list)
|
||||
)
|
||||
|
||||
@el.find('.js-activeAgents .js-info').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
delay: 0
|
||||
placement: 'bottom'
|
||||
container: 'body'
|
||||
title: ->
|
||||
App.i18n.translateContent('Active Agents')
|
||||
content: =>
|
||||
users = []
|
||||
for user_id in @meta.active_agent_ids
|
||||
users.push App.User.find(user_id)
|
||||
App.view('customer_chat/user_list')(
|
||||
users: users
|
||||
)
|
||||
)
|
||||
|
||||
show: (params) =>
|
||||
@title 'Customer Chat', true
|
||||
|
@ -248,15 +209,68 @@ class App.CustomerChat extends App.Controller
|
|||
@stopPushState()
|
||||
@pushState()
|
||||
|
||||
activeChatTopcis: =>
|
||||
preferences = @Session.get('preferences')
|
||||
return [] if !preferences
|
||||
return [] if !preferences.chat
|
||||
return [] if !preferences.chat.active
|
||||
chats = []
|
||||
for chat in App.Chat.all()
|
||||
if preferences.chat.active[chat.id] is 'on' || preferences.chat.active[chat.id.toString()] is 'on'
|
||||
chats.push chat
|
||||
chats
|
||||
|
||||
updateMeta: =>
|
||||
$('.popover').remove()
|
||||
activeChatTopcis = @activeChatTopcis()
|
||||
@$('.js-header').html(App.view('customer_chat/chat_header')(chats: activeChatTopcis))
|
||||
@refreshElements()
|
||||
if @meta.waiting_chat_count && @maxChatWindows > @windowCount()
|
||||
@acceptChatElement.addClass('is-clickable is-blinking')
|
||||
|
||||
# activate normal button
|
||||
@acceptChatElement.not('[data-chat-id]').addClass('is-active pulsate-animation')
|
||||
|
||||
# activate specific chat buttons
|
||||
if activeChatTopcis.length > 1
|
||||
for chat in activeChatTopcis
|
||||
if @meta.waiting_chat_count_by_chat[chat.id]
|
||||
@$(".js-header .js-acceptChat[data-chat-id=#{chat.id}]").addClass('is-active pulsate-animation').attr('disabled', false)
|
||||
@idleTimeoutStart()
|
||||
else
|
||||
@acceptChatElement.removeClass('is-clickable is-blinking')
|
||||
@acceptChatElement.removeClass('is-active pulsate-animation')
|
||||
@idleTimeoutStop()
|
||||
|
||||
@badgeWaitingCustomers.text(@meta.waiting_chat_count)
|
||||
if activeChatTopcis.length > 1
|
||||
for chat in App.Chat.all()
|
||||
do (chat) =>
|
||||
@$(".js-header .js-waitingCustomers[data-chat-id=#{chat.id}] .js-badgeWaitingCustomers").text(@meta.waiting_chat_count_by_chat[chat.id])
|
||||
@el.find(".js-waitingCustomers[data-chat-id=#{chat.id}] .js-info").popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
delay: 0
|
||||
placement: 'bottom'
|
||||
container: 'body' # place in body do prevent it from animating
|
||||
title: ->
|
||||
App.i18n.translateContent('Waiting Customers')
|
||||
content: =>
|
||||
@chatSessionList(@meta.waiting_chat_session_list_by_chat[chat.id])
|
||||
)
|
||||
else
|
||||
@badgeWaitingCustomers.text(@meta.waiting_chat_count)
|
||||
@el.find('.js-waitingCustomers .js-totalInfo').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
delay: 0
|
||||
placement: 'bottom'
|
||||
container: 'body' # place in body do prevent it from animating
|
||||
title: ->
|
||||
App.i18n.translateContent('Waiting Customers')
|
||||
content: =>
|
||||
@chatSessionList(@meta.waiting_chat_session_list_by_chat[activeChatTopcis[0].id])
|
||||
)
|
||||
|
||||
@badgeChattingCustomers.text(@meta.running_chat_count)
|
||||
@badgeActiveAgents.text(@meta.active_agent_count)
|
||||
|
||||
|
@ -266,6 +280,37 @@ class App.CustomerChat extends App.Controller
|
|||
@addChat(session)
|
||||
@meta.active_sessions = false
|
||||
|
||||
@el.find('.js-chattingCustomers .js-info').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
delay: 0
|
||||
placement: 'bottom'
|
||||
container: 'body'
|
||||
title: ->
|
||||
App.i18n.translateContent('Chatting Customers')
|
||||
content: =>
|
||||
@chatSessionList(@meta.running_chat_session_list)
|
||||
)
|
||||
|
||||
@el.find('.js-activeAgents .js-info').popover(
|
||||
trigger: 'hover'
|
||||
html: true
|
||||
animation: false
|
||||
delay: 0
|
||||
placement: 'bottom'
|
||||
container: 'body'
|
||||
title: ->
|
||||
App.i18n.translateContent('Active Agents')
|
||||
content: =>
|
||||
users = []
|
||||
for user_id in @meta.active_agent_ids
|
||||
users.push App.User.find(user_id)
|
||||
App.view('customer_chat/user_list')(
|
||||
users: users
|
||||
)
|
||||
)
|
||||
|
||||
@updateNavMenu()
|
||||
|
||||
addChat: (session) ->
|
||||
|
@ -298,9 +343,10 @@ class App.CustomerChat extends App.Controller
|
|||
for session_id, chat of @chatWindows
|
||||
chat.trigger('layout-changed')
|
||||
|
||||
acceptChat: =>
|
||||
acceptChat: (e) =>
|
||||
return if @windowCount() >= @maxChatWindows
|
||||
App.WebSocket.send(event:'chat_session_start')
|
||||
chat_id = $(e.currentTarget).attr('data-chat-id')
|
||||
App.WebSocket.send(event:'chat_session_start', chat_id: chat_id)
|
||||
@idleTimeoutStop()
|
||||
|
||||
settings: (params = {}) ->
|
||||
|
@ -344,6 +390,7 @@ class ChatWindow extends App.Controller
|
|||
'click .js-scrollHint': 'onScrollHintClick'
|
||||
'click .js-info': 'toggleMeta'
|
||||
'click .js-createTicket': 'ticketCreate'
|
||||
'click .js-transferChat': 'transfer'
|
||||
'submit .js-metaForm': 'sendMetaForm'
|
||||
|
||||
elements:
|
||||
|
@ -450,6 +497,7 @@ class ChatWindow extends App.Controller
|
|||
@html App.view('customer_chat/chat_window')(
|
||||
name: @name
|
||||
session: @session
|
||||
chats: App.Chat.all()
|
||||
)
|
||||
|
||||
@el.one('transitionend', @onTransitionend)
|
||||
|
@ -766,6 +814,12 @@ class ChatWindow extends App.Controller
|
|||
else if showHint
|
||||
@showScrollHint()
|
||||
|
||||
transfer: (e) =>
|
||||
e.preventDefault()
|
||||
chat_id = $(e.currentTarget).attr('data-chat-id')
|
||||
App.WebSocket.send(event:'chat_transfer', chat_id: chat_id, session_id: @session.id)
|
||||
@close()
|
||||
|
||||
ticketCreate: (e) =>
|
||||
e.preventDefault()
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<div class="status-fields">
|
||||
<% if @chats.length > 1: %>
|
||||
<div class="buttonDropdown dropdown">
|
||||
<div class="status-field status-field--spacer js-acceptChat">
|
||||
<span class="badge js-badgeWaitingCustomers"></span> <%- @T('Waiting Customers') %>
|
||||
</div>
|
||||
<div class="status-field status-field--arrow" data-toggle="dropdown">
|
||||
<%- @Icon('arrow-down') %>
|
||||
</div>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<% for chat in @chats: %>
|
||||
<li class="js-waitingCustomers js-acceptChat" disabled data-chat-id="<%= chat.id %>" role="menuitem">
|
||||
<span class="badge js-badgeWaitingCustomers"></span> <%- @T('Waiting in %s', chat.name) %> <span class="flex-spacer"></span>
|
||||
<div class="status-badge js-info">
|
||||
<div class="info-badge"><%- @Icon('info') %></div>
|
||||
</div>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% else: %>
|
||||
<div class="status-field js-acceptChat js-waitingCustomers">
|
||||
<span class="badge js-badgeWaitingCustomers"></span> <%- @T('Waiting Customers') %>
|
||||
<div class="status-badge js-totalInfo">
|
||||
<div class="info-badge"><%- @Icon('info') %></div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="status-field js-chattingCustomers">
|
||||
<span class="badge js-badgeChattingCustomers"></span> <%- @T('Chatting Customers') %>
|
||||
<div class="status-badge js-info">
|
||||
<div class="info-badge"><%- @Icon('info') %></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-field js-activeAgents">
|
||||
<span class="badge js-badgeActiveAgents"></span> <%- @T('Active Agents') %>
|
||||
<div class="status-badge js-info">
|
||||
<div class="info-badge"><%- @Icon('info') %></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -24,6 +24,15 @@
|
|||
<div class="chat-body js-body"></div>
|
||||
<div class="chat-body js-meta hidden">
|
||||
<% if @session: %>
|
||||
<%- @T('Transfer conversation to another chat:') %>
|
||||
<ul>
|
||||
<% for chat in @chats: %>
|
||||
<% if @session.chat_id isnt chat.id: %>
|
||||
<li><a data-chat-id="<%= chat.id %>" class="js-transferChat" href="#"><%- chat.name %></a>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><%- @T('Created at') %>: <%- @Ttimestamp(@session.created_at) %></li>
|
||||
<% if @session && @session.preferences: %>
|
||||
|
|
|
@ -3,28 +3,7 @@
|
|||
<div class="page-header-title">
|
||||
<h1><%- @T('Customer Chat') %></h1>
|
||||
</div>
|
||||
<div class="page-header-center">
|
||||
<div class="status-fields">
|
||||
<div class="status-field js-acceptChat js-waitingCustomers">
|
||||
<span class="badge js-badgeWaitingCustomers"></span> <%- @T('Waiting Customers') %>
|
||||
<div class="status-badge js-info">
|
||||
<div class="info-badge"><%- @Icon('info') %></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-field js-chattingCustomers">
|
||||
<span class="badge js-badgeChattingCustomers"></span> <%- @T('Chatting Customers') %>
|
||||
<div class="status-badge js-info">
|
||||
<div class="info-badge"><%- @Icon('info') %></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-field js-activeAgents">
|
||||
<span class="badge js-badgeActiveAgents"></span> <%- @T('Active Agents') %>
|
||||
<div class="status-badge js-info">
|
||||
<div class="info-badge"><%- @Icon('info') %></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-header-center js-header"></div>
|
||||
<div class="page-header-meta">
|
||||
<div class="btn btn--action js-settings"><%- @T('Settings') %></div>
|
||||
</div>
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
<%- @Icon('arrow-down', 'dropdown-arrow') %>
|
||||
</div>
|
||||
</div>
|
||||
<table class="settings-list">
|
||||
<table class="settings-list settings-list--stretch">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%- @T('Topic') %>
|
||||
<th width="100%"><%- @T('Greeting') %> (<%- @T('Separate multiple values by ;') %>)
|
||||
<th><%- @T('Greeting') %> (<%- @T('Separate multiple values by ;') %>)
|
||||
<th><%- @T('Enabled') %>
|
||||
</th></tr>
|
||||
</thead>
|
||||
|
|
|
@ -889,6 +889,23 @@ pre code.hljs {
|
|||
|
||||
.status-fields {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
|
||||
.dropdown li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.dropdown-menu .status-badge {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.dropdown-menu .badge {
|
||||
margin-top: -2px;
|
||||
margin-right: 7px;
|
||||
background: hsla(0,0%,0%,.5);
|
||||
}
|
||||
}
|
||||
|
||||
.status-field {
|
||||
|
@ -896,10 +913,15 @@ pre code.hljs {
|
|||
border: 1px solid hsl(0,0%,90%);
|
||||
display: flex;
|
||||
height: 34px;
|
||||
flex-shrink: 1;
|
||||
min-width: 20px;
|
||||
padding: 5px 0;
|
||||
align-items: center;
|
||||
line-height: 35px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
&.is-clickable {
|
||||
&.is-active {
|
||||
background: hsl(203,65%,55%);
|
||||
color: white;
|
||||
border-color: hsl(203,65%,45%);
|
||||
|
@ -908,10 +930,6 @@ pre code.hljs {
|
|||
@extend %clickable;
|
||||
}
|
||||
|
||||
&.is-blinking {
|
||||
animation: pulsate 667ms ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
&:not(:last-child):not(:only-child) {
|
||||
@include bidi-style(border-right-width, 0, border-left-width, 1px);
|
||||
}
|
||||
|
@ -919,6 +937,12 @@ pre code.hljs {
|
|||
&:first-child {
|
||||
border-radius: 5px 0 0 5px;
|
||||
@include rtl(border-radius, 0 5px 5px 0);
|
||||
|
||||
.dropdown.open & {
|
||||
border-radius: 5px 0 0 0;
|
||||
|
||||
@include rtl(border-radius, 0 5px 0 0);
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
@ -931,7 +955,7 @@ pre code.hljs {
|
|||
}
|
||||
|
||||
.badge {
|
||||
@include bidi-style(margin, 0 5px 0 10px, margin, 0 10px 0 5px);
|
||||
@include bidi-style(margin, 0 7px 0 10px, margin, 0 10px 0 7px);
|
||||
background: hsla(210,50%,10%,.24);
|
||||
}
|
||||
|
||||
|
@ -942,6 +966,22 @@ pre code.hljs {
|
|||
justify-content: center;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
&--arrow {
|
||||
@extend %clickable;
|
||||
border-left: none;
|
||||
width: 34px;
|
||||
justify-content: center;
|
||||
|
||||
.icon {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
&--spacer {
|
||||
padding-left: 16px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
|
@ -2945,6 +2985,10 @@ ol.tabs li {
|
|||
|
||||
.tab {
|
||||
flex: none;
|
||||
|
||||
&:nth-last-child(2) {
|
||||
@include bidi-style(border-right-width, 0, border-right-width, 1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7386,6 +7430,12 @@ footer {
|
|||
|
||||
.dropdown-menu > li[disabled] {
|
||||
opacity: 0.33;
|
||||
|
||||
&:hover,
|
||||
&.is-active {
|
||||
background: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu > li > a {
|
||||
|
@ -7404,7 +7454,7 @@ footer {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-menu .badge {
|
||||
.dropdown-menu .badge--text {
|
||||
@include bidi-style(padding-left, 10px, padding-right, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -201,6 +201,7 @@ returns
|
|||
end
|
||||
|
||||
running_chat_session_list_local = running_chat_session_list(chat_ids)
|
||||
|
||||
running_chat_session_list_local.each do |session|
|
||||
next if !session['user_id']
|
||||
|
||||
|
@ -211,16 +212,18 @@ returns
|
|||
end
|
||||
|
||||
{
|
||||
waiting_chat_count: waiting_chat_count(chat_ids),
|
||||
waiting_chat_session_list: waiting_chat_session_list(chat_ids),
|
||||
running_chat_count: running_chat_count(chat_ids),
|
||||
running_chat_session_list: running_chat_session_list_local,
|
||||
active_agent_count: active_agent_count(chat_ids),
|
||||
active_agent_ids: active_agent_ids,
|
||||
seads_available: seads_available(chat_ids),
|
||||
seads_total: seads_total(chat_ids),
|
||||
active: Chat::Agent.state(user_id),
|
||||
assets: assets,
|
||||
waiting_chat_count: waiting_chat_count(chat_ids),
|
||||
waiting_chat_count_by_chat: waiting_chat_count_by_chat(chat_ids),
|
||||
waiting_chat_session_list: waiting_chat_session_list(chat_ids),
|
||||
waiting_chat_session_list_by_chat: waiting_chat_session_list_by_chat(chat_ids),
|
||||
running_chat_count: running_chat_count(chat_ids),
|
||||
running_chat_session_list: running_chat_session_list_local,
|
||||
active_agent_count: active_agent_count(chat_ids),
|
||||
active_agent_ids: active_agent_ids,
|
||||
seads_available: seads_available(chat_ids),
|
||||
seads_total: seads_total(chat_ids),
|
||||
active: Chat::Agent.state(user_id),
|
||||
assets: assets,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -305,6 +308,14 @@ returns
|
|||
Chat::Session.where(state: ['waiting'], chat_id: chat_ids).count
|
||||
end
|
||||
|
||||
def self.waiting_chat_count_by_chat(chat_ids)
|
||||
list = {}
|
||||
Chat.where(active: true, id: chat_ids).pluck(:id).each do |chat_id|
|
||||
list[chat_id] = Chat::Session.where(chat_id: chat_id, state: ['waiting']).count
|
||||
end
|
||||
list
|
||||
end
|
||||
|
||||
def self.waiting_chat_session_list(chat_ids)
|
||||
sessions = []
|
||||
Chat::Session.where(state: ['waiting'], chat_id: chat_ids).each do |session|
|
||||
|
@ -313,6 +324,17 @@ returns
|
|||
sessions
|
||||
end
|
||||
|
||||
def self.waiting_chat_session_list_by_chat(chat_ids)
|
||||
sessions = {}
|
||||
Chat.where(active: true, id: chat_ids).pluck(:id).each do |chat_id|
|
||||
Chat::Session.where(chat_id: chat_id, state: ['waiting']).each do |session|
|
||||
sessions[chat_id] ||= []
|
||||
sessions[chat_id].push session.attributes
|
||||
end
|
||||
end
|
||||
sessions
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
get count running sessions in given chats
|
||||
|
|
|
@ -22,7 +22,11 @@ return is sent as message back to peer
|
|||
# find first in waiting list
|
||||
chat_user = User.lookup(id: @session['id'])
|
||||
chat_ids = Chat.agent_active_chat_ids(chat_user)
|
||||
chat_session = Chat::Session.where(state: 'waiting', chat_id: chat_ids).order(created_at: :asc).first
|
||||
chat_session = if @payload['chat_id']
|
||||
Chat::Session.where(state: 'waiting', chat_id: @payload['chat_id']).order(created_at: :asc).first
|
||||
else
|
||||
Chat::Session.where(state: 'waiting', chat_id: chat_ids).order(created_at: :asc).first
|
||||
end
|
||||
if !chat_session
|
||||
return {
|
||||
event: 'chat_session_start',
|
||||
|
@ -37,25 +41,32 @@ return is sent as message back to peer
|
|||
chat_session.preferences[:participants] = chat_session.add_recipient(@client_id)
|
||||
chat_session.save
|
||||
|
||||
# send chat_session_init to client
|
||||
user = chat_session.agent_user
|
||||
data = {
|
||||
event: 'chat_session_start',
|
||||
data: {
|
||||
state: 'ok',
|
||||
agent: user,
|
||||
session_id: chat_session.session_id,
|
||||
chat_id: chat_session.chat_id,
|
||||
},
|
||||
}
|
||||
# send to customer
|
||||
chat_session.send_to_recipients(data, @client_id)
|
||||
session_attributes = chat_session.attributes
|
||||
session_attributes['messages'] = []
|
||||
Chat::Message.where(chat_session_id: chat_session.id).order(created_at: :asc).each do |message|
|
||||
session_attributes['messages'].push message.attributes
|
||||
end
|
||||
|
||||
# send chat_session_init to customer client
|
||||
if session_attributes['messages'].blank?
|
||||
user = chat_session.agent_user
|
||||
data = {
|
||||
event: 'chat_session_start',
|
||||
data: {
|
||||
state: 'ok',
|
||||
agent: user,
|
||||
session_id: chat_session.session_id,
|
||||
chat_id: chat_session.chat_id,
|
||||
},
|
||||
}
|
||||
chat_session.send_to_recipients(data, @client_id)
|
||||
end
|
||||
|
||||
# send to agent
|
||||
data = {
|
||||
event: 'chat_session_start',
|
||||
data: {
|
||||
session: chat_session.attributes,
|
||||
session: session_attributes,
|
||||
},
|
||||
}
|
||||
Sessions.send(@client_id, data)
|
||||
|
|
39
lib/sessions/event/chat_transfer.rb
Normal file
39
lib/sessions/event/chat_transfer.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
class Sessions::Event::ChatTransfer < Sessions::Event::ChatBase
|
||||
|
||||
def run
|
||||
return super if super
|
||||
return if !permission_check('chat.agent', 'chat')
|
||||
|
||||
# find chat session
|
||||
chat_session = Chat::Session.find_by(id: @payload['session_id'])
|
||||
if !chat_session
|
||||
return {
|
||||
event: 'chat_session_start',
|
||||
data: {
|
||||
state: 'failed',
|
||||
message: 'No session available.',
|
||||
},
|
||||
}
|
||||
end
|
||||
chat_ids_to_notify = [chat_session.chat_id, @payload['chat_id']]
|
||||
chat_session.chat_id = @payload['chat_id']
|
||||
chat_session.state = 'waiting'
|
||||
chat_session.save
|
||||
|
||||
# send state update with sessions to agents
|
||||
Chat.broadcast_agent_state_update(chat_ids_to_notify)
|
||||
|
||||
# send transfer message to client
|
||||
message = {
|
||||
event: 'chat_session_notice',
|
||||
data: {
|
||||
session_id: chat_session.session_id,
|
||||
message: 'Conversation transfered into other chat. Please stay tuned.',
|
||||
},
|
||||
}
|
||||
chat_session.send_to_recipients(message, @client_id)
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
|
@ -753,6 +753,8 @@ do($ = window.jQuery, window) ->
|
|||
@onSessionClosed pipe.data
|
||||
when 'chat_session_left'
|
||||
@onSessionClosed pipe.data
|
||||
when 'chat_session_notice'
|
||||
@addStatus @T(pipe.data.message)
|
||||
when 'chat_status_customer'
|
||||
switch pipe.data.state
|
||||
when 'online'
|
||||
|
|
|
@ -1032,6 +1032,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
|
|||
case 'chat_session_left':
|
||||
this.onSessionClosed(pipe.data);
|
||||
break;
|
||||
case 'chat_session_notice':
|
||||
this.addStatus(this.T(pipe.data.message));
|
||||
break;
|
||||
case 'chat_status_customer':
|
||||
switch (pipe.data.state) {
|
||||
case 'online':
|
||||
|
|
3
public/assets/chat/chat.min.js
vendored
3
public/assets/chat/chat.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1858,42 +1858,40 @@ figcaption {
|
|||
}
|
||||
-->
|
||||
<script>
|
||||
var chat = new ZammadChat({
|
||||
chatId: 1,
|
||||
host: 'ws://localhost:6042',
|
||||
cssUrl: 'http://localhost:5000/assets/chat/chat.css',
|
||||
debug: true
|
||||
});
|
||||
function getSearchParameters() {
|
||||
var prmstr = window.location.search.substr(1);
|
||||
return prmstr != null && prmstr != '' ? transformToAssocArray(prmstr) : {};
|
||||
}
|
||||
function transformToAssocArray( prmstr ) {
|
||||
var params = {};
|
||||
var prmarr = prmstr.split('&');
|
||||
for ( var i = 0; i < prmarr.length; i++) {
|
||||
var tmparr = prmarr[i].split('=');
|
||||
params[tmparr[0]] = tmparr[1];
|
||||
}
|
||||
return params;
|
||||
}
|
||||
var hostname = window.location.hostname;
|
||||
var port = window.location.port;
|
||||
var params = getSearchParameters();
|
||||
var host = 'ws://'+ (location.host || 'localhost').split(':')[0] +':6042'
|
||||
if (params['port']) {
|
||||
host = 'ws://' + hostname + ':' + params['port']
|
||||
}
|
||||
cssUrl = 'http://' + hostname + ':' + port + '/assets/chat/chat.css'
|
||||
|
||||
$('.settings :input').on({
|
||||
change: function(){
|
||||
switch($(this).attr('data-option')){
|
||||
case "flat":
|
||||
$('.zammad-chat').toggleClass('zammad-chat--flat', this.checked);
|
||||
break;
|
||||
case "color":
|
||||
setScssVariable('themeColor', this.value);
|
||||
updateStyle();
|
||||
break;
|
||||
case "borderRadius":
|
||||
setScssVariable('borderRadius', this.value + "px");
|
||||
updateStyle();
|
||||
break;
|
||||
}
|
||||
},
|
||||
input: function(){
|
||||
switch($(this).attr('data-option')){
|
||||
case "borderRadius":
|
||||
$('[data-option="borderRadius"]').val(this.value);
|
||||
setScssVariable('borderRadius', this.value + "px");
|
||||
updateStyle();
|
||||
break;
|
||||
case "fontSize":
|
||||
$('[data-option="fontSize"]').val(this.value);
|
||||
setScssVariable('fontSize', this.value + "px");
|
||||
updateStyle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
var chat = new ZammadChat({
|
||||
chatId: 2,
|
||||
host: host,
|
||||
cssUrl: cssUrl,
|
||||
debug: true,
|
||||
background: '#494d52',
|
||||
flat: true,
|
||||
idleTimeout: 1,
|
||||
idleTimeoutIntervallCheck: 0.5,
|
||||
inactiveTimeout: 2,
|
||||
inactiveTimeoutIntervallCheck: 0.5,
|
||||
waitingListTimeout: 1.2,
|
||||
waitingListTimeoutIntervallCheck: 0.5,
|
||||
});
|
||||
</script>
|
|
@ -47,6 +47,12 @@ RSpec.describe Sessions::Event::ChatSessionStart do
|
|||
session: { 'id' => agent.id },
|
||||
)
|
||||
end
|
||||
let(:chat_message_history) do
|
||||
Chat::Message.create!(
|
||||
chat_session_id: chat_session.id,
|
||||
content: 'some message',
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
Setting.set('chat', true)
|
||||
|
@ -186,4 +192,37 @@ RSpec.describe Sessions::Event::ChatSessionStart do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when starting a chat session as agent with transfered conversation' do
|
||||
it 'send out chat_session_start to customer and agent with already created messages' do
|
||||
chat_message_history
|
||||
expect(subject_as_agent.run).to eq(nil)
|
||||
messages_to_customer = Sessions.queue('customer_session_id')
|
||||
expect(messages_to_customer.count).to eq(0)
|
||||
|
||||
messages_to_agent = Sessions.queue(client_id)
|
||||
expect(messages_to_agent.count).to eq(1)
|
||||
expect(messages_to_agent[0]).to include(
|
||||
'event' => 'chat_session_start',
|
||||
'data' => hash_including(
|
||||
'session' => hash_including(
|
||||
'user_id' => agent.id,
|
||||
'state' => 'running',
|
||||
'preferences' => hash_including(
|
||||
'participants' => ['customer_session_id', client_id]
|
||||
),
|
||||
'messages' => array_including(
|
||||
hash_including(
|
||||
'content' => 'some message',
|
||||
),
|
||||
),
|
||||
'id' => chat_session.id,
|
||||
'chat_id' => chat_session.chat_id,
|
||||
'session_id' => chat_session.session_id,
|
||||
'name' => nil,
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
84
spec/lib/sessions/event/chat_transfer_spec.rb
Normal file
84
spec/lib/sessions/event/chat_transfer_spec.rb
Normal file
|
@ -0,0 +1,84 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Sessions::Event::ChatTransfer do
|
||||
let(:client_id) { rand(123_456_789) }
|
||||
let(:chat) { Chat.first }
|
||||
let(:chat_transfer_into) { Chat.create!(name: 'chat 2', updated_by_id: 1, created_by_id: 1) }
|
||||
let(:chat_session) do
|
||||
Sessions.create('customer_session_id', { 'id' => customer.id }, {})
|
||||
Sessions.queue('customer_session_id')
|
||||
Chat::Session.create(
|
||||
chat_id: chat.id,
|
||||
user_id: nil,
|
||||
preferences: { participants: ['customer_session_id'] },
|
||||
state: 'running',
|
||||
)
|
||||
end
|
||||
let!(:agent) do
|
||||
create(:agent_user, preferences: { chat: { active: { chat.id.to_s => 'on' } } })
|
||||
end
|
||||
let!(:customer) { create(:customer_user) }
|
||||
let(:subject_as_agent) do
|
||||
Sessions.create(client_id, { 'id' => agent.id }, {})
|
||||
Sessions.queue(client_id)
|
||||
described_class.new(
|
||||
payload: { 'data' => { 'session_id' => chat_session.session_id }, 'session_id' => chat_session.id, 'chat_id' => chat_transfer_into.id },
|
||||
user_id: agent.id,
|
||||
client_id: client_id,
|
||||
clients: {},
|
||||
session: { 'id' => agent.id },
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
Setting.set('chat', true)
|
||||
end
|
||||
|
||||
context 'when transfering a chat session as customer' do
|
||||
let(:subject_as_customer) do
|
||||
Sessions.create(client_id, { 'id' => customer.id }, {})
|
||||
Sessions.queue(client_id)
|
||||
described_class.new(
|
||||
payload: { 'data' => { 'session_id' => chat_session.session_id }, 'chat_id' => chat_transfer_into.id },
|
||||
user_id: customer.id,
|
||||
client_id: client_id,
|
||||
clients: {},
|
||||
session: { 'id' => customer.id },
|
||||
)
|
||||
end
|
||||
|
||||
context 'without chat.agent permissions' do
|
||||
it 'send out no_permission event to user' do
|
||||
expect(subject_as_customer.run).to eq(nil)
|
||||
messages = Sessions.queue(client_id)
|
||||
expect(messages.count).to eq(1)
|
||||
expect(messages).to eq([
|
||||
'event' => 'chat_error',
|
||||
'data' => {
|
||||
'state' => 'no_permission'
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when transfering a chat session as agent' do
|
||||
it 'send out chat_session_notice to customer and agent and set chat session to waiting' do
|
||||
expect(subject_as_agent.run).to eq(nil)
|
||||
|
||||
messages_to_customer = Sessions.queue('customer_session_id')
|
||||
expect(messages_to_customer.count).to eq(1)
|
||||
expect(messages_to_customer[0]).to eq(
|
||||
'event' => 'chat_session_notice',
|
||||
'data' => {
|
||||
'message' => 'Conversation transfered into other chat. Please stay tuned.',
|
||||
'session_id' => chat_session.session_id,
|
||||
},
|
||||
)
|
||||
|
||||
messages_to_agent = Sessions.queue(client_id)
|
||||
expect(messages_to_agent.count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in a new issue