Popovers refactoring

This commit is contained in:
Mantas 2018-09-10 10:33:15 +03:00 committed by Mantas Masalskis
parent 254c8c1e4b
commit 9d5b83169d
26 changed files with 391 additions and 315 deletions

View file

@ -77,10 +77,6 @@ class App.Controller extends Spine.Controller
if @ajaxCalls
for callId in @ajaxCalls
App.Ajax.abort(callId)
@userTicketPopupsDestroy()
@ticketPopupsDestroy()
@userPopupsDestroy()
@organizationPopupsDestroy()
release: ->
# release custom bindings after it got removed from dom
@ -295,228 +291,6 @@ class App.Controller extends Spine.Controller
item.attr('title', App.i18n.translateTimestamp(timestamp))
item.html(time)
ticketPopups: (position = 'right') ->
# open ticket in new task if curent user agent
if @permissionCheck('ticket.agent')
@$('div.ticket-popover, span.ticket-popover').bind('click', (e) =>
id = $(e.target).data('id')
return if !id
ticket = App.Ticket.findNative(id)
@navigate ticket.uiUrl()
)
@ticketPopupsDestroy()
# show ticket popup
ui = @
@ticketPopupsList = @el.find('.ticket-popover').popover(
trigger: 'hover'
container: 'body'
html: true
animation: false
delay: 100
placement: position
title: ->
ticketId = $(@).data('id')
ticket = App.Ticket.find(ticketId)
App.Utils.htmlEscape(ticket.title)
content: ->
ticketId = $(@).data('id')
ticket = App.Ticket.fullLocal(ticketId)
html = $(App.view('popover/ticket')(
ticket: ticket
))
html.find('.humanTimeFromNow').each(->
ui.frontendTimeUpdateItem($(@))
)
html
)
ticketPopupsDestroy: =>
if @ticketPopupsList
@ticketPopupsList.popover('destroy')
userPopups: (position = 'right') ->
# open user in new task if current user is agent
return if !@permissionCheck('ticket.agent')
@$('div.user-popover, span.user-popover').bind('click', (e) =>
id = $(e.target).data('id')
return if !id
user = App.User.findNative(id)
@navigate user.uiUrl()
)
@userPopupsDestroy()
# show user popup
@userPopupsList = @el.find('.user-popover').popover(
trigger: 'hover'
container: 'body'
html: true
animation: false
delay: 100
placement: "auto #{position}"
title: ->
userId = $(@).data('id')
user = App.User.find(userId)
headline = App.Utils.htmlEscape(user.displayName())
if user.isOutOfOffice()
headline += " (#{App.Utils.htmlEscape(user.outOfOfficeText())})"
headline
content: ->
userId = $(@).data('id')
user = App.User.fullLocal(userId)
# get display data
userData = []
for attributeName, attributeConfig of App.User.attributesGet('view')
# check if value for _id exists
name = attributeName
nameNew = name.substr(0, name.length - 3)
if nameNew of user
name = nameNew
# add to show if value exists
if user[name] && attributeConfig.shown
# do not show firstname and lastname / already show via diplayName()
if name isnt 'firstname' && name isnt 'lastname' && name isnt 'organization'
userData.push attributeConfig
# insert data
App.view('popover/user')(
user: user
userData: userData
)
)
userPopupsDestroy: =>
if @userPopupsList
@userPopupsList.popover('destroy')
organizationPopups: (position = 'right') ->
# open org in new task if current user agent
return if !@permissionCheck('ticket.agent')
@$('div.organization-popover, span.organization-popover').bind('click', (e) =>
id = $(e.target).data('id')
return if !id
organization = App.Organization.find(id)
@navigate organization.uiUrl()
)
@organizationPopupsDestroy()
# show organization popup
@organizationPopupsList = @el.find('.organization-popover').popover(
trigger: 'hover'
container: 'body'
html: true
animation: false
delay: 100
placement: "auto #{position}"
title: ->
organization_id = $(@).data('id')
organization = App.Organization.find(organization_id)
App.Utils.htmlEscape(organization.name)
content: ->
organization_id = $(@).data('id')
organization = App.Organization.fullLocal(organization_id)
# get display data
organizationData = []
for attributeName, attributeConfig of App.Organization.attributesGet('view')
# check if value for _id exists
name = attributeName
nameNew = name.substr(0, name.length - 3)
if nameNew of organization
name = nameNew
# add to show if value exists
if organization[name] && attributeConfig.shown
# do not show firstname and lastname / already show via diplayName()
if name isnt 'name'
organizationData.push attributeConfig
# insert data
App.view('popover/organization')(
organization: organization,
organizationData: organizationData,
)
)
organizationPopupsDestroy: =>
if @organizationPopupsList
@organizationPopupsList.popover('destroy')
userTicketPopups: (params) ->
show = (data, ticket_list) =>
if !data.position
data.position = 'left'
@userTicketPopupsDestroy()
# show user popup
ui = @
@userTicketPopupsList = @el.find(data.selector).popover(
trigger: 'hover'
container: 'body'
html: true
animation: false
delay: 100
placement: "auto #{data.position}"
title: ->
$(@).find('[title="*"]').val()
content: ->
type = $(@).filter('[data-type]').data('type')
tickets = []
if ticket_list[type]
for ticketId in ticket_list[type]
tickets.push App.Ticket.fullLocal(ticketId)
# insert data
html = $(App.view('popover/user_ticket_list')(
tickets: tickets
))
html.find('.humanTimeFromNow').each( ->
ui.frontendTimeUpdateItem($(@))
)
html
)
fetch = (params) =>
@ajax(
type: 'GET'
url: "#{@Config.get('api_path')}/ticket_customer"
data:
customer_id: params.user_id
processData: true
success: (data, status, xhr) ->
App.Collection.loadAssets(data.assets)
show(params, { open: data.ticket_ids_open, closed: data.ticket_ids_closed })
)
# get data
fetch(params)
userTicketPopupsDestroy: =>
if @userTicketPopupsList
@userTicketPopupsList.popover('destroy')
anyPopoversDestroy: ->
# do not remove permanent .popover--notifications widget
$('.popover:not(.popover--notifications)').remove()
recentView: (object, o_id) =>
params =
object: object

