Browse Source

Added initial version of Out Of Office functionality.

Thorsten Eckel 7 years ago
parent
commit
20a8e40484

+ 4 - 1
app/assets/javascripts/app/controllers/_application_controller.coffee

@@ -344,7 +344,10 @@ class App.Controller extends Spine.Controller
       title: ->
         userId = $(@).data('id')
         user   = App.User.find(userId)
-        App.Utils.htmlEscape(user.displayName())
+        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)

+ 164 - 0
app/assets/javascripts/app/controllers/_profile/out_of_office.coffee

@@ -0,0 +1,164 @@
+class Index extends App.ControllerSubContent
+  requiredPermission: 'user_preferences.out_of_office+ticket.agent'
+  header: 'Out of Office'
+  events:
+    'submit form': 'submit'
+    'click .js-disabled': 'disable'
+    'click .js-enable': 'enable'
+
+  constructor: ->
+    super
+    @render()
+
+  render: =>
+    user = @Session.get()
+    if !@localData
+      @localData =
+        out_of_office: user.out_of_office
+        out_of_office_start_at: user.out_of_office_start_at
+        out_of_office_end_at: user.out_of_office_end_at
+        out_of_office_replacement_id: user.out_of_office_replacement_id
+        out_of_office_replacement_id_completion: user.preferences.out_of_office_replacement_id_completion
+        out_of_office_text: user.preferences.out_of_office_text
+    form = $(App.view('profile/out_of_office')(
+      user: user
+      localData: @localData
+      placeholder: App.User.outOfOfficeTextPlaceholder()
+    ))
+
+    dateStart = new App.ControllerForm(
+      model:
+        configure_attributes:
+          [
+            name: 'out_of_office_start_at'
+            display: ''
+            tag: 'date'
+            past: false
+            future: true
+            null: false
+          ]
+      noFieldset: true
+      params: @localData
+    )
+    form.find('.js-startDate').html(dateStart.form)
+
+    dateEnd = new App.ControllerForm(
+      model:
+        configure_attributes:
+          [
+            name: 'out_of_office_end_at'
+            display: ''
+            tag: 'date'
+            past: false
+            future: true
+            null: false
+          ]
+      noFieldset: true
+      params: @localData
+    )
+    form.find('.js-endDate').html(dateEnd.form)
+
+    agentList = new App.ControllerForm(
+      model:
+        configure_attributes:
+          [
+            name: 'out_of_office_replacement_id'
+            display: ''
+            relation: 'User'
+            tag: 'user_autocompletion'
+            autocapitalize: false
+            multiple: false
+            limit: 30
+            minLengt: 2
+            placeholder: 'Enter Person or Organization/Company'
+            null: false
+            translate: false
+            disableCreateObject: true
+            value: @localData
+          ]
+      noFieldset: true
+      params: @localData
+    )
+    form.find('.js-recipientDropdown').html(agentList.form)
+    if @localData.out_of_office is true
+      form.find('.js-disabled').removeClass('is-disabled')
+      #form.find('.js-enable').addClass('is-disabled')
+    else
+      form.find('.js-disabled').addClass('is-disabled')
+      #form.find('.js-enable').removeClass('is-disabled')
+    @html(form)
+
+  enable: (e) =>
+    e.preventDefault()
+    params = @formParam(e.target)
+    params.out_of_office = true
+    @store(e, params)
+
+  disable: (e) =>
+    e.preventDefault()
+    params = @formParam(e.target)
+    params.out_of_office = false
+    @store(e, params)
+
+  submit: (e, params) =>
+    e.preventDefault()
+    params = @formParam(e.target)
+    @store(e, params)
+
+  store: (e, params) =>
+    @formDisable(e)
+    for key, value of params
+      @localData[key] = value
+    App.Ajax.request(
+      id:          'user_out_of_office'
+      type:        'PUT'
+      url:         "#{@apiPath}/users/out_of_office"
+      data:        JSON.stringify(params)
+      processData: true
+      success:     @success
+      error:       @error
+    )
+
+  success: (data) =>
+    if data.message is 'ok'
+      @render()
+      @notify(
+        type: 'success'
+        msg:  App.i18n.translateContent('Successfully!')
+        timeout: 1000
+      )
+    else
+      if data.notice
+        @notify
+          type:      'error'
+          msg:       App.i18n.translateContent(data.notice[0], data.notice[1])
+          removeAll: true
+      else
+        @notify
+          type:      'error'
+          msg:       'Please contact your administrator.'
+          removeAll: true
+      @formEnable( @$('form') )
+
+  error: (xhr, status, error) =>
+    @formEnable( @$('form') )
+
+    # do not close window if request is aborted
+    return if status is 'abort'
+    data = JSON.parse(xhr.responseText)
+
+    # show error message
+    if xhr.status is 401 || error is 'Unauthorized'
+      message     = '» ' + App.i18n.translateInline('Unauthorized') + ' «'
+    else if xhr.status is 404 || error is 'Not Found'
+      message     = '» ' + App.i18n.translateInline('Not Found') + ' «'
+    else if data.error
+      message     = App.i18n.translateInline(data.error)
+    else
+      message     = '» ' + App.i18n.translateInline('Error') + ' «'
+    @notify
+      type:      'error'
+      msg:       App.i18n.translateContent(message)
+      removeAll: true
+
+App.Config.set('OutOfOffice', { prio: 2800, name: 'Out of Office', parent: '#profile', target: '#profile/out_of_office', permission: ['user_preferences.out_of_office+ticket.agent'], controller: Index }, 'NavBarProfile')

