Refactoring: Migrated static dashboard widgets to extendable backend structure.

This commit is contained in:
Denny Bresch 2019-01-24 09:58:58 +01:00 committed by Thorsten Eckel
parent 9d8ce06948
commit 39bdbf22db
20 changed files with 580 additions and 197 deletions

View file

@ -359,11 +359,22 @@ browser:build:
- name: registry.znuny.com/docker/docker-imap-devel:latest
alias: mail
## Browser core tests
.variables_browser_template: &variables_browser_definition
RAILS_ENV: "production"
APP_RESTART_CMD: "bundle exec rake zammad:ci:app:restart"
.test_browser_core_template: &test_browser_core_definition
<<: *base_env
stage: browser-core
dependencies:
- browser:build
## Capybara
.test_capybara_template: &test_capybara_definition
<<: *base_env
stage: browser-core
<<: *test_browser_core_definition
script:
- bundle exec rake zammad:ci:test:prepare[with_elasticsearch]
- bundle exec rspec --fail-fast -t type:system
@ -396,17 +407,6 @@ test:browser:core:capybara_ff_mysql:
<<: *variables_capybara_ff_definition
<<: *services_browser_mysql_definition
## Browser core tests
.variables_browser_template: &variables_browser_definition
RAILS_ENV: "production"
APP_RESTART_CMD: "bundle exec rake zammad:ci:app:restart"
.test_browser_core_template: &test_browser_core_definition
<<: *base_env
stage: browser-core
dependencies:
- browser:build
### API clients

View file

@ -5,103 +5,23 @@ class App.DashboardStats extends App.Controller
@bind('dashboard_stats_rebuild', @load)
load: =>
stats_store = App.StatsStore.first()
if stats_store
@render(stats_store.data)
else
@render()
@setupStatsWidget('Stats', 'stats', @el)
render: (data = {}) ->
if !data.StatsTicketWaitingTime
data.StatsTicketWaitingTime =
handling_time: 0
average: 0
state: 'supergood'
average_per_agent: 0
if !data.StatsTicketEscalation
data.StatsTicketEscalation =
state: 'supergood'
own: 0
total: 0
if !data.StatsTicketChannelDistribution
data.StatsTicketChannelDistribution =
channels:
1:
inbound: 0
outbound: 0
inbound_in_percent: 0
outbound_in_percent: 0
2:
inbound: 0
outbound: 0
inbound_in_percent: 0
outbound_in_percent: 0
3:
inbound: 0
outbound: 0
inbound_in_percent: 0
outbound_in_percent: 0
if !data.StatsTicketLoadMeasure
data.StatsTicketLoadMeasure =
state: 'supergood'
percent: 0
own: 0
total: 0
average_per_agent: 0
if !data.StatsTicketInProcess
data.StatsTicketInProcess =
state: 'supergood'
percent: 0
average_per_agent: 0
if !data.StatsTicketReopen
data.StatsTicketReopen =
state: 'supergood'
percent: 0
average_per_agent: 0
setupStatsWidget: (config, event, el) ->
@html App.view('dashboard/stats')(data)
# load all statsWidgets ./stats/*
App.Event.trigger(event + ':init')
statsWidgets = App.Config.get(config)
if statsWidgets
widgets = $.map(statsWidgets, (v) -> v )
widgets = _.sortBy(widgets, (item) -> return item.prio)
if data.StatsTicketWaitingTime
@renderWidgetClockFace(data.StatsTicketWaitingTime.handling_time, data.StatsTicketWaitingTime.state, data.StatsTicketWaitingTime.percent)
renderWidgetClockFace: (time, state, percent) =>
canvas = @el.find 'canvas'
ctx = canvas.get(0).getContext '2d'
radius = 26
@el.find('.time.stat-widget .stat-amount').text time
canvas.attr 'width', 2 * radius
canvas.attr 'height', 2 * radius
handlingTimeColors = {}
handlingTimeColors['supergood'] = '#38AE6A' # supergood
handlingTimeColors['good'] = '#A9AC41' # good
handlingTimeColors['ok'] = '#FAAB00' # ok
handlingTimeColors['bad'] = '#F6820B' # bad
handlingTimeColors['superbad'] = '#F35910' # superbad
for handlingState, timeColor of handlingTimeColors
if state == handlingState
backgroundColor = timeColor
break
# 30% background
if time isnt 0
ctx.globalAlpha = 0.3
ctx.fillStyle = backgroundColor
ctx.beginPath()
ctx.arc radius, radius, radius, 0, Math.PI * 2, true
ctx.closePath()
ctx.fill()
# 100% pie piece
ctx.globalAlpha = 1
ctx.beginPath()
ctx.moveTo radius, radius
arcsector = Math.PI * 2 * percent
ctx.arc radius, radius, radius, -Math.PI/2, arcsector - Math.PI/2, false
ctx.lineTo radius, radius
ctx.closePath()
ctx.fill()
for widget in widgets
if @permissionCheck(widget.permission)
try
new widget.controller(
el: el
)
catch e
@log 'error', "statsWidgets #{key}:", e
App.Event.trigger(event + ':ready')

