Просмотр исходного кода

Fixes #5384 - Improve heavy used admin objects with pagination and search in admin interface.

Co-authored-by: Mantas Masalskis <mm@zammad.com>
Co-authored-by: Dominik Klein <dk@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Rolf Schmidt 3 месяцев назад
Родитель
Сommit
0c93022abf

+ 2 - 3
.dev/rubocop/todo.yml

@@ -169,7 +169,6 @@ Metrics/BlockLength:
     - 'Gemfile'
     - 'config/initializers/mariadb_json_columns.rb'
     - 'app/models/ticket/can_selector.rb'
-    - 'app/models/concerns/can_selector.rb'
     - 'app/controllers/application_controller/authenticates.rb'
     - 'app/controllers/reports_controller.rb'
     - 'app/controllers/time_accountings_controller.rb'
@@ -186,6 +185,7 @@ Metrics/BlockLength:
     - 'app/models/channel/filter/database.rb'
     - 'app/models/channel/filter/identify_sender.rb'
     - 'app/models/chat/session/search.rb'
+    - 'app/models/concerns/can_search.rb'
     - 'app/models/concerns/can_be_published.rb'
     - 'app/models/concerns/can_clone_attachments.rb'
     - 'app/models/concerns/can_csv_import.rb'
@@ -193,7 +193,6 @@ Metrics/BlockLength:
     - 'app/models/concerns/has_history.rb'
     - 'app/models/concerns/has_rich_text.rb'
     - 'app/models/job.rb'
-    - 'app/models/knowledge_base/search.rb'
     - 'app/models/object_manager/attribute.rb'
     - 'app/models/organization/search.rb'
     - 'app/models/package.rb'
@@ -349,7 +348,6 @@ Metrics/CyclomaticComplexity:
 Metrics/ModuleLength:
   Exclude:
     - 'app/models/ticket/can_selector.rb'
-    - 'app/models/concerns/can_selector.rb'
     - 'app/controllers/application_controller/authenticates.rb'
     - 'app/controllers/application_controller/renders_models.rb'
     - 'app/models/application_model/can_assets.rb'
@@ -357,6 +355,7 @@ Metrics/ModuleLength:
     - 'app/models/application_model/can_creates_and_updates.rb'
     - 'app/models/channel/email_build.rb'
     - 'app/models/channel/filter/identify_sender.rb'
+    - 'app/models/concerns/can_search.rb'
     - 'app/models/concerns/can_be_published.rb'
     - 'app/models/concerns/can_csv_import.rb'
     - 'app/models/concerns/has_groups.rb'

+ 90 - 12
app/assets/javascripts/app/controllers/_application_controller/_generic_index.coffee

@@ -1,14 +1,21 @@
 class App.ControllerGenericIndex extends App.Controller
+  elements:
+    '.js-search': 'searchField'
+
   events:
     'click [data-type=edit]':    'edit'
     'click [data-type=new]':     'new'
     'click [data-type=payload]': 'payload'
     'click [data-type=import]':  'import'
     'click .js-description':     'description'
+    'input .js-search': 'search'
 
   constructor: ->
     super
 
+    @searchQuery        ||= ''
+    @dndCallbackOrignal   = @dndCallback
+
     # set title
     if @pageData.title
       @title @pageData.title, true
@@ -18,7 +25,9 @@ class App.ControllerGenericIndex extends App.Controller
       @navupdate @pageData.navupdate
 
     # bind render after a change is done
-    if !@disableRender
+    if @pageData?.pagerAjax && !@disableRender
+      @controllerBind("#{@genericObject}:create #{@genericObject}:update #{@genericObject}:touch #{@genericObject}:destroy", @delayedRender)
+    else if !@disableRender
       @subscribeId = App[ @genericObject ].subscribe(@render)
 
     App[ @genericObject ].bind 'ajaxError', (rec, msg) =>
@@ -49,12 +58,31 @@ class App.ControllerGenericIndex extends App.Controller
     if @subscribeId
       App[@genericObject].unsubscribe(@subscribeId)
 
-  paginate: (page) =>
-    return if page is @pageData.pagerSelected
+  paginate: (page, params) =>
+    search_query = decodeURIComponent(params?.search_query || '')
+    return if page is @pageData.pagerSelected && @searchQuery is search_query
+
     @pageData.pagerSelected = page
+    @searchQuery = search_query
+
+    if @table && @searchField.val() isnt search_query
+      @searchField.val(search_query)
+
     @render()
 
+  search: ->
+    @delay(
+      =>
+        @navigate "#{@pageData.pagerBaseUrl}1/#{encodeURIComponent(@searchField.val())}"
+    , 300, "#{@controllerId}-render")
+
+  delayedRender: =>
+    @delay(@render, 300, "#{@controllerId}-render")
+
   render: =>
