Init version of extended search.
This commit is contained in:
parent
682e019880
commit
e7f95a9e85
3 changed files with 265 additions and 2 deletions
232
app/assets/javascripts/app/controllers/search.coffee
Normal file
232
app/assets/javascripts/app/controllers/search.coffee
Normal file
|
@ -0,0 +1,232 @@
|
|||
class App.Search extends App.Controller
|
||||
searchResultCache: {}
|
||||
elements:
|
||||
'.js-search': 'searchInput'
|
||||
|
||||
events:
|
||||
'submit form.search-holder': 'preventDefault'
|
||||
'keydown .js-search': 'listNavigate'
|
||||
'click .js-tab': 'showTab'
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
# check authentication
|
||||
if !@authenticate(false)
|
||||
App.TaskManager.remove(@task_key)
|
||||
return
|
||||
|
||||
# update taskbar with new meta data
|
||||
App.TaskManager.touch(@task_key)
|
||||
|
||||
@render()
|
||||
|
||||
meta: =>
|
||||
title = App.i18n.translateInline('Extended Search')
|
||||
if @query
|
||||
title += ": #{App.Utils.htmlEscape(@query)}"
|
||||
meta =
|
||||
url: @url()
|
||||
id: ''
|
||||
head: title
|
||||
title: title
|
||||
iconClass: 'magnifier'
|
||||
meta
|
||||
|
||||
url: ->
|
||||
'#search'
|
||||
|
||||
show: (params) =>
|
||||
@navupdate(url: '#search', type: 'menu')
|
||||
console.log('par', params)
|
||||
return if !params.query
|
||||
@$('.js-search').val(decodeURIComponent(params.query)).trigger('change')
|
||||
@searchFunction(200, true)
|
||||
|
||||
hide: ->
|
||||
# nothing
|
||||
|
||||
changed: ->
|
||||
# nothing
|
||||
|
||||
render: ->
|
||||
currentState = App.TaskManager.get(@task_key).state
|
||||
if !@query
|
||||
if currentState && currentState.query
|
||||
@query = currentState.query
|
||||
|
||||
if !@model
|
||||
if currentState && currentState.model
|
||||
@model = currentState.model
|
||||
else
|
||||
@model = 'Ticket'
|
||||
|
||||
@tabs = []
|
||||
for model in App.Config.get('models_searchable')
|
||||
tab =
|
||||
name: model
|
||||
model: model
|
||||
count: 0
|
||||
active: false
|
||||
if @model == model
|
||||
tab.active = true
|
||||
@tabs.push tab
|
||||
|
||||
# build view
|
||||
@html App.view('search/index')(
|
||||
query: @query
|
||||
tabs: @tabs
|
||||
)
|
||||
|
||||
if @query
|
||||
@searchFunction(200, true)
|
||||
|
||||
listNavigate: (e) =>
|
||||
if e.keyCode is 27 # close on esc
|
||||
@empty()
|
||||
return
|
||||
|
||||
# on other keys, show result
|
||||
@searchFunction(200)
|
||||
|
||||
empty: =>
|
||||
@searchInput.val('')
|
||||
|
||||
# remove not needed popovers
|
||||
@delay(@anyPopoversDestroy, 100, 'removePopovers')
|
||||
|
||||
searchFunction: (delay, force = false) =>
|
||||
|
||||
search = =>
|
||||
query = @searchInput.val().trim()
|
||||
if !force
|
||||
return if !query
|
||||
return if query is @query
|
||||
@query = query
|
||||
|
||||
# use cache for search result
|
||||
if @searchResultCache[@query]
|
||||
@renderResult(@searchResultCache[@query].result)
|
||||
currentTime = new Date
|
||||
return if @searchResultCache[@query].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
|
||||
|
||||
@updateTask()
|
||||
|
||||
App.Ajax.request(
|
||||
id: 'search'
|
||||
type: 'GET'
|
||||
url: "#{@apiPath}/search"
|
||||
data:
|
||||
query: @query
|
||||
limit: 200
|
||||
processData: true,
|
||||
success: (data, status, xhr) =>
|
||||
App.Collection.loadAssets(data.assets)
|
||||
result = {}
|
||||
for item in data.result
|
||||
if App[item.type] && App[item.type].find
|
||||
if !result[item.type]
|
||||
result[item.type] = []
|
||||
item_object = App[item.type].find(item.id)
|
||||
if item_object.searchResultAttributes
|
||||
item_object_search_attributes = item_object.searchResultAttributes()
|
||||
result[item.type].push item_object_search_attributes
|
||||
else
|
||||
@log 'error', "No such model #{item.type.toLocaleLowerCase()}.searchResultAttributes()"
|
||||
else
|
||||
@log 'error', "No such model App.#{item.type}"
|
||||
|
||||
diff = false
|
||||
if @searchResultCache[@query]
|
||||
diff = difference(@searchResultCache[@query].resultRaw, data.result)
|
||||
|
||||
# cache search result
|
||||
@searchResultCache[@query] =
|
||||
result: result
|
||||
resultRaw: data.result
|
||||
time: new Date
|
||||
|
||||
# if result hasn't changed, do not rerender
|
||||
return if diff isnt false && _.isEmpty(diff)
|
||||
|
||||
@renderResult(result)
|
||||
)
|
||||
@delay(search, delay, 'search')
|
||||
|
||||
renderResult: (result = []) =>
|
||||
@result = result
|
||||
for tab in @tabs
|
||||
count = 0
|
||||
if result[tab.model]
|
||||
count = result[tab.model].length
|
||||
if @model is tab.model
|
||||
@renderTab(tab.model, result[tab.model] || [])
|
||||
@$(".js-tab#{tab.model} .js-counter").text("(#{count})")
|
||||
|
||||
showTab: (e) =>
|
||||
tabs = $(e.currentTarget).closest('.tabs')
|
||||
tabModel = $(e.currentTarget).data('tab-content')
|
||||
tabs.find('.js-tab').removeClass('active')
|
||||
$(e.currentTarget).addClass('active')
|
||||
@renderTab(tabModel, @result[tabModel] || [])
|
||||
|
||||
renderTab: (model, localList) =>
|
||||
|
||||
# remember last shown model
|
||||
if @model isnt model
|
||||
@model = model
|
||||
@updateTask()
|
||||
|
||||
list = []
|
||||
for item in localList
|
||||
object = App[model].fullLocal(item.id)
|
||||
list.push object
|
||||
if model is 'Ticket'
|
||||
ticket_ids = []
|
||||
for item in localList
|
||||
ticket_ids.push item.id
|
||||
new App.TicketList(
|
||||
table_id: "find_#{model}"
|
||||
el: @$('.js-content')
|
||||
columns: [ 'number', 'title', 'customer', 'group', 'owner', 'created_at' ]
|
||||
ticket_ids: ticket_ids
|
||||
radio: false
|
||||
)
|
||||
else
|
||||
openObject = (id,e) =>
|
||||
object = App[@model].fullLocal(id)
|
||||
@navigate object.uiUrl()
|
||||
new App.ControllerTable(
|
||||
table_id: "find_#{model}"
|
||||
el: @$('.js-content')
|
||||
model: App[model]
|
||||
objects: list
|
||||
bindRow:
|
||||
events:
|
||||
'click': openObject
|
||||
)
|
||||
|
||||
updateTask: =>
|
||||
current = App.TaskManager.get(@task_key).state
|
||||
current.query = @query
|
||||
current.model = @model
|
||||
App.TaskManager.update(@task_key, { state: current })
|
||||
App.TaskManager.touch(@task_key)
|
||||
|
||||
class Router extends App.ControllerPermanent
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
||||
# cleanup params
|
||||
clean_params =
|
||||
query: params.query
|
||||
|
||||
App.TaskManager.execute(
|
||||
key: 'Search'
|
||||
controller: 'Search'
|
||||
params: clean_params
|
||||
show: true
|
||||
)
|
||||
|
||||
App.Config.set('search', Router, 'Routes')
|
||||
App.Config.set('search/:query', Router, 'Routes')
|
|
@ -6,9 +6,15 @@ class App.TicketList extends App.Controller
|
|||
|
||||
render: =>
|
||||
|
||||
openTicket = (id,e) =>
|
||||
ticket = App.Ticket.fullLocal(id)
|
||||
@navigate ticket.uiUrl()
|
||||
callbackTicketTitleAdd = (value, object, attribute, attributes, refObject) ->
|
||||
attribute.title = object.title
|
||||
value
|
||||
callbackLinkToTicket = (value, object, attribute, attributes, refObject) ->
|
||||
attribute.link = object.uiUrl()
|
||||
value
|
||||
callbackUserPopover = (value, object, attribute, attributes, refObject) ->
|
||||
attribute.class = 'user-popover'
|
||||
attribute.data =
|
||||
|
@ -44,10 +50,14 @@ class App.TicketList extends App.Controller
|
|||
list.push ticketItem
|
||||
@el.html('')
|
||||
new App.ControllerTable(
|
||||
table_id: @table_id
|
||||
el: @el
|
||||
overview: [ 'number', 'title', 'customer', 'group', 'created_at' ]
|
||||
overview: @columns || [ 'number', 'title', 'customer', 'group', 'created_at' ]
|
||||
model: App.Ticket
|
||||
objects: list
|
||||
#bindRow:
|
||||
# events:
|
||||
# 'click': openTicket
|
||||
callbackHeader: [ callbackIconHeader ]
|
||||
callbackAttributes:
|
||||
icon:
|
||||
|
@ -59,7 +69,9 @@ class App.TicketList extends App.Controller
|
|||
owner_id:
|
||||
[ callbackUserPopover ]
|
||||
title:
|
||||
[ callbackTicketTitleAdd ]
|
||||
[ callbackLinkToTicket, callbackTicketTitleAdd ]
|
||||
number:
|
||||
[ callbackLinkToTicket, callbackTicketTitleAdd ]
|
||||
radio: @radio
|
||||
)
|
||||
|
||||
|
|
19
app/assets/javascripts/app/views/search/index.jst.eco
Normal file
19
app/assets/javascripts/app/views/search/index.jst.eco
Normal file
|
@ -0,0 +1,19 @@
|
|||
<div class="flex vertical">
|
||||
|
||||
<div class="searchfield">
|
||||
<%- @Icon('magnifier') %>
|
||||
<input class="js-search form-control" name="query" placeholder="<%- @Ti('Find what you search. E. g. "search phrase"') %>" value="<%= @query %>" type="search" autocomplete="off">
|
||||
<div class="empty-search js-emptySearch">
|
||||
<%- @Icon('diagonal-cross') %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs tabs-wide">
|
||||
<% for tab in @tabs: %>
|
||||
<div data-tab-content="<%- tab.model %>" class="tab js-tab js-tab<%- tab.model %><% if tab.active: %> active<% end %>"><%- @T(tab.name) %> <span class="js-counter"><%- tab.count %></span></div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="js-content"></div>
|
||||
|
||||
</div>
|
Loading…
Reference in a new issue