View file

@ -0,0 +1,40 @@
class Stats extends App.Controller
constructor: ->
super
@load()
load: =>
stats_store = App.StatsStore.first()
if stats_store
@render(stats_store.data)
else
@render()
render: (data = {}) ->
if !data.StatsTicketChannelDistribution
data.StatsTicketChannelDistribution =
channels:
1:
inbound: 1
outbound: 0
inbound_in_percent: 0
outbound_in_percent: 0
2:
inbound: 0
outbound: 0
inbound_in_percent: 0
outbound_in_percent: 0
3:
inbound: 2
outbound: 0
inbound_in_percent: 0
outbound_in_percent: 0
content = App.view('dashboard/stats/ticket_channel_distribution')(data)
if @$('.ticket_channel_distribution').length > 0
@$('.ticket_channel_distribution').html(content)
else
@el.append(content)
App.Config.set('ticket_channel_distribution', {controller: Stats, permission: 'ticket.agent', prio: 300 }, 'Stats')

View file

@ -0,0 +1,27 @@
class Stats extends App.Controller
constructor: ->
super
@load()
load: =>
stats_store = App.StatsStore.first()
if stats_store
@render(stats_store.data)
else
@render()
render: (data = {}) ->
if !data.StatsTicketEscalation
data.StatsTicketEscalation =
state: 'supergood'
own: 0
total: 0
content = App.view('dashboard/stats/ticket_escalation')(data)
if @$('.ticket_escalation').length > 0
@$('.ticket_escalation').html(content)
else
@el.append(content)
App.Config.set('ticket_escalation', {controller: Stats, permission: 'ticket.agent', prio: 200 }, 'Stats')

View file

@ -0,0 +1,28 @@
class Stats extends App.Controller
constructor: ->
super
@load()
load: =>
stats_store = App.StatsStore.first()
if stats_store
@render(stats_store.data)
else
@render()
render: (data = {}) ->
if !data.StatsTicketInProcess
data.StatsTicketInProcess =
state: 'supergood'
percent: 0
average_per_agent: 0
content = App.view('dashboard/stats/ticket_in_process')(data)
if @$('.ticket_in_process').length > 0
@$('.ticket_in_process').html(content)
else
@el.append(content)
App.Config.set('ticket_in_process', {controller: Stats, permission: 'ticket.agent', prio: 500 }, 'Stats')

View file

@ -0,0 +1,29 @@
class Stats extends App.Controller
constructor: ->
super
@load()
load: =>
stats_store = App.StatsStore.first()
if stats_store
@render(stats_store.data)
else
@render()
render: (data = {}) ->
if !data.StatsTicketLoadMeasure
data.StatsTicketLoadMeasure =
state: 'supergood'
percent: 0
own: 0
total: 0
average_per_agent: 0
content = App.view('dashboard/stats/ticket_load_measure')(data)
if @$('.ticket_load_measure').length > 0
@$('.ticket_load_measure').html(content)
else
@el.append(content)
App.Config.set('ticket_load_measure', {controller: Stats, permission: 'ticket.agent', prio: 400 }, 'Stats')

View file

