Improved reload and table rendering of report screens.

This commit is contained in:
Martin Edenhofer 2017-12-12 03:54:50 +01:00
parent 0064a5bb6a
commit 95e8aa2dce
14 changed files with 231 additions and 118 deletions

View file

@ -133,6 +133,8 @@ class App.ControllerTable extends App.Controller
customOrderDirection: undefined customOrderDirection: undefined
customOrderBy: undefined customOrderBy: undefined
frontendTimeUpdateExecute: true
bindCol: {} bindCol: {}
bindRow: {} bindRow: {}
@ -269,6 +271,7 @@ class App.ControllerTable extends App.Controller
@currentRows = newCurrentRows @currentRows = newCurrentRows
@log 'debug', 'table.fullRender.contentRemoved', removePositions, addPositions @log 'debug', 'table.fullRender.contentRemoved', removePositions, addPositions
@renderPager(@el, true) @renderPager(@el, true)
@frontendTimeUpdateElement(@el) if @frontendTimeUpdateExecute is true
return ['fullRender.contentRemoved', removePositions, addPositions] return ['fullRender.contentRemoved', removePositions, addPositions]
if newRows.length isnt @currentRows.length if newRows.length isnt @currentRows.length
@ -304,6 +307,7 @@ class App.ControllerTable extends App.Controller
else else
@currentRows = clone(rows) @currentRows = clone(rows)
container.find('.js-tableBody').html(rows) container.find('.js-tableBody').html(rows)
@frontendTimeUpdateElement(container) if @frontendTimeUpdateExecute is true
@renderPager(container) @renderPager(container)

View file