View file

@ -545,6 +545,9 @@ class App.ControllerNavSidbar extends App.Controller
sidebar: @$('.sidebar').scrollTop()
class App.GenericHistory extends App.ControllerModal
@extend App.PopoverProvidable
@registerPopovers 'User'
buttonClose: true
buttonCancel: false
buttonSubmit: false
@ -568,7 +571,7 @@ class App.GenericHistory extends App.ControllerModal
content
onShown: =>
@userPopups()
@renderPopovers()
sortorder: =>
@items = @items.reverse()

View file

@ -1,4 +1,7 @@
class App.CTI extends App.Controller
@extend App.PopoverProvidable
@registerPopovers 'User'
elements:
'.js-callerLog': 'callerLog'
events:
@ -160,9 +163,10 @@ class App.CTI extends App.Controller
if diff_in_min > 1
item.disabled = false
@userPopupsDestroy()
@removePopovers()
@callerLog.html( App.view('cti/caller_log')(list: @list))
@userPopups()
@renderPopovers()
@updateNavMenu()
done: (e) =>

View file

@ -1,4 +1,7 @@
class App.Navigation extends App.ControllerWidgetPermanent
@extend App.PopoverProvidable
@registerAllPopovers()
className: 'navigation vertical'
elements:
@ -157,6 +160,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
)
renderResult: (result = []) =>
@removePopovers()
# remove result if not result exists
if _.isEmpty(result)
@ -174,14 +178,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
# show result list
@searchContainer.addClass('open')
# start ticket popups
@ticketPopups()
# start user popups
@userPopups()
# start oorganization popups
@organizationPopups()
@renderPopovers()
render: ->
@ -206,7 +203,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
searchFocus: (e) =>
@query = '' # reset query cache
@searchContainer.addClass('focused')
@anyPopoversDestroy()
App.PopoverProvidable.anyPopoversDestroy()
@search()
searchBlur: (e) =>
@ -288,14 +285,14 @@ class App.Navigation extends App.ControllerWidgetPermanent
@searchContainer.removeClass('filled').removeClass('open').removeClass('focused')
@globalSearch.close()
# remove not needed popovers
@delay(@anyPopoversDestroy, 100, 'removePopovers')
@delayedRemoveAnyPopover()
andClose: =>
@searchInput.blur()
@searchContainer.removeClass('open')
@globalSearch.close()
@delay(@anyPopoversDestroy, 100, 'removePopovers')
@delayedRemoveAnyPopover()
search: =>
query = @searchInput.val().trim()

View file