+    if @pageData?.objects
+      @title @pageData.objects, true
+
     if @pageData.pagerAjax
       sortBy  = @table?.customOrderBy || @table?.orderBy || @defaultSortBy  || 'id'
       orderBy = @table?.customOrderDirection || @table?.orderDirection || @defaultOrder || 'ASC'
@@ -66,18 +94,40 @@ class App.ControllerGenericIndex extends App.Controller
         fallbackOrderBy = "#{orderBy}, ASC"
 
       @startLoading()
-      App[@genericObject].indexFull(
+
+      params = {
+        force: true
+        refresh: false
+        sort_by: fallbackSortBy
+        order_by:  fallbackOrderBy
+        page: @pageData.pagerSelected
+        per_page: @pageData.pagerPerPage
+        query: @searchQuery
+      }
+
+      active_filters = []
+      @$('.tab.active').each( (i,d) ->
+        active_filters.push $(d).data('id')
+      )
+
+      if @filterCallback
+        params = @filterCallback(active_filters, params)
+
+      method = 'indexFull'
+      if @searchBar
+        method = 'searchFull'
+
+      App[@genericObject][method](
         (collection, data) =>
           @pageData.pagerTotalCount = data.total_count
+          if data.total_count > @pageData.pagerPerPage || @searchQuery
+            @dndCallback = undefined
+          else if @dndCallback is undefined && @dndCallbackOrignal
+            @dndCallback       = @dndCallbackOrignal
+            @table.renderState = undefined if @table
           @stopLoading()
           @renderObjects(collection)
-        {
-          refresh: false
-          sort_by: fallbackSortBy
-          order_by:  fallbackOrderBy
-          page: @pageData.pagerSelected
-          per_page: @pageData.pagerPerPage
-        }
+        params
       )
       return
 
@@ -108,6 +158,18 @@ class App.ControllerGenericIndex extends App.Controller
         buttons:         @pageData.buttons
         subHead:         @pageData.subHead
         showDescription: showDescription
+        objects:         @pageData.objects
+        searchBar:       @searchBar
+        searchQuery:     @searchQuery
+        filterMenu:      @filterMenu
+      )
+
+      @$('.tab').off('click').on(
+        'click'
+        (e) =>
+          e.preventDefault()
+          $(e.target).toggleClass('active')
+          @delayedRender()
       )
 
       # show description in content if no no content exists
@@ -144,6 +206,7 @@ class App.ControllerGenericIndex extends App.Controller
           pagerPerPage: @pageData.pagerPerPage
           pagerTotalCount: @pageData.pagerTotalCount
           sortRenderCallback: @render
+          searchQuery: @searchQuery
         },
         params
       )
@@ -151,7 +214,7 @@ class App.ControllerGenericIndex extends App.Controller
     if !@table
       @table = new App.ControllerTable(params)
     else
-      @table.update(objects: objects, pagerSelected: @pageData.pagerSelected, pagerTotalCount: @pageData.pagerTotalCount)
+      @table.update(objects: objects, pagerSelected: @pageData.pagerSelected, pagerTotalCount: @pageData.pagerTotalCount, dndCallback: @dndCallback, searchQuery: @searchQuery)
 
     if @pageData.logFacility
       new App.HttpLog(
@@ -183,6 +246,12 @@ class App.ControllerGenericIndex extends App.Controller
       handlers:         @handlers
       validateOnSubmit: @validateOnSubmit
       screen:           @editScreen
+      callback: =>
+        @resetActiveTabs()
+        if @searchQuery
+          @navigate "#{@pageData.pagerBaseUrl}"
+        else
+          @delayedRender()
     )
 
   newControllerClass: ->
@@ -203,6 +272,12 @@ class App.ControllerGenericIndex extends App.Controller
       handlers:         @handlers
       validateOnSubmit: @validateOnSubmit
       screen:           @createScreen
+      callback: =>
+        @resetActiveTabs()
+        if @searchQuery
+          @navigate "#{@pageData.pagerBaseUrl}"
+        else
+          @delayedRender()
     )
 
   clone: (item) =>
@@ -224,3 +299,6 @@ class App.ControllerGenericIndex extends App.Controller
       description: App[ @genericObject ].description
       container:   @container
     )
+
+  resetActiveTabs: ->
+    @$('.tab.active').removeClass('active')

+ 13 - 5
app/assets/javascripts/app/controllers/_application_controller/table.coffee

@@ -123,6 +123,7 @@ class App.ControllerTable extends App.Controller
 
   currentRows: []
 
+  orderEnabled: true
   orderDirection: 'ASC'
   orderBy: undefined
 
@@ -208,6 +209,7 @@ class App.ControllerTable extends App.Controller
       @renderPagerStatic(el, find)
 
   renderPagerAjax: (el, find = false) =>