@ -108,33 +108,40 @@ class Graph extends App.ControllerContent
@render() @render()
render: => update: (data) =>
update = (data) => # show only selected lines
dataNew = {}
for key, value of data.data
if @params.backendSelected[key] is true
dataNew[key] = value
@ui.storeParams()
# show only selected lines if !@lastNewData
dataNew = {} @lastNewData = {}
for key, value of data.data
if @params.backendSelected[key] is true
dataNew[key] = value
@ui.storeParams()
if !@lastNewData return if @lastNewData && JSON.stringify(dataNew) is JSON.stringify(@lastNewData)
@lastNewData = {} @lastNewData = dataNew
return if @lastNewData && JSON.stringify(dataNew) is JSON.stringify(@lastNewData) @draw(dataNew)
@lastNewData = dataNew t = new Date
@el.find('#download-chart').html(t.toString())
@draw(dataNew) if @downloadWidget
t = new Date @downloadWidget.update(
@el.find('#download-chart').html(t.toString()) config: @config
new Download( params: @params
ui: @ui
)
else
@downloadWidget = new Download(
el: @el.find('.js-dataDownload') el: @el.find('.js-dataDownload')
config: @config config: @config
params: @params params: @params
ui: @ui ui: @ui
) )
render: =>
url = "#{@apiPath}/reports/generate" url = "#{@apiPath}/reports/generate"
interval = 5 * 60000 interval = 5 * 60000
if @params.timeRange is 'year' if @params.timeRange is 'year'
@ -142,9 +149,9 @@ class Graph extends App.ControllerContent
if @params.timeRange is 'month' if @params.timeRange is 'month'
interval = 60000 interval = 60000
if @params.timeRange is 'week' if @params.timeRange is 'week'
interval = 40000 interval = 50000
if @params.timeRange is 'day' if @params.timeRange is 'day'
interval = 20000 interval = 30000
if @params.timeRange is 'realtime' if @params.timeRange is 'realtime'
interval = 10000 interval = 10000
@ -164,7 +171,7 @@ class Graph extends App.ControllerContent
) )
processData: true processData: true
success: (data) => success: (data) =>
update(data) @update(data)
@delay(@render, interval, 'report-update', 'page') @delay(@render, interval, 'report-update', 'page')
) )
@ -215,7 +222,7 @@ class Graph extends App.ControllerContent
class Download extends App.Controller class Download extends App.Controller
events: events:
'click .js-dataDownloadBackendSelector': 'tableUpdate' 'click .js-dataDownloadBackendSelector': 'selectBackend'
constructor: (data) -> constructor: (data) ->
@ -225,7 +232,24 @@ class Download extends App.Controller
super super
@render() @render()
render: -> selectBackend: (e) =>
e.preventDefault()
@el.find('.js-dataDownloadBackendSelector').parent().removeClass('active')
$(e.target).parent().addClass('active')
@profileSelectedId = $(e.target).data('profile-id')
@params.downloadBackendSelected = $(e.target).data('backend')
@ui.storeParams()
@table = false
@render()
update: =>
@render()
render: =>
if !@contentRendered
@contentRendered = true
@html(App.view('report/download_content')())
reports = [] reports = []
@ -244,44 +268,84 @@ class Download extends App.Controller
@profileSelectedId = key @profileSelectedId = key
profiles.push App.ReportProfile.find(key) profiles.push App.ReportProfile.find(key)
@html App.view('report/download_header')( downloadHeaderHtml = App.view('report/download_header')(
reports: reports reports: reports
profiles: profiles profiles: profiles
downloadBackendSelected: @params.downloadBackendSelected downloadBackendSelected: @params.downloadBackendSelected
metric: @config.metric[@params.metric] metric: @config.metric[@params.metric]
) )
if downloadHeaderHtml isnt @downloadHeaderHtml
@el.find('.js-dataDownloadHeader').html(downloadHeaderHtml)
@downloadHeaderHtml = downloadHeaderHtml
@tableUpdate() @tableUpdate()
tableUpdate: (e) => tableRender: (tickets, count) =>
if e if _.isEmpty(tickets)
e.preventDefault() @$('.js-dataDownloadButton').html('')
@el.find('.js-dataDownloadBackendSelector').parent().removeClass('active') @$('.js-dataDownloadTable').html('')
$(e.target).parent().addClass('active') return
@profileSelectedId = $(e.target).data('profile-id')
@params.downloadBackendSelected = $(e.target).data('backend')
@ui.storeParams()
table = (tickets, count) => profile_id = 0
url = '#ticket/zoom/' for key, value of @params.profileSelected
if App.Config.get('import_mode') if value
url = App.Config.get('import_otrs_endpoint') + '/index.pl?Action=AgentTicketZoom;TicketID=' profile_id = key
if _.isEmpty(tickets) downloadUrl = "#{@apiPath}/reports/sets?sheet=true;metric=#{@params.metric};year=#{@params.year};month=#{@params.month};week=#{@params.week};day=#{@params.day};timeRange=#{@params.timeRange};profile_id=#{profile_id};downloadBackendSelected=#{@params.downloadBackendSelected}"
@el.find('.js-dataDownloadTable').html('') @$('.js-dataDownloadButton').html(App.view('report/download_button')(
else count: count
profile_id = 0 downloadUrl: downloadUrl
for key, value of @params.profileSelected ))
if value
profile_id = key
downloadUrl = "#{@apiPath}/reports/sets?sheet=true;metric=#{@params.metric};year=#{@params.year};month=#{@params.month};week=#{@params.week};day=#{@params.day};timeRange=#{@params.timeRange};profile_id=#{profile_id};downloadBackendSelected=#{@params.downloadBackendSelected}"
html = App.view('report/download_list')(
tickets: tickets
count: count
url: url
download: downloadUrl
)
@el.find('.js-dataDownloadTable').html(html)
openTicket = (id,e) =>
ticket = App.Ticket.findNative(id)
@navigate ticket.uiUrl()
callbackTicketTitleAdd = (value, object, attribute, attributes) ->
attribute.title = object.title
value
callbackLinkToTicket = (value, object, attribute, attributes) ->
attribute.link = object.uiUrl()
value
callbackIconHeader = (headers) ->
attribute =
name: 'icon'
display: ''
translation: false
width: '28px'
displayWidth:28
unresizable: true
headers.unshift(0)
headers[0] = attribute
headers
callbackIcon = (value, object, attribute, header) ->
value = ' '
attribute.class = object.iconClass()
attribute.link = ''
attribute.title = object.iconTitle()
value
params =
el: @el.find('.js-dataDownloadTable')
model: App.Ticket
objects: tickets
overviewAttributes: ['number', 'title', 'state', 'group', 'created_at']
bindRow:
events:
'click': openTicket
callbackHeader: [ callbackIconHeader ]
callbackAttributes:
icon:
[ callbackIcon ]
title:
[ callbackLinkToTicket, callbackTicketTitleAdd ]
number:
[ callbackLinkToTicket, callbackTicketTitleAdd ]
if !@table
@table = new App.ControllerTable(params)
else
@table.update(objects: tickets)
tableUpdate: =>
@ajax( @ajax(
id: 'report_download' id: 'report_download'
type: 'POST' type: 'POST'
@ -298,15 +362,14 @@ class Download extends App.Controller
downloadBackendSelected: @params.downloadBackendSelected downloadBackendSelected: @params.downloadBackendSelected
) )
processData: true processData: true
success: (data) -> success: (data) =>
App.Collection.loadAssets(data.assets) App.Collection.loadAssets(data.assets)
ticket_collection = [] ticket_collection = []
if data.ticket_ids if data.ticket_ids
for record_id in data.ticket_ids for record_id in data.ticket_ids
ticket = App.Ticket.fullLocal( record_id ) ticket = App.Ticket.fullLocal(record_id)
ticket_collection.push ticket ticket_collection.push ticket
@tableRender(ticket_collection, data.count)
table(ticket_collection, data.count)
) )
class TimeRangePicker extends App.Controller class TimeRangePicker extends App.Controller