@ -0,0 +1,26 @@
class Stats extends App.Controller
constructor: ->
super
@load()
load: =>
stats_store = App.StatsStore.first()
if stats_store
@render(stats_store.data)
else
@render()
render: (data = {}) ->
if !data.StatsTicketReopen
data.StatsTicketReopen =
state: 'supergood'
percent: 0
average_per_agent: 0
content = App.view('dashboard/stats/ticket_reopen')(data)
if @$('.ticket_reopen').length > 0
@$('.ticket_reopen').html(content)
else
@el.append(content)
App.Config.set('ticket_reopen', {controller: Stats, permission: 'ticket.agent', prio: 600 }, 'Stats')

View file

@ -0,0 +1,72 @@
class Stats extends App.Controller
constructor: ->
super
@load()
load: =>
stats_store = App.StatsStore.first()
if stats_store
@render(stats_store.data)
else
@render()
render: (data = {}) ->
if !data.StatsTicketWaitingTime
data.StatsTicketWaitingTime =
handling_time: 0
average: 0
state: 'supergood'
average_per_agent: 0
content = App.view('dashboard/stats/ticket_waiting_time')(data)
if @$('.ticket_waiting_time').length > 0
@$('.ticket_waiting_time').html(content)
else
@el.append(content)
if data.StatsTicketWaitingTime
@renderWidgetClockFace(data.StatsTicketWaitingTime.handling_time, data.StatsTicketWaitingTime.state, data.StatsTicketWaitingTime.percent)
renderWidgetClockFace: (time, state, percent) =>
canvas = @el.find 'canvas'
ctx = canvas.get(0).getContext '2d'
radius = 26
@el.find('.time.stat-widget .stat-amount').text time
canvas.attr 'width', 2 * radius
canvas.attr 'height', 2 * radius
handlingTimeColors = {}
handlingTimeColors['supergood'] = '#38AE6A' # supergood
handlingTimeColors['good'] = '#A9AC41' # good
handlingTimeColors['ok'] = '#FAAB00' # ok
handlingTimeColors['bad'] = '#F6820B' # bad
handlingTimeColors['superbad'] = '#F35910' # superbad
for handlingState, timeColor of handlingTimeColors
if state == handlingState
backgroundColor = timeColor
break
# 30% background
if time isnt 0
ctx.globalAlpha = 0.3
ctx.fillStyle = backgroundColor
ctx.beginPath()
ctx.arc radius, radius, radius, 0, Math.PI * 2, true
ctx.closePath()
ctx.fill()
# 100% pie piece
ctx.globalAlpha = 1
ctx.beginPath()
ctx.moveTo radius, radius
arcsector = Math.PI * 2 * percent
ctx.arc radius, radius, radius, -Math.PI/2, arcsector - Math.PI/2, false
ctx.lineTo radius, radius
ctx.closePath()
ctx.fill()
App.Config.set('ticket_waiting_time', {controller: Stats, permission: 'ticket.agent', prio: 100 }, 'Stats')

View file