+    page  = parseInt(@pagerSelected) - 1
     pages = parseInt((@pagerTotalCount - 1)  / @pagerPerPage)
     if pages < 1
       if find
@@ -216,7 +218,7 @@ class App.ControllerTable extends App.Controller
         el.filter('.js-pager').html('')
       return
     pager = App.view('generic/table_pager')(
-      page:  @pagerSelected - 1
+      page:  page
       pages: pages
     )
     if find
@@ -225,7 +227,8 @@ class App.ControllerTable extends App.Controller
       el.filter('.js-pager').html(pager)
 
   renderPagerStatic: (el, find = false) =>
-    pages = parseInt(((@objects.length - 1)  / @pagerItemsPerPage))
+    page      = parseInt(@pagerShownPage)
+    pages     = parseInt(((@objects.length - 1)  / @pagerItemsPerPage))
     if pages < 1
       if find
         el.find('.js-pager').html('')
@@ -233,7 +236,7 @@ class App.ControllerTable extends App.Controller
         el.filter('.js-pager').html('')
       return
     pager = App.view('generic/table_pager')(
-      page:  @pagerShownPage
+      page:  page
       pages: pages
     )
     if find
@@ -750,10 +753,13 @@ class App.ControllerTable extends App.Controller
     @objects.slice(page * @pagerItemsPerPage, (page + 1) * @pagerItemsPerPage)
 
   paginate: (e) =>
+    return if !@pagerEnabled
+
+    e.preventDefault()
     e.stopPropagation()
     page = $(e.currentTarget).attr('data-page')
     if @pagerAjax
-      @navigate "#{@pagerBaseUrl}#{(parseInt(page) + 1)}"
+      @navigate "#{@pagerBaseUrl}#{(parseInt(page) + 1)}/#{encodeURIComponent(@searchQuery)}"
     else
       render = =>
         @pagerShownPage = page
@@ -1176,11 +1182,13 @@ class App.ControllerTable extends App.Controller
           header.displayWidth = @resizeTargetRight.outerWidth()
 
   sortByColumn: (event) =>
+    return if !@orderEnabled
+
     column = $(event.currentTarget).closest('[data-column-key]').attr('data-column-key')
 
     # for ajax pagination we only accept valid attributes for sorting
     if @model && @pagerAjax
-      return if !@attributesList[column]
+      return if !@attributesList[column] || @attributesList[column]?.relation
 
     orderBy = @customOrderBy || @orderBy
     orderDirection = @customOrderDirection || @orderDirection

+ 1 - 1
app/assets/javascripts/app/controllers/_plugin/navigation.coffee

@@ -172,7 +172,7 @@ class Navigation extends App.Controller
         type:      'personal'
       )
 
-  renderResult: (result = []) =>
+  renderResult: (result = {}) =>
     @removePopovers()
 
     # remove result if not result exists

+ 4 - 2
app/assets/javascripts/app/controllers/group.coffee

@@ -9,6 +9,8 @@ class Group extends App.ControllerSubContent
       id: @id
       genericObject: 'Group'
       defaultSortBy: 'name'
+      searchBar: true
+      searchQuery: @search_query
       pageData:
         home:      'groups'
         object:    __('Group')
@@ -16,7 +18,7 @@ class Group extends App.ControllerSubContent
         pagerAjax: true
         pagerBaseUrl: '#manage/groups/'
         pagerSelected: ( @page || 1 )