View file

@ -12,8 +12,8 @@ class App extends Spine.Controller
helper = helper =
# define print name helper # define print name helper
P: (object, attributeName, attributes) -> P: (object, attributeName, attributes, table = false) ->
App.viewPrint(object, attributeName, attributes) App.viewPrint(object, attributeName, attributes, table)
# define date format helper # define date format helper
date: (time) -> date: (time) ->
@ -136,7 +136,7 @@ class App extends Spine.Controller
return marked(string) return marked(string)
App.i18n.translateContent(string) App.i18n.translateContent(string)
@viewPrint: (object, attributeName, attributes) -> @viewPrint: (object, attributeName, attributes, table) ->
if !attributes if !attributes
attributes = {} attributes = {}
if object.constructor.attributesGet if object.constructor.attributesGet
@ -172,10 +172,10 @@ class App extends Spine.Controller
if object[attributeNameWithoutRef] if object[attributeNameWithoutRef]
valueRef = object[attributeNameWithoutRef] valueRef = object[attributeNameWithoutRef]
@viewPrintItem(value, attributeConfig, valueRef) @viewPrintItem(value, attributeConfig, valueRef, table)
# define print name helper # define print name helper
@viewPrintItem: (item, attributeConfig = {}, valueRef) -> @viewPrintItem: (item, attributeConfig = {}, valueRef, table) ->
return '-' if item is undefined return '-' if item is undefined
return '-' if item is '' return '-' if item is ''
return item if item is null return item if item is null
@ -238,7 +238,7 @@ class App extends Spine.Controller
# transform date # transform date
if attributeConfig.tag is 'date' if attributeConfig.tag is 'date'
isHtmlEscape = true isHtmlEscape = true
resultLocal = App.i18n.translateDate(resultLocal) resultLocal = App.i18n.translateDate(resultLocal)
# transform input tel|url to make it clickable # transform input tel|url to make it clickable
if attributeConfig.tag is 'input' if attributeConfig.tag is 'input'
@ -258,8 +258,10 @@ class App extends Spine.Controller
cssClass = attributeConfig.class || '' cssClass = attributeConfig.class || ''
if cssClass.match 'escalation' if cssClass.match 'escalation'
escalation = true escalation = true
humanTime = App.PrettyDate.humanTime(resultLocal, escalation) humanTime = ''
resultLocal = "<time class=\"humanTimeFromNow #{cssClass}\" data-time=\"#{resultLocal}\" title=\"#{timestamp}\">#{humanTime}</time>" if !table
humanTime = App.PrettyDate.humanTime(resultLocal, escalation)
resultLocal = "<time class=\"humanTimeFromNow #{cssClass}\" data-time=\"#{resultLocal}\" title=\"#{timestamp}\">#{humanTime}</time>"
if !isHtmlEscape && typeof resultLocal is 'string' if !isHtmlEscape && typeof resultLocal is 'string'
resultLocal = App.Utils.htmlEscape(resultLocal) resultLocal = App.Utils.htmlEscape(resultLocal)