@ -1,77 +0,0 @@
<div class="column">
<div class="time stat-widget vertical">
<div class="stat-title"><%- @T('∅ Waiting time today') %></div>
<div class="stat-graphic">
<div class="stat-stopwatch centered">
<%- @Icon('stopwatch', 'stat-icon stopwatch-icon') %>
<canvas class="stat-dial"></canvas>
<div class="stat-amount"></div>
</div>
</div>
<div class="stat-label"><%- @T('My handling time: %s minutes', @StatsTicketWaitingTime.handling_time) %></div>
<div class="stat-detail"><%- @T('Average: %s minutes', @StatsTicketWaitingTime.average_per_agent) %></div>
</div>
</div>
<div class="column">
<div class="mood stat-widget vertical">
<div class="stat-title"><%- @T('Mood') %></div>
<div class="stat-graphic">
<%- @Icon("mood-#{@StatsTicketEscalation.state}", 'stat-icon mood-icon') %>
</div>
<div class="stat-label"><%- @T('%s of my tickets escalated.', @StatsTicketEscalation.own) %></div>
<div class="stat-detail"><%- @T('Total: %s', @StatsTicketEscalation.total) %></div>
</div>
</div>
<div class="column">
<div class="channel-distribution stat-widget vertical centered">
<div class="stat-title"><%- @T('Channel Distribution') %></div>
<div class="stat-graphic">
<% for channel_name, channel of @StatsTicketChannelDistribution.channels: %>
<div class="stats-row email-channel">
<%- @Icon(channel.icon, 'stat-channel-icon') %>
<div class="stat-bars">
<div class="stat-bar primary" style="height: <%- channel.inbound_in_percent %>%" title="<%- @Ti('Inbound') %>: <%- channel.inbound_in_percent %>% (<%- channel.inbound %>)"></div>
<div class="stat-bar secondary" style="height: <%- channel.outbound_in_percent %>%" title="<%- @Ti('Outbound') %>: <%- channel.outbound_in_percent %>% (<%- channel.outbound %>)"></div>
</div>
<div class="stat-label"></div>
</div>
<% end %>
</div>
</div>
</div>
<div class="column">
<div class="status stat-widget vertical">
<div class="stat-title"><%- @T('Assigned') %></div>
<div class="stat-graphic">
<div class="stat-tickets vertical reverse end">
<% stack_counter = parseInt(@StatsTicketLoadMeasure.percent*0.16) %>
<% for count in [1..stack_counter]: %>
<%- @Icon('one-ticket', "one-ticket #{@StatsTicketLoadMeasure.state}-color") %>
<% end %>
</div>
<%- @Icon('total-tickets', 'total-tickets') %>
</div>
<div class="stat-label"><%- @T('Tickets assigned to me: %s of %s', @StatsTicketLoadMeasure.own, @StatsTicketLoadMeasure.total) %></div>
<div class="stat-detail"><%- @T('Average: %s', @StatsTicketLoadMeasure.average_per_agent) %></div>
</div>
</div>
<div class="column">
<div class="in-process stat-widget vertical">
<div class="stat-title"><%- @T('Your tickets in process') %></div>
<div class="stat-graphic">
<%- @Icon('in-process', "in-process-icon #{@StatsTicketInProcess.state}-color") %>
</div>
<div class="stat-label" title="<%- @T('%s of your tickets are currently in process.', @StatsTicketInProcess.in_process) %>"><%- @T('%s% are currently in process', @StatsTicketInProcess.percent) %></div>
<div class="stat-detail"><%- @T('Average: %s%', @StatsTicketInProcess.average_per_agent) %></div>
</div>
</div>
<div class="column">
<div class="reopening stat-widget vertical">
<div class="stat-title"><%- @T('Reopening rate') %></div>
<div class="stat-graphic">
<%- @Icon('reopening', "reopening-icon #{@StatsTicketReopen.state}-color") %>
</div>
<div class="stat-label"><%- @T('%s% have been reopened', @StatsTicketReopen.percent) %></div>
<div class="stat-detail"><%- @T('Average: %s%', @StatsTicketReopen.average_per_agent) %></div>
</div>
</div>

View file

@ -0,0 +1,17 @@
<div class="column ticket_channel_distribution">
<div class="stat-widget vertical centered">
<div class="stat-title"><%- @T('Channel Distribution') %></div>
<div class="stat-graphic">
<% for channel_name, channel of @StatsTicketChannelDistribution.channels: %>
<div class="stats-row email-channel">
<%- @Icon(channel.icon, 'stat-channel-icon') %>
<div class="stat-bars">
<div class="stat-bar primary" style="height: <%- channel.inbound_in_percent %>%" title="<%- @Ti('Inbound') %>: <%- channel.inbound_in_percent %>% (<%- channel.inbound %>)"></div>
<div class="stat-bar secondary" style="height: <%- channel.outbound_in_percent %>%" title="<%- @Ti('Outbound') %>: <%- channel.outbound_in_percent %>% (<%- channel.outbound %>)"></div>
</div>
<div class="stat-label"></div>
</div>
<% end %>
</div>
</div>
</div>

View file

