Browse Source

Refactoring: Migrated static dashboard widgets to extendable backend structure.

Denny Bresch 6 years ago
parent
commit
39bdbf22db

+ 13 - 13
.gitlab-ci.yml

@@ -359,11 +359,22 @@ browser:build:
     - name: registry.znuny.com/docker/docker-imap-devel:latest
       alias: mail
 
-## Capybara
+## Browser core tests
 
-.test_capybara_template: &test_capybara_definition
+.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
+  <<: *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
 

+ 20 - 100
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()
-
-  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
-
-    @html App.view('dashboard/stats')(data)
-
-    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()
+    @setupStatsWidget('Stats', 'stats', @el)
+
+  setupStatsWidget: (config, event, el) ->
+
+    # 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)
+
+      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')

+ 40 - 0
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')

+ 27 - 0
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')

+ 28 - 0
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')

+ 29 - 0
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')

+ 26 - 0
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')

+ 72 - 0
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')

+ 0 - 77
app/assets/javascripts/app/views/dashboard/stats.jst.eco

@@ -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>

+ 17 - 0
app/assets/javascripts/app/views/dashboard/stats/ticket_channel_distribution.jst.eco

@@ -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>

Some files were not shown because too many files changed in this diff