View file

@ -21,7 +21,7 @@
</td> </td>
<% end %> <% end %>
<% for header in @headers: %> <% for header in @headers: %>
<% value = @P(@object, header.name, @attributes) %> <% value = @P(@object, header.name, @attributes, true) %>
<% if @callbacks: %> <% if @callbacks: %>
<% for attribute, callbacksAll of @callbacks: %> <% for attribute, callbacksAll of @callbacks: %>
<% if attribute is header.name: %> <% if attribute is header.name: %>

View file

@ -0,0 +1 @@
<a class="btn btn--action" href="<%- @downloadUrl %>" target="_blank" data-type="attachment"><%- @Icon('download') %><span><%- @T('Download %s record(s)', @count) %></span></a>

View file

@ -0,0 +1,6 @@
<div>
<div class="js-dataDownloadHeader"></div>
<div class="js-dataDownloadButton"></div>
<div class="spacer"></div>
<div class="js-dataDownloadTable"></div>
</div>

View file

@ -1,14 +1,9 @@
<div> <ul class="nav nav-tabs">
<% for profile in @profiles: %>
<ul class="nav nav-tabs"> <% for backend in @metric.backend: %>
<% for profile in @profiles: %> <% if backend.dataDownload: %>
<% for backend in @metric.backend: %> <li <% if backend.name is @downloadBackendSelected: %>class="is-active active"<% end %>><a href="#" class="js-dataDownloadBackendSelector" data-toggle="tab" data-profile-id="<%= profile.id %>" data-backend="<%= backend.name %>"><%- @T(backend.display) %></a></li>
<% if backend.dataDownload: %>
<li <% if backend.name is @downloadBackendSelected: %>class="is-active active"<% end %>><a href="#" class="js-dataDownloadBackendSelector" data-toggle="tab" data-profile-id="<%= profile.id %>" data-backend="<%= backend.name %>"><%- @T(backend.display) %></a></li>
<% end %>
<% end %> <% end %>
<% end %> <% end %>
</ul> <% end %>
</ul>
<div class="js-dataDownloadTable"></div>
</div>

View file

@ -1,24 +0,0 @@
<a class="btn btn--action" href="<%- @download %>" target="_blank" data-type="attachment"><%- @Icon('download') %><span><%- @T('Download %s record(s)', @count) %></span></a>
<div class="spacer"></div>
<table class="table table-striped table-hover">
<thead>
<tr>
<td><%- @T('Number') %></td>
<td><%- @T('Title') %></td>
<td><%- @T('State') %></td>
<td><%- @T('Group') %></td>
<td><%- @T('Created') %></td>
</tr>
</thead>
<tbody>
<% for ticket in @tickets: %>
<tr>
<td><a <% if App.Config.get('import_mode'): %>target="_blank"<% end %> href="<%= @url %><%= ticket.id %>"><%- @P(ticket, 'number') %></a></td>
<td><%- @P(ticket, 'title') %></td>
<td><%- @P(ticket, 'state') %></td>
<td><%- @P(ticket, 'group') %></td>
<td><%- @P(ticket, 'created_at') %></td>
</tr>
<% end %>
</tbody>
</table>