-        pagerPerPage: 150
+        pagerPerPage: 50
         navupdate: '#groups'
         notes:     [
           __('Groups are …')
@@ -32,6 +34,6 @@ class Group extends App.ControllerSubContent
       if key isnt 'el' && key isnt 'shown' && key isnt 'match'
         @[key] = value
 
-    @genericController.paginate( @page || 1 )
+    @genericController.paginate(@page || 1, params)
 
 App.Config.set('Group', { prio: 1500, name: __('Groups'), parent: '#manage', target: '#manage/groups', controller: Group, permission: ['admin.group'] }, 'NavBarAdmin')

+ 4 - 2
app/assets/javascripts/app/controllers/macro.coffee

@@ -9,6 +9,8 @@ class Macro extends App.ControllerSubContent
       id: @id
       genericObject: 'Macro'
       defaultSortBy: 'name'
+      searchBar: true
+      searchQuery: @search_query
       pageData:
         home: 'macros'
         object: __('Macro')
@@ -16,7 +18,7 @@ class Macro extends App.ControllerSubContent
         pagerAjax: true
         pagerBaseUrl: '#manage/macros/'
         pagerSelected: ( @page || 1 )
-        pagerPerPage: 150
+        pagerPerPage: 50
         navupdate: '#macros'
         buttons: [
           { name: __('New Macro'), 'data-type': 'new', class: 'btn--success' }
@@ -29,6 +31,6 @@ class Macro extends App.ControllerSubContent
       if key isnt 'el' && key isnt 'shown' && key isnt 'match'
         @[key] = value
 
-    @genericController.paginate( @page || 1 )
+    @genericController.paginate(@page || 1, params)
 
 App.Config.set('Macros', { prio: 2310, name: __('Macros'), parent: '#manage', target: '#manage/macros', controller: Macro, permission: ['admin.macro'] }, 'NavBarAdmin')

+ 1 - 0
app/assets/javascripts/app/controllers/manage.coffee

@@ -22,6 +22,7 @@ class ManageRouter extends App.ControllerPermanent
 App.Config.set('manage', ManageRouter, 'Routes')
 App.Config.set('manage/:target', ManageRouter, 'Routes')
 App.Config.set('manage/:target/:page', ManageRouter, 'Routes')
+App.Config.set('manage/:target/:page/:search_query', ManageRouter, 'Routes')
 App.Config.set('settings/:target', ManageRouter, 'Routes')
 App.Config.set('channels/:target', ManageRouter, 'Routes')
 App.Config.set('channels/:target/error/:error_code', ManageRouter, 'Routes')

+ 4 - 2
app/assets/javascripts/app/controllers/organization.coffee

@@ -14,6 +14,8 @@ class Organization extends App.ControllerSubContent
           container: @el.closest('.content')
         )
       defaultSortBy: 'name'
+      searchBar: true
+      searchQuery: @search_query
       pageData:
         home: 'organizations'
         object: __('Organization')
@@ -21,7 +23,7 @@ class Organization extends App.ControllerSubContent
         pagerAjax: true
         pagerBaseUrl: '#manage/organizations/'
         pagerSelected: ( @page || 1 )
-        pagerPerPage: 150
+        pagerPerPage: 50
         navupdate: '#organizations'
         buttons: [
           { name: __('Import'), 'data-type': 'import', class: 'btn' }
@@ -35,7 +37,7 @@ class Organization extends App.ControllerSubContent
       if key isnt 'el' && key isnt 'shown' && key isnt 'match'
         @[key] = value
 
-    @genericController.paginate( @page || 1 )
+    @genericController.paginate(@page || 1, params)
 
 
 App.Config.set('Organization', { prio: 2000, name: __('Organizations'), parent: '#manage', target: '#manage/organizations', controller: Organization, permission: ['admin.organization'] }, 'NavBarAdmin')

+ 13 - 0
app/assets/javascripts/app/controllers/overview.coffee

@@ -15,10 +15,16 @@ class Overview extends App.ControllerSubContent
       genericObject: 'Overview'
       defaultSortBy: 'prio'
       #groupBy: 'role'
+      searchBar: true
+      searchQuery: @search_query
       pageData:
         home: 'overviews'
         object: __('Overview')
         objects: __('Overviews')
+        pagerAjax: true
+        pagerBaseUrl: '#manage/overviews/'
+        pagerSelected: ( @page || 1 )
+        pagerPerPage: 50
         navupdate: '#overviews'
         buttons: [
           { name: __('New Overview'), 'data-type': 'new', class: 'btn--success' }
@@ -43,4 +49,11 @@ class Overview extends App.ControllerSubContent
         )
     )
 
+  show: (params) =>
+    for key, value of params
+      if key isnt 'el' && key isnt 'shown' && key isnt 'match'
+        @[key] = value
+
+    @genericController.paginate(@page || 1, params)
+
 App.Config.set('Overview', { prio: 2300, name: __('Overviews'), parent: '#manage', target: '#manage/overviews', controller: Overview, permission: ['admin.overview'] }, 'NavBarAdmin')

+ 4 - 2
app/assets/javascripts/app/controllers/role.coffee

@@ -12,6 +12,8 @@ class Role extends App.ControllerSubContent
       defaultSortBy: 'name'
       createScreen: 'create'
       editScreen: 'edit'
+      searchBar: true
+      searchQuery: @search_query
       pageData:
         home:      'roles'
         object:    __('Role')
@@ -19,7 +21,7 @@ class Role extends App.ControllerSubContent
         pagerAjax: true
         pagerBaseUrl: '#manage/roles/'
         pagerSelected: ( @page || 1 )
-        pagerPerPage: 150
+        pagerPerPage: 50
         navupdate: '#roles'
         notes:     [
           __('Roles are …')
@@ -35,6 +37,6 @@ class Role extends App.ControllerSubContent
       if key isnt 'el' && key isnt 'shown' && key isnt 'match'
         @[key] = value
 
-    @genericController.paginate( @page || 1 )
+    @genericController.paginate(@page || 1, params)
 
 App.Config.set('Role', { prio: 1600, name: __('Roles'), parent: '#manage', target: '#manage/roles', controller: Role, permission: ['admin.role'] }, 'NavBarAdmin')

Некоторые файлы не были показаны из-за большого количества измененных файлов