@ -0,0 +1,10 @@
<div class="column ticket_escalation">
<div class="stat-widget vertical">
<div class="stat-title"><%- @T('Mood') %></div>
<div class="stat-graphic">
<%- @Icon("mood-#{@StatsTicketEscalation.state}", 'stat-icon mood-icon') %>
</div>
<div class="stat-label"><%- @T('%s of my tickets escalated.', @StatsTicketEscalation.own) %></div>
<div class="stat-detail"><%- @T('Total: %s', @StatsTicketEscalation.total) %></div>
</div>
</div>

View file

@ -0,0 +1,10 @@
<div class="column ticket_in_process">
<div class="stat-widget vertical">
<div class="stat-title"><%- @T('Your tickets in process') %></div>
<div class="stat-graphic">
<%- @Icon('in-process', "in-process-icon #{@StatsTicketInProcess.state}-color") %>
</div>
<div class="stat-label" title="<%- @T('%s of your tickets are currently in process.', @StatsTicketInProcess.in_process) %>"><%- @T('%s% are currently in process', @StatsTicketInProcess.percent) %></div>
<div class="stat-detail"><%- @T('Average: %s%', @StatsTicketInProcess.average_per_agent) %></div>
</div>
</div>

View file

@ -0,0 +1,16 @@
<div class="column ticket_load_measure">
<div class="stat-widget vertical">
<div class="stat-title"><%- @T('Assigned') %></div>
<div class="stat-graphic">
<div class="stat-tickets vertical reverse end">
<% stack_counter = parseInt(@StatsTicketLoadMeasure.percent*0.16) %>
<% for count in [1..stack_counter]: %>
<%- @Icon('one-ticket', "one-ticket #{@StatsTicketLoadMeasure.state}-color") %>
<% end %>
</div>
<%- @Icon('total-tickets', 'total-tickets') %>
</div>
<div class="stat-label"><%- @T('Tickets assigned to me: %s of %s', @StatsTicketLoadMeasure.own, @StatsTicketLoadMeasure.total) %></div>
<div class="stat-detail"><%- @T('Average: %s', @StatsTicketLoadMeasure.average_per_agent) %></div>
</div>
</div>

View file

@ -0,0 +1,10 @@
<div class="column ticket_reopen">
<div class="stat-widget vertical">
<div class="stat-title"><%- @T('Reopening rate') %></div>
<div class="stat-graphic">
<%- @Icon('reopening', "reopening-icon #{@StatsTicketReopen.state}-color") %>
</div>
<div class="stat-label"><%- @T('%s% have been reopened', @StatsTicketReopen.percent) %></div>
<div class="stat-detail"><%- @T('Average: %s%', @StatsTicketReopen.average_per_agent) %></div>
</div>
</div>

View file

@ -0,0 +1,14 @@
<div class="column ticket_waiting_time">
<div class="time stat-widget vertical">
<div class="stat-title"><%- @T('∅ Waiting time today') %></div>
<div class="stat-graphic">
<div class="stat-stopwatch centered">
<%- @Icon('stopwatch', 'stat-icon stopwatch-icon') %>
<canvas class="stat-dial"></canvas>
<div class="stat-amount"></div>
</div>
</div>
<div class="stat-label"><%- @T('My handling time: %s minutes', @StatsTicketWaitingTime.handling_time) %></div>
<div class="stat-detail"><%- @T('Average: %s minutes', @StatsTicketWaitingTime.average_per_agent) %></div>
</div>
</div>

View file