View file

@ -211,22 +211,26 @@ class ReportsController < ApplicationController
row = 2 row = 2
result[:ticket_ids].each do |ticket_id| result[:ticket_ids].each do |ticket_id|
ticket = Ticket.lookup(id: ticket_id) begin
row += 1 ticket = Ticket.lookup(id: ticket_id)
worksheet.write(row, 0, ticket.number) row += 1
worksheet.write(row, 1, ticket.title) worksheet.write(row, 0, ticket.number)
worksheet.write(row, 2, ticket.state.name) worksheet.write(row, 1, ticket.title)
worksheet.write(row, 3, ticket.priority.name) worksheet.write(row, 2, ticket.state.name)
worksheet.write(row, 4, ticket.group.name) worksheet.write(row, 3, ticket.priority.name)
worksheet.write(row, 5, ticket.owner.fullname) worksheet.write(row, 4, ticket.group.name)
worksheet.write(row, 6, ticket.customer.fullname) worksheet.write(row, 5, ticket.owner.fullname)
worksheet.write(row, 7, ticket.try(:organization).try(:name)) worksheet.write(row, 6, ticket.customer.fullname)
worksheet.write(row, 8, ticket.create_article_type.name) worksheet.write(row, 7, ticket.try(:organization).try(:name))
worksheet.write(row, 9, ticket.create_article_sender.name) worksheet.write(row, 8, ticket.create_article_type.name)
worksheet.write(row, 10, ticket.tag_list.join(',')) worksheet.write(row, 9, ticket.create_article_sender.name)
worksheet.write(row, 11, ticket.created_at) worksheet.write(row, 10, ticket.tag_list.join(','))
worksheet.write(row, 12, ticket.updated_at) worksheet.write(row, 11, ticket.created_at)
worksheet.write(row, 13, ticket.close_at) worksheet.write(row, 12, ticket.updated_at)
worksheet.write(row, 13, ticket.close_at)
rescue => e
Rails.logger.error "SKIP: #{e.message}"
end
end end
workbook.close workbook.close

View file

@ -10,7 +10,7 @@ class Report::Base
# :selector # :selector
def self.history_count(params) def self.history_count(params)
history_object = History::Object.lookup( name: params[:object] ) history_object = History::Object.lookup(name: params[:object])
query, bind_params, tables = Ticket.selector2sql(params[:selector]) query, bind_params, tables = Ticket.selector2sql(params[:selector])

View file

@ -135,8 +135,8 @@ returns
field: params[:params][:field], field: params[:params][:field],
} }
limit = 1000 limit = 6000
if !params[:sheet] if params[:sheet].blank?
limit = 100 limit = 100
end end
@ -146,6 +146,7 @@ returns
end end
result = SearchIndexBackend.selectors(['Ticket'], selector, limit, nil, aggs_interval) result = SearchIndexBackend.selectors(['Ticket'], selector, limit, nil, aggs_interval)
return result if params[:sheet].present?
assets = {} assets = {}
result[:ticket_ids].each do |ticket_id| result[:ticket_ids].each do |ticket_id|
ticket_full = Ticket.find(ticket_id) ticket_full = Ticket.find(ticket_id)

View file

@ -123,6 +123,7 @@ returns
} }
local_params = defaults.merge(local_params) local_params = defaults.merge(local_params)
result = history(local_params) result = history(local_params)
return result if params[:sheet].present?
assets = {} assets = {}
result[:ticket_ids].each do |ticket_id| result[:ticket_ids].each do |ticket_id|
ticket_full = Ticket.find(ticket_id) ticket_full = Ticket.find(ticket_id)

View file