+ 2 - 2
app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee

@@ -20,7 +20,7 @@ class App.UiElement.postmaster_set
             name:     'Customer'
             relation: 'User'
             tag:      'user_autocompletion'
-            disableCreateUser: true
+            disableCreateObject: true
           }
           {
             value:    'group_id'
@@ -32,7 +32,7 @@ class App.UiElement.postmaster_set
             name:     'Owner'
             relation: 'User'
             tag:      'user_autocompletion'
-            disableCreateUser: true
+            disableCreateObject: true
           }
         ]
       article:

+ 1 - 1
app/assets/javascripts/app/controllers/_ui_element/user_autocompletion_search.coffee

@@ -2,5 +2,5 @@
 class App.UiElement.user_autocompletion_search
   @render: (attributeOrig, params = {}) ->
     attribute = _.clone(attributeOrig)
-    attribute.disableCreateUser = true
+    attribute.disableCreateObject = true
     new App.UserOrganizationAutocompletion(attribute: attribute, params: params).element()

+ 1 - 1
app/assets/javascripts/app/controllers/layout_ref.coffee

@@ -1499,7 +1499,7 @@ class InputsRef extends App.ControllerContent
         null: false
         relation: 'User'
         autocapitalize: false
-        disableCreateUser: true
+        disableCreateObject: true
         multiple: true
 
     @$('.userOrganizationAutocompletePlaceholder').replaceWith( userOrganizationAutocomplete.element() )

+ 1 - 1
app/assets/javascripts/app/controllers/ticket_customer.coffee