@ -1,4 +1,6 @@
class App.Search extends App.Controller
@extend App.PopoverProvidable
elements:
'.js-search': 'searchInput'
@ -112,8 +114,7 @@ class App.Search extends App.Controller
@updateFilledClass()
@updateTask()
# remove not needed popovers
@delay(@anyPopoversDestroy, 100, 'removePopovers')
@delayedRemoveAnyPopover()
search: (force = false) =>
query = @searchInput.val().trim()

View file

@ -926,6 +926,9 @@ class Navbar extends App.Controller
@autoFoldTabs()
class Table extends App.Controller
@extend App.PopoverProvidable
@registerPopovers 'Organization', 'User'
events:
'click [data-type=settings]': 'settings'
'click [data-type=viewmode]': 'viewmode'
@ -1174,11 +1177,7 @@ class Table extends App.Controller
'click': callbackCheckbox
)
# start user popups
@userPopups()
# start organization popups
@organizationPopups()
@renderPopovers()
@bulkForm = new BulkForm(
holder: @el

View file

@ -1,4 +1,7 @@
class App.WidgetAvatar extends App.ObserverController
@extend App.PopoverProvidable
@registerPopovers 'User'
model: 'User'
observe:
login: true
@ -17,4 +20,4 @@ class App.WidgetAvatar extends App.ObserverController
render: (user) =>
@html(user.avatar(@size, @position, @cssClass, false, false, @type))
@userPopups(@position)
@renderPopovers()

View file

@ -1,4 +1,10 @@
class App.WidgetLink extends App.Controller
@extend App.PopoverProvidable
@registerPopovers 'Ticket'
@popoversDefaults:
position: 'left'
events:
'click .js-add': 'add'
'click .js-delete': 'delete'
@ -54,7 +60,8 @@ class App.WidgetLink extends App.Controller
@html App.view('link/info')(
links: list
)
@ticketPopups('left')
@renderPopovers()
delete: (e) =>
e.preventDefault()

View file

@ -1,4 +1,7 @@
class App.WidgetOrganization extends App.Controller
@extend App.PopoverProvidable
@registerPopovers 'User'
events:
'focusout [contenteditable]': 'update'
@ -44,16 +47,7 @@ class App.WidgetOrganization extends App.Controller
maxlength: 250
)
# enable user popups
@userPopups()
###
@userTicketPopups(
selector: '.user-tickets'
user_id: user.id
position: 'right'
)
###
@renderPopovers()
update: (e) =>
name = $(e.target).attr('data-name')

View file

@ -1,4 +1,7 @@
class App.TicketList extends App.Controller
@extend App.PopoverProvidable
@registerPopovers 'Organization', 'User'
constructor: ->
super
@ -85,8 +88,4 @@ class App.TicketList extends App.Controller
radio: @radio
)
# start user popups
@userPopups()
# start organization popups
@organizationPopups()
@renderPopovers()

View file

@ -138,6 +138,9 @@ class App.TicketStats extends App.Controller
)
class App.TicketStatsList extends App.Controller
@extend App.PopoverProvidable
@registerPopovers 'Ticket'
events:
'click .js-showAll': 'showAll'
@ -166,7 +169,7 @@ class App.TicketStatsList extends App.Controller
limit: @limit
)
@ticketPopups()
@renderPopovers()
showAll: (e) =>
e.preventDefault()

View file

