diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7598542fa..304e5c1d4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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 diff --git a/app/assets/javascripts/app/controllers/_dashboard/stats.coffee b/app/assets/javascripts/app/controllers/_dashboard/stats.coffee index 3c138bc06..d4a672c16 100644 --- a/app/assets/javascripts/app/controllers/_dashboard/stats.coffee +++ b/app/assets/javascripts/app/controllers/_dashboard/stats.coffee @@ -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') diff --git a/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_channel_distribution.coffee b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_channel_distribution.coffee new file mode 100644 index 000000000..47e8e752e --- /dev/null +++ b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_channel_distribution.coffee @@ -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') diff --git a/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_escalation.coffee b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_escalation.coffee new file mode 100644 index 000000000..6d29c02cb --- /dev/null +++ b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_escalation.coffee @@ -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') diff --git a/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_in_process.coffee b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_in_process.coffee new file mode 100644 index 000000000..e15cacf0b --- /dev/null +++ b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_in_process.coffee @@ -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') diff --git a/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_load_measure.coffee b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_load_measure.coffee new file mode 100644 index 000000000..74b8112ed --- /dev/null +++ b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_load_measure.coffee @@ -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') diff --git a/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_reopen.coffee b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_reopen.coffee new file mode 100644 index 000000000..1a6b845dd --- /dev/null +++ b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_reopen.coffee @@ -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') diff --git a/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_waiting_time.coffee b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_waiting_time.coffee new file mode 100644 index 000000000..c6e0f85f8 --- /dev/null +++ b/app/assets/javascripts/app/controllers/_dashboard/stats/ticket_waiting_time.coffee @@ -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') diff --git a/app/assets/javascripts/app/views/dashboard/stats.jst.eco b/app/assets/javascripts/app/views/dashboard/stats.jst.eco deleted file mode 100644 index 940f38c1d..000000000 --- a/app/assets/javascripts/app/views/dashboard/stats.jst.eco +++ /dev/null @@ -1,77 +0,0 @@ -