@ -0,0 +1,98 @@
class AddStatsBackends < ActiveRecord::Migration[5.1]
def up
return if !Setting.find_by(name: 'system_init_done')
# add the dashboard stats backend for 'Stats::TicketWaitingTime'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketWaitingTime',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketWaitingTime',
preferences: {
permission: ['ticket.agent'],
prio: 1,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketEscalation'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketEscalation',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketEscalation',
preferences: {
permission: ['ticket.agent'],
prio: 2,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketChannelDistribution'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketChannelDistribution',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketChannelDistribution',
preferences: {
permission: ['ticket.agent'],
prio: 3,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketLoadMeasure'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketLoadMeasure',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketLoadMeasure',
preferences: {
permission: ['ticket.agent'],
prio: 4,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketInProcess'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketInProcess',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketInProcess',
preferences: {
permission: ['ticket.agent'],
prio: 5,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketReopen'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketReopen',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketReopen',
preferences: {
permission: ['ticket.agent'],
prio: 6,
},
frontend: false
)
end
end

View file

@ -4225,3 +4225,93 @@ Setting.create_if_not_exists(
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketWaitingTime'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketWaitingTime',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketWaitingTime',
preferences: {
permission: ['ticket.agent'],
prio: 1,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketEscalation'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketEscalation',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketEscalation',
preferences: {
permission: ['ticket.agent'],
prio: 2,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketChannelDistribution'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketChannelDistribution',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketChannelDistribution',
preferences: {
permission: ['ticket.agent'],
prio: 3,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketLoadMeasure'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketLoadMeasure',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketLoadMeasure',
preferences: {
permission: ['ticket.agent'],
prio: 4,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketInProcess'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketInProcess',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketInProcess',
preferences: {
permission: ['ticket.agent'],
prio: 5,
},
frontend: false
)
# add the dashboard stats backend for 'Stats::TicketReopen'
Setting.create_if_not_exists(
title: 'Stats Backend',
name: 'Stats::TicketReopen',
area: 'Dashboard::Stats',
description: 'Defines a dashboard stats backend that get scheduled automatically.',
options: {},
state: 'Stats::TicketReopen',
preferences: {
permission: ['ticket.agent'],
prio: 6,
},
frontend: false
)

View file

@ -15,15 +15,6 @@ returns
def self.generate
backends = [
Stats::TicketChannelDistribution,
Stats::TicketInProcess,
Stats::TicketLoadMeasure,
Stats::TicketEscalation,
Stats::TicketReopen,
Stats::TicketWaitingTime,
]
# generate stats per agent
users = User.with_permissions('ticket.agent')
agent_count = 0
@ -34,7 +25,24 @@ returns
agent_count += 1
data = {}
backends.each do |backend|
backends = Setting.where(area: 'Dashboard::Stats')
if backends.blank?
raise "No settings with area 'Dashboard::Stats' defined"
end
backends.each do |stats_item|
# additional permission check
next if stats_item.preferences[:permission] && !user.permissions?(stats_item.preferences[:permission])
backend = stats_item.state_current[:value]
if !backend
raise 'Dashboard::Stats backend ' + stats_item.name + ' is not defined'
end
require_dependency backend.to_filename
backend = backend.constantize
data[backend] = backend.generate(user)
end
user_result[user.id] = data

29
spec/lib/stats_spec.rb Normal file
View file

@ -0,0 +1,29 @@
require 'rails_helper'
RSpec.describe Stats do
describe '#generate' do
before do
# create a user for which the stats can be generated
create(:agent_user)
end
it 'generates stats' do
expect { Stats.generate }.to_not raise_error
end
context 'when backend registration is invalid' do
it 'fails for empty registration' do
Setting.set('Stats::TicketWaitingTime', nil)
expect { Stats.generate }.to raise_error(RuntimeError)
end
it 'fails for unknown backend' do
Setting.set('Stats::TicketWaitingTime', 'Stats::UNKNOWN')
expect { Stats.generate }.to raise_error(LoadError)
end
end
end
end

View file

@ -0,0 +1,16 @@
require 'rails_helper'
RSpec.describe 'Dashboard', type: :system, authenticated: true do
it 'shows default widgets' do
visit 'dashboard'
expect(page).to have_css('.stat-widgets')
expect(page).to have_css('.ticket_waiting_time > div > div.stat-title', text: /∅ Waiting time today/i)
expect(page).to have_css('.ticket_escalation > div > div.stat-title', text: /Mood/i)
expect(page).to have_css('.ticket_channel_distribution > div > div.stat-title', text: /Channel Distribution/i)
expect(page).to have_css('.ticket_load_measure > div > div.stat-title', text: /Assigned/i)
expect(page).to have_css('.ticket_in_process > div > div.stat-title', text: /Your tickets in process/i)
expect(page).to have_css('.ticket_reopen > div > div.stat-title', text: /Reopening rate/i)
end
end