@ -1,4 +1,7 @@
class App.WidgetUser extends App.Controller
@extend App.PopoverProvidable
@registerPopovers 'UserTicket'
events:
'focusout [contenteditable]': 'update'
@ -76,10 +79,9 @@ class App.WidgetUser extends App.Controller
maxlength: 250
)
@userTicketPopups(
selector: '.user-tickets'
@renderPopovers(
selector: '.user-tickets',
user_id: user.id
position: 'right'
)
update: (e) =>

View file

@ -85,6 +85,10 @@ class App extends Spine.Controller
S: (key) ->
App.Session.get(key)
# define view helper for rendering partial views
V: (name, params) ->
App.view(name)(params)
# define address line helper
AddressLine: (line) ->
return '' if !line

View file

@ -0,0 +1,77 @@
class App.PopoverProvider
@selectorCssClassPrefix = null # needs to be overrided
@templateName = null # needs to be overrided
@permission = 'ticket.agent'
@providers = {}
@providersConfigKey = 'PopoverProviders'
@registerProvider: (key, klass) ->
@providers[key] = klass
App.Config.set(key, klass, @providersConfigKey)
@defaults =
position: 'right'
parentController: null
popovers = null
constructor: (params) ->
if params.parentController is null
throw 'Parent controller needs to be set'
@params = _.extend {}, @constructor.defaults, params
build: (buildParams) ->
return if !@checkPermissions()
@clear(@popovers)
@bind()
@popovers = @buildPopovers()
checkPermissions: ->
@params.parentController.permissionCheck(@constructor.permission)
cssClass: ->
"#{@constructor.selectorCssClassPrefix}-popover"
bind: ->
buildPopovers: (supplementaryData = {}) ->
context = @
selector = supplementaryData.selector || ".#{@cssClass()}"
@params.parentController.el.find(selector).popover(
trigger: 'hover'
container: 'body'
html: true
animation: false
delay: 100
placement: "auto #{@params.position}"
title: ->
context.buildTitleFor(@, supplementaryData)
content: ->
context.buildContentFor(@, supplementaryData)
)
clear: ->
return if !@popovers
@popovers.popover('destroy')
buildTitleFor: (elem) ->
'title'
buildContentFor: (elem) ->
'content'
buildHtmlContent: (params) ->
html = $(App.view("popover/#{@constructor.templateName}")(params))
html.find('.humanTimeFromNow').each =>
@params.parentController.frontendTimeUpdateItem($(@))
html
displayTitleUsing: (object) ->
throw 'please override'

View file

@ -0,0 +1,45 @@
class App.SingleObjectPopoverProvider extends App.PopoverProvider
@klass = null # needs to be overrided
@ignoredAttributes = []
@includeData = true
@templateName = 'single_object_generic'
fullCssSelector: ->
"div.#{@cssClass()}, span.#{@cssClass()}"
bind: ->
@params.parentController.$(@fullCssSelector()).bind('click', (e) =>
id = @objectIdFor(e.target)
return if !id
object = @constructor.klass.find(id)
@params.parentController.navigate object.uiUrl()
)
objectIdFor: (elem) ->
$(elem).data('id')
buildTitleFor: (elem) ->
object = @constructor.klass.find(@objectIdFor(elem))
App.Utils.htmlEscape(@displayTitleUsing(object))
buildContentFor: (elem) ->
id = @objectIdFor(elem)
object = @constructor.klass.fullLocal(id)
# get display data
data = _.values(@constructor.klass.attributesGet('view'))
.filter (attr) ->
# check if value for _id exists
name = attr.name
nameNew = name.substr(0, name.length - 3)
if nameNew of object
name = nameNew
# add to show if value exists
# do not show ignroed attributes
object[name] && attr.shown && !_.include(@constructor.ignoredAttributes, name)
@buildHtmlContent(
object: object
attributes: data
)

View file

@ -0,0 +1,10 @@
class Organization extends App.SingleObjectPopoverProvider
@klass = App.Organization
@selectorCssClassPrefix = 'organization'
@templateName = 'organization'
@ignoredAttributes = ['name']
displayTitleUsing: (object) ->
object.name
App.PopoverProvider.registerProvider('Organization', Organization)

View file

@ -0,0 +1,10 @@
class Ticket extends App.SingleObjectPopoverProvider
@klass = App.Ticket
@selectorCssClassPrefix = 'ticket'
@templateName = 'ticket'
@includeData = false
displayTitleUsing: (object) ->
object.title
App.PopoverProvider.registerProvider('Ticket', Ticket)

View file

@ -0,0 +1,13 @@
class User extends App.SingleObjectPopoverProvider
@klass = App.User
@selectorCssClassPrefix = 'user'
@templateName = 'user'
@ignoredAttributes = ['firstname', 'lastname', 'organization']
displayTitleUsing: (object) ->
output = object.displayName()
if object.isOutOfOffice()
output += " (#{object.outOfOfficeText()})"
output
App.PopoverProvider.registerProvider('User', User)

View file

@ -0,0 +1,37 @@
class App.UserTicketPopoverProvider extends App.PopoverProvider
@templateName = 'user_ticket_list'
fetch: (buildParams) ->
@params.parentController.ajax(
type: 'GET'
url: "#{App.Config.get('api_path')}/ticket_customer"
data:
customer_id: buildParams.user_id
processData: true
success: (data, status, xhr) =>
App.Collection.loadAssets(data.assets)
ticketsList = { open: data.ticket_ids_open, closed: data.ticket_ids_closed }
@callback(ticketsList: ticketsList, selector: buildParams.selector)
)
build: (buildParams) ->
return if !@checkPermissions()
@fetch(buildParams)
callback: (supplementaryData) ->
@clear(@popovers)
@popovers = @buildPopovers(supplementaryData)
buildTitleFor: (elem) ->
$(elem).find('[title="*"]').val()
buildContentFor: (elem, supplementaryData) ->
type = $(elem).filter('[data-type]').data('type')
ticket_ids = supplementaryData.ticketsList[type] || []
tickets = ticket_ids.map (ticketId) -> App.Ticket.fullLocal(ticketId)
# insert data
@buildHtmlContent(tickets: tickets)

View file

@ -96,6 +96,7 @@ class App.TaskManager
_instance.maxTaskCount = key
class _taskManagerSingleton extends App.Controller
@extend App.PopoverProvidable
@include App.LogInclude
constructor: (params = {}) ->
@ -394,7 +395,7 @@ class _taskManagerSingleton extends App.Controller
if controller.hide && _.isFunction(controller.hide)
controller.hide()
@anyPopoversDestroy()
@delayedRemoveAnyPopover()
true

View file

@ -0,0 +1,38 @@
class UserTicket extends App.PopoverProvider
@templateName = 'user_ticket_list'
fetch: (buildParams) ->
@params.parentController.ajax(
type: 'GET'
url: "#{App.Config.get('api_path')}/ticket_customer"
data:
customer_id: buildParams.user_id
processData: true
success: (data, status, xhr) =>
App.Collection.loadAssets(data.assets)
ticketsList = { open: data.ticket_ids_open, closed: data.ticket_ids_closed }
@callback(ticketsList: ticketsList, selector: buildParams.selector)
)
build: (buildParams) ->
return if !@checkPermissions()
@fetch(buildParams)
callback: (supplementaryData) ->
@clear(@popovers)
@popovers = @buildPopovers(supplementaryData)
buildTitleFor: (elem) ->
$(elem).find('[title="*"]').val()
buildContentFor: (elem, supplementaryData) ->
type = $(elem).filter('[data-type]').data('type')
ticket_ids = supplementaryData.ticketsList[type] || []
tickets = ticket_ids.map (ticketId) -> App.Ticket.fullLocal(ticketId)
# insert data
@buildHtmlContent(tickets: tickets)
App.PopoverProvider.registerProvider('UserTicket', UserTicket)

View file

@ -0,0 +1,56 @@
InstanceMethods =
# do not call directly
initializePopovers: ->
@el.on('remove', @removePopovers)
params = _.extend {}, @constructor.popoversDefaults,
parentController: @
@initializedPopovers = @selectedPopovers().map (key) ->
klass = App.Config.get(App.PopoverProvider.providersConfigKey)[key]
new klass(params)
# returns all or selected popovers
selectedPopovers: ->
if @constructor.allPopovers
popoversConfig = App.Config.get(App.PopoverProvider.providersConfigKey)
return Object.keys(popoversConfig)
return @constructor.registeredPopovers || []
# do not call directly
buildPopovers: (buildParams) ->
for popover in @initializedPopovers
popover.build(buildParams)
renderPopovers: (buildParams = {}) ->
if !@initializedPopovers
@initializePopovers()
@buildPopovers(buildParams)
removePopovers: ->
return if !@initializedPopovers
for popover in @initializedPopovers
popover.clear()
@initializedPopovers = undefined
delayedRemoveAnyPopover: ->
@delay(@constructor.anyPopoversDestroy, 100, 'removePopovers')
App.PopoverProvidable =
registerPopovers: (klasses...) ->
@allPopovers = undefined
@registeredPopovers = klasses
registerAllPopovers: ->
@allPopovers = true
anyPopoversDestroy: ->
# do not remove permanent .popover--notifications widget
$('.popover:not(.popover--notifications)').remove()
extended: ->
@include InstanceMethods

View file

@ -1,18 +1,10 @@
<hr>
<% for row in @organizationData: %>
<% if @organization[row.name]: %>
<div class="popover-block">
<label><%- @T( row.display ) %></label>
<%- @P( @organization, row.name ) %>
</div>
<% end %>
<% end %>
</div>
<% if @organization.members: %>
<%- @V('popover/single_object_generic', object: @object, attributes: @attributes) %>
<% if @object.members: %>
<hr>
<div class="popover-block">
<label><%- @T('Members') %></label>
<% for user in @organization.members: %>
<% for user in @object.members: %>
<div class="person"><%= user.displayName() %></div>
<% end %>
</div>

View file

@ -0,0 +1,12 @@
<% if !_.isEmpty(@attributes): %>
<hr>
<% end %>
<% for row in @attributes: %>
<% if @object[row.name]: %>
<div class="popover-block">
<label><%- @T( row.display ) %></label>
<%- @P( @object, row.name ) %>
</div>
<% end %>
<% end %>

View file

@ -1,22 +1,22 @@
<div>
<%- @Icon(@ticket.icon(), @ticket.iconClass()) %> <span class="<%- @T(@ticket.iconTextClass()) %>"><%- @ticket.iconTitle() %></span>
<%- @Icon(@object.icon(), @object.iconClass()) %> <span class="<%- @T(@object.iconTextClass()) %>"><%- @object.iconTitle() %></span>
</div>
<hr>
<div class="popover-block">
<label><%- @T('Agent') %></label>
<div class="person">
<%= @ticket.owner.displayName() %>
<% if @ticket.owner.organization: %>
<span class="organization"><%= @ticket.owner.organization.displayName() %></span>
<%= @object.owner.displayName() %>
<% if @object.owner.organization: %>
<span class="organization"><%= @object.owner.organization.displayName() %></span>
<% end %>
</div>
</div>
<div class="popover-block">
<label><%- @T('Customer') %></label>
<div class="person">
<%= @ticket.customer.displayName() %>
<% if @ticket.customer.organization: %>
<span class="organization"><%= @ticket.customer.organization.displayName() %></span>
<%= @object.customer.displayName() %>
<% if @object.customer.organization: %>
<span class="organization"><%= @object.customer.organization.displayName() %></span>
<% end %>
</div>
</div>
@ -24,18 +24,18 @@
<div class="horizontal two-columns">
<div class="column">
<label>#</label>
<div class="u-textTruncate"><%- @P(@ticket, 'number') %></div>
<div class="u-textTruncate"><%- @P(@object, 'number') %></div>
</div>
<div class="column">
<label><%- @T('Priority') %></label>
<div class="u-textTruncate"><%- @P(@ticket, 'priority') %></div>
<div class="u-textTruncate"><%- @P(@object, 'priority') %></div>
</div>
<div class="column">
<label><%- @T('Created') %></label>
<div class="u-textTruncate"><%- @P(@ticket, 'created_at') %></div>
<div class="u-textTruncate"><%- @P(@object, 'created_at') %></div>
</div>
<div class="column">
<label><%- @T('Group') %></label>
<div class="u-textTruncate"><%- @P(@ticket, 'group') %></div>
<div class="u-textTruncate"><%- @P(@object, 'group') %></div>
</div>
</div>

View file

@ -1,25 +1,20 @@
<% if @user['organization']: %>
<div class="user-organization"><a href="<%- @user.organization.uiUrl() %>"><%= @user.organization.displayName() %></a></div>
<% if @object['organization']: %>
<div class="user-organization"><a href="<%- @object.organization.uiUrl() %>"><%= @object.organization.displayName() %></a></div>
<% end %>
<hr>
<% for row in @userData: %>
<% if @user[row.name]: %>
<div class="popover-block">
<label><%- @T(row.display) %></label>
<%- @P(@user, row.name) %>
</div>
<% end %>
<% end %>
<% if !_.isEmpty(@user['accounts']): %>
<%- @V('popover/single_object_generic', object: @object, attributes: @attributes) %>
<% if !_.isEmpty(@object['accounts']): %>
<hr>
<div class="popover-block">
<label><%- @T('Linked Accounts') %></label>
<% for account of @user['accounts']: %>
<a href="<%= @user['accounts'][account]['link'] %>" target="_blank"><%= account %></a>
<% for account of @object['accounts']: %>
<a href="<%= @object['accounts'][account]['link'] %>" target="_blank"><%= account %></a>
<% end %>
</div>
<% end %>
<% if !_.isEmpty(@user['links']): %>
<% for link in @user['links']: %>
<% if !_.isEmpty(@object['links']): %>
<hr>
<% for link in @object['links']: %>
<div class="popover-block">
<label><%- @T(link['title']) %></label>
<% for item in link['items']: %>