@ -98,6 +98,7 @@ returns
end: params[:range_end], end: params[:range_end],
selector: params[:selector] selector: params[:selector]
) )
return result if params[:sheet].present?
assets = {} assets = {}
result[:ticket_ids].each do |ticket_id| result[:ticket_ids].each do |ticket_id|
ticket_full = Ticket.find(ticket_id) ticket_full = Ticket.find(ticket_id)

View file

@ -1646,4 +1646,63 @@ test('table new - initial list', function() {
equal(el.find('tbody > tr:nth-child(4) > td').length, 0, 'check row 3') equal(el.find('tbody > tr:nth-child(4) > td').length, 0, 'check row 3')
$('#table').append('<hr><h1>table with data 11</h1><div id="table-new11"></div>')
var el = $('#table-new11')
App.TicketPriority.refresh([
{
id: 1,
name: '1 low',
note: 'some note 1',
active: true,
created_at: '2014-06-10T11:17:34.000Z',
},
{
id: 2,
name: '2 normal',
note: 'some note 2',
active: false,
created_at: '2014-06-10T10:17:34.000Z',
},
], {clear: true})
var table = new App.ControllerTable({
el: el,
overviewAttributes: ['name', 'created_at', 'active'],
model: App.TicketPriority,
objects: App.TicketPriority.search({sortBy:'name', order: 'ASC'}),
checkbox: false,
radio: false,
frontendTimeUpdateExecute: false,
})
//equal(el.find('table').length, 0, 'row count')
//table.render()
equal(el.find('table > thead > tr').length, 1, 'row count')
equal(el.find('table > thead > tr > th:nth-child(1)').text().trim(), 'Name', 'check header')
equal(el.find('table > thead > tr > th:nth-child(2)').text().trim(), 'Erstellt', 'check header')
equal(el.find('table > thead > tr > th:nth-child(3)').text().trim(), 'Aktiv', 'check header')
equal(el.find('tbody > tr:nth-child(1) > td').length, 3, 'check row 1')
equal(el.find('tbody > tr:nth-child(1) > td:first').text().trim(), '1 niedrig', 'check row 1')
equal(el.find('tbody > tr:nth-child(1) > td:nth-child(2)').text().trim(), '', 'check row 1')
equal(el.find('tbody > tr:nth-child(1) > td:nth-child(3)').text().trim(), 'true', 'check row 1')
equal(el.find('tbody > tr:nth-child(2) > td').length, 3, 'check row 2')
equal(el.find('tbody > tr:nth-child(2) > td:first').text().trim(), '2 normal', 'check row 2')
equal(el.find('tbody > tr:nth-child(2) > td:nth-child(2)').text().trim(), '', 'check row 2')
equal(el.find('tbody > tr:nth-child(3) > td').length, 0, 'check row 3')
result = table.update({sync: true, objects: App.TicketPriority.search({sortBy:'name', order: 'ASC'})})
equal(result[0], 'noChanges')
equal(el.find('table > thead > tr').length, 1, 'row count')
equal(el.find('table > thead > tr > th:nth-child(1)').text().trim(), 'Name', 'check header')
equal(el.find('table > thead > tr > th:nth-child(2)').text().trim(), 'Erstellt', 'check header')
equal(el.find('table > thead > tr > th:nth-child(3)').text().trim(), 'Aktiv', 'check header')
equal(el.find('tbody > tr:nth-child(1) > td').length, 3, 'check row 1')
equal(el.find('tbody > tr:nth-child(1) > td:first').text().trim(), '1 niedrig', 'check row 1')
equal(el.find('tbody > tr:nth-child(1) > td:nth-child(2)').text().trim(), '', 'check row 1')
equal(el.find('tbody > tr:nth-child(1) > td:nth-child(3)').text().trim(), 'true', 'check row 1')
equal(el.find('tbody > tr:nth-child(2) > td').length, 3, 'check row 2')
equal(el.find('tbody > tr:nth-child(2) > td:first').text().trim(), '2 normal', 'check row 2')
equal(el.find('tbody > tr:nth-child(2) > td:nth-child(2)').text().trim(), '', 'check row 2')
}) })