@@ -6,7 +6,7 @@ class App.TicketCustomer extends App.ControllerModal
 
   content: ->
     configure_attributes = [
-      { name: 'customer_id', display: 'Customer', tag: 'user_autocompletion', null: false, placeholder: 'Enter Person or Organization/Company', minLengt: 2, disableCreateUser: true },
+      { name: 'customer_id', display: 'Customer', tag: 'user_autocompletion', null: false, placeholder: 'Enter Person or Organization/Company', minLengt: 2, disableCreateObject: true },
     ]
     controller = new App.ControllerForm(
       model:

+ 6 - 0
app/assets/javascripts/app/controllers/widget/avatar.coffee

@@ -7,6 +7,12 @@ class App.WidgetAvatar extends App.ObserverController
     email: true
     image: true
     vip: true
+    out_of_office: true,
+    out_of_office_start_at: true,
+    out_of_office_end_at: true,
+    out_of_office_replacement_id: true,
+    active: true
+
   globalRerender: false
 
   render: (user) =>

+ 2 - 1
app/assets/javascripts/app/models/overview.coffee

@@ -8,6 +8,7 @@ class App.Overview extends App.Model
     { name: 'role_ids',   display: 'Available for Role',  tag: 'column_select', multiple: true, null: false, relation: 'Role', translate: true },
     { name: 'user_ids',   display: 'Available for User',  tag: 'column_select', multiple: true, null: true,  relation: 'User', sortBy: 'firstname' },
     { name: 'organization_shared', display: 'Only available for Users with shared Organization', tag: 'select', options: { true: 'yes', false: 'no' }, default: false, null: true },
+    { name: 'out_of_office', display: 'Only available for Users which are replacements for other users.', tag: 'select', options: { true: 'yes', false: 'no' }, default: false, null: true },
     { name: 'condition',  display: 'Conditions for shown Tickets', tag: 'ticket_selector', null: false },
     { name: 'prio',       display: 'Prio',                readonly: 1 },
     {
@@ -72,4 +73,4 @@ Sie können auch individuelle Übersichten für einzelne Agenten oder agenten Gr
 '''
 
   uiUrl: ->
-    '#ticket/view/' + @link
+    "#ticket/view/#{@link}"

+ 33 - 1
app/assets/javascripts/app/models/user.coffee

@@ -53,8 +53,14 @@ class App.User extends App.Model
     cssClass += ' ' if cssClass
     cssClass += "size-#{ size }"
 
+    if @active is false
+      cssClass += ' avatar--inactive'
+
+    if @isOutOfOffice()
+      cssClass += ' avatar--vacation'
+
     if placement
-      placement = " data-placement='#{ placement }'"
+      placement = " data-placement='#{placement}'"
 
     if !avatar
       if type is 'personal'
@@ -104,6 +110,19 @@ class App.User extends App.Model
       vip: vip
       url: @imageUrl()
 
+  isOutOfOffice: ->
+    return false if @out_of_office isnt true
+    start_time = @out_of_office_start_at
+    return false if !start_time
+    end_time = @out_of_office_end_at
+    return false if !end_time
+    start_time = new Date(Date.parse(start_time))
+    end_time = new Date(Date.parse(end_time))
+    now = new Date((new Date).toDateString())
+    if start_time <= now && end_time >= now
+      return true
+    false
+
   imageUrl: ->
     return if !@image
     # set image url
@@ -237,3 +256,16 @@ class App.User extends App.Model
           break
       return access if access
     false
+
+  @outOfOfficeTextPlaceholder: ->
+    today = new Date()
+    outOfOfficeText = 'Christmas holiday'
+    if today.getMonth() < 3
+      outOfOfficeText = 'Easter holiday'
+    else if today.getMonth() < 9
+      outOfOfficeText = 'Summer holiday'
+    outOfOfficeText
+
+  outOfOfficeText: ->
+    return @preferences.out_of_office_text if !_.isEmpty(@preferences.out_of_office_text)
+    App.User.outOfOfficeTextPlaceholder()

+ 34 - 0
app/assets/javascripts/app/views/profile/out_of_office.jst.eco

@@ -0,0 +1,34 @@
+<div class="page-header">
+  <div class="page-header-title"><h1><%- @T('Out of Office') %></h1></div>
+</div>
+
+<form class="action">
+  <div class="action-flow action-flow--row">
+    <div class="action-row">
+      <div class="action-flow action-flow--noWrap">
+        <h2><span class="action-form-status">
+            <% if @localData.out_of_office is true: %>
+              <%- @Icon('status', 'ok inline') %>
+            <% else: %>
+              <%- @Icon('status', 'error inline') %>
+            <% end %></span> <input id="out_of_office_reason" name="out_of_office_text" class="form-control form-control--inline" type="text" placeholder="<%- @Ti('e. g.') %> <%- @Ti(@placeholder) %>" value="<% if !_.isEmpty(@localData.out_of_office_text): %><%= @localData.out_of_office_text %><% end %>"></h2>
+      </div>
+    </div>
+    <div class="action-block action-block--flex">
+      <div class="label"><%- @T('From') %></div>
+      <div class="form-group js-startDate"></div>
+    </div>
+    <div class="action-block action-block--flex">
+      <div class="label"><%- @T('Till') %></div>
+      <div class="form-group js-endDate"></div>
+    </div>
+    <div class="action-row">
+      <label for="out_of_office_replacement"><%- @T('Replacement') %></label>
+      <div class="dropdown js-recipientDropdown"></div>
+    </div>
+    <div class="action-controls">
+      <div class="btn btn--danger js-disabled"><%- @Ti('Disable') %></div>
+      <div class="btn btn--create js-enable"><%- @Ti('Enable') %></div>
+    </div>
+  </div>
+</form>

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