Added new user/org autocompletion.
This commit is contained in:
parent
db4df87be1
commit
e116941d1c
19 changed files with 718 additions and 343 deletions
|
@ -753,6 +753,11 @@ class App.ControllerForm extends App.Controller
|
|||
$('#' + attribute.id ).parent().css('height', 'auto')
|
||||
App.Delay.set( a, 120, undefined, 'tags' )
|
||||
|
||||
# user
|
||||
else if attribute.tag is 'user_autocompletion'
|
||||
completion = new App.UserOrganizationAutocompletion( attribute: attribute )
|
||||
item = completion.element()
|
||||
|
||||
# autocompletion
|
||||
else if attribute.tag is 'autocompletion'
|
||||
item = $( App.view('generic/autocompletion')( attribute: attribute ) )
|
||||
|
|
|
@ -4,13 +4,9 @@ class App.TicketCreate extends App.Controller
|
|||
|
||||
events:
|
||||
'click .type-tabs .tab': 'changeFormType'
|
||||
'click .customer_new': 'userNew'
|
||||
'submit form': 'submit'
|
||||
'click .submit': 'submit'
|
||||
'click .cancel': 'cancel'
|
||||
'hide.bs.dropdown .js-recipientDropdown': 'hideOrganisationMembers'
|
||||
'click .js-organisation': 'showOrganisationMembers'
|
||||
'click .js-back': 'hideOrganisationMembers'
|
||||
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
@ -40,61 +36,6 @@ class App.TicketCreate extends App.Controller
|
|||
@log 'notice', 'error', defaults
|
||||
@render(defaults)
|
||||
|
||||
showOrganisationMembers: (e) =>
|
||||
e.stopPropagation()
|
||||
|
||||
listEntry = $(e.currentTarget)
|
||||
organisationId = listEntry.data('organisation')
|
||||
|
||||
@recipientList = @$('.recipientList')
|
||||
@organisationList = @$("##{ organisationId }")
|
||||
|
||||
# move organisation-list to the right and slide it in
|
||||
|
||||
$.Velocity.hook(@organisationList, 'translateX', '100%')
|
||||
@organisationList.removeClass('hide')
|
||||
|
||||
@organisationList.velocity
|
||||
properties:
|
||||
translateX: 0
|
||||
options:
|
||||
speed: 300
|
||||
|
||||
# fade out list
|
||||
|
||||
@recipientList.velocity
|
||||
properties:
|
||||
translateX: '-100%'
|
||||
options:
|
||||
speed: 300
|
||||
complete: => @recipientList.height(@organisationList.height())
|
||||
|
||||
hideOrganisationMembers: (e) =>
|
||||
e && e.stopPropagation()
|
||||
|
||||
return if !@organisationList
|
||||
|
||||
# fade list back in
|
||||
|
||||
@recipientList.velocity
|
||||
properties:
|
||||
translateX: 0
|
||||
options:
|
||||
speed: 300
|
||||
|
||||
# reset list height
|
||||
|
||||
@recipientList.height('')
|
||||
|
||||
# slide out organisation-list and hide it
|
||||
|
||||
@organisationList.velocity
|
||||
properties:
|
||||
translateX: '100%'
|
||||
options:
|
||||
speed: 300
|
||||
complete: => @organisationList.addClass('hide')
|
||||
|
||||
changeFormType: (e) =>
|
||||
type = $(e.target).data('type')
|
||||
if !type
|
||||
|
@ -353,12 +294,6 @@ class App.TicketCreate extends App.Controller
|
|||
params: params
|
||||
)
|
||||
|
||||
userNew: (e) =>
|
||||
e.preventDefault()
|
||||
new UserNew(
|
||||
create_screen: @
|
||||
)
|
||||
|
||||
cancel: (e) ->
|
||||
e.preventDefault()
|
||||
@navigate '#'
|
||||
|
@ -545,68 +480,6 @@ class Sidebar extends App.Controller
|
|||
items: items
|
||||
)
|
||||
|
||||
class UserNew extends App.ControllerModal
|
||||
constructor: ->
|
||||
super
|
||||
@head = 'New User'
|
||||
@cancel = true
|
||||
@button = true
|
||||
|
||||
controller = new App.ControllerForm(
|
||||
el: @el.find('#form-user')
|
||||
model: App.User
|
||||
screen: 'edit'
|
||||
autofocus: true
|
||||
)
|
||||
|
||||
@show( controller.form )
|
||||
|
||||
onSubmit: (e) ->
|
||||
|
||||
e.preventDefault()
|
||||
params = @formParam(e.target)
|
||||
|
||||
# if no login is given, use emails as fallback
|
||||
if !params.login && params.email
|
||||
params.login = params.email
|
||||
|
||||
# find role_id
|
||||
if !params.role_ids || _.isEmpty( params.role_ids )
|
||||
role = App.Role.findByAttribute( 'name', 'Customer' )
|
||||
params.role_ids = role.id
|
||||
@log 'notice', 'updateAttributes', params
|
||||
|
||||
user = new App.User
|
||||
user.load(params)
|
||||
|
||||
errors = user.validate()
|
||||
if errors
|
||||
@log 'error', errors
|
||||
@formValidate( form: e.target, errors: errors )
|
||||
return
|
||||
|
||||
# save user
|
||||
ui = @
|
||||
user.save(
|
||||
done: ->
|
||||
|
||||
# force to reload object
|
||||
callbackReload = (user) ->
|
||||
realname = user.displayName()
|
||||
if user.email
|
||||
realname = "#{ realname } <#{ user.email }>"
|
||||
ui.create_screen.el.find('[name=customer_id]').val( user.id )
|
||||
ui.create_screen.el.find('[name=customer_id_autocompletion]').val( realname )
|
||||
|
||||
# start customer info controller
|
||||
ui.userInfo( user_id: user.id )
|
||||
ui.hide()
|
||||
App.User.full( @id, callbackReload , true )
|
||||
|
||||
fail: ->
|
||||
ui.hide()
|
||||
)
|
||||
|
||||
class Router extends App.ControllerPermanent
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
|
|
@ -10,6 +10,11 @@ App.Config.set( 'layout_ref', Index, 'Routes' )
|
|||
|
||||
|
||||
class Content extends App.ControllerContent
|
||||
events:
|
||||
'hide.bs.dropdown .js-recipientDropdown': 'hideOrganisationMembers'
|
||||
'click .js-organisation': 'showOrganisationMembers'
|
||||
'click .js-back': 'hideOrganisationMembers'
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
@render()
|
||||
|
@ -35,6 +40,60 @@ class Content extends App.ControllerContent
|
|||
render: ->
|
||||
@html App.view('layout_ref/content')()
|
||||
|
||||
showOrganisationMembers: (e) =>
|
||||
e.stopPropagation()
|
||||
|
||||
listEntry = $(e.currentTarget)
|
||||
organisationId = listEntry.data('organisation-id')
|
||||
|
||||
@recipientList = @$('.recipientList')
|
||||
@organisationList = @$("##{ organisationId }")
|
||||
|
||||
# move organisation-list to the right and slide it in
|
||||
|
||||
$.Velocity.hook(@organisationList, 'translateX', '100%')
|
||||
@organisationList.removeClass('hide')
|
||||
|
||||
@organisationList.velocity
|
||||
properties:
|
||||
translateX: 0
|
||||
options:
|
||||
speed: 300
|
||||
|
||||
# fade out list
|
||||
|
||||
@recipientList.velocity
|
||||
properties:
|
||||
translateX: '-100%'
|
||||
options:
|
||||
speed: 300
|
||||
complete: => @recipientList.height(@organisationList.height())
|
||||
|
||||
hideOrganisationMembers: (e) =>
|
||||
e && e.stopPropagation()
|
||||
|
||||
return if !@organisationList
|
||||
|
||||
# fade list back in
|
||||
|
||||
@recipientList.velocity
|
||||
properties:
|
||||
translateX: 0
|
||||
options:
|
||||
speed: 300
|
||||
|
||||
# reset list height
|
||||
|
||||
@recipientList.height('')
|
||||
|
||||
# slide out organisation-list and hide it
|
||||
@organisationList.velocity
|
||||
properties:
|
||||
translateX: '100%'
|
||||
options:
|
||||
speed: 300
|
||||
complete: => @organisationList.addClass('hide')
|
||||
|
||||
App.Config.set( 'layout_ref/content', Content, 'Routes' )
|
||||
|
||||
|
||||
|
|
|
@ -2,15 +2,13 @@ class App.TicketCustomer extends App.ControllerModal
|
|||
constructor: ->
|
||||
super
|
||||
configure_attributes = [
|
||||
{ name: 'customer_id', display: 'Customer', tag: 'autocompletion', type: 'text', limit: 100, null: false, relation: 'User', class: 'span5', autocapitalize: false, help: 'Select the new customer of the Ticket.', source: @apiPath + '/users/search', minLengt: 2 },
|
||||
{ name: 'customer_id', display: 'Customer', tag: 'user_autocompletion', null: false, placeholder: 'Enter Person or Organisation/Company', minLengt: 2, disableCreateUser: true },
|
||||
]
|
||||
|
||||
controller = new App.ControllerForm(
|
||||
model: {
|
||||
model:
|
||||
configure_attributes: configure_attributes,
|
||||
className: 'update',
|
||||
},
|
||||
autofocus: true,
|
||||
autofocus: true
|
||||
)
|
||||
@head = 'Change Customer'
|
||||
@close = true
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
class App.UserOrganizationAutocompletion extends App.Controller
|
||||
events:
|
||||
'hide.bs.dropdown .js-recipientDropdown': 'hideOrganisationMembers'
|
||||
'click .js-organisation': 'showOrganisationMembers'
|
||||
'click .js-back': 'hideOrganisationMembers'
|
||||
'click .js-user': 'selectUser'
|
||||
'click .js-user-new': 'newUser'
|
||||
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
||||
@key = Math.floor( Math.random() * 999999 ).toString()
|
||||
|
||||
if !@attribute.source
|
||||
@attribute.source = @apiPath + '/search_user_org'
|
||||
@build()
|
||||
|
||||
element: =>
|
||||
@el
|
||||
|
||||
selectUser: (e) ->
|
||||
userId = $(e.target).parents('.recipientList-entry').data('user-id')
|
||||
if !userId
|
||||
userId = $(e.target).data('user-id')
|
||||
|
||||
@el.find('[name="' + @attribute.name + '"]').val( userId ).trigger('change')
|
||||
|
||||
setUser: ->
|
||||
userId = @el.find('[name="' + @attribute.name + '"]').val()
|
||||
return if !userId
|
||||
user = App.User.find(userId)
|
||||
name = user.displayName()
|
||||
if user.email
|
||||
name += " <#{user.email}>"
|
||||
@el.find('[name="' + @attribute.name + '_completion"]').val( name ).trigger('change')
|
||||
|
||||
if @callback
|
||||
@callback(userId)
|
||||
|
||||
buildOrganizationItem: (organization) =>
|
||||
App.view('generic/user_search/item_organization')(
|
||||
organization: organization
|
||||
)
|
||||
|
||||
buildOrganizationMembers: (organization) =>
|
||||
organizationMemebers = $( App.view('generic/user_search/item_organization_members')(
|
||||
organization: organization
|
||||
) )
|
||||
for userId in organization.member_ids
|
||||
user = App.User.fullLocal(userId)
|
||||
organizationMemebers.append( @buildUserItem(user) )
|
||||
|
||||
buildUserItem: (user) =>
|
||||
App.view('generic/user_search/item_user')(
|
||||
user: user
|
||||
)
|
||||
|
||||
buildUserNew: =>
|
||||
App.view('generic/user_search/new_user')()
|
||||
|
||||
build: =>
|
||||
@el.html App.view('generic/user_search/input')(
|
||||
attribute: @attribute
|
||||
)
|
||||
@el.find('[name="' + @attribute.name + '"]').on(
|
||||
'change',
|
||||
(e) =>
|
||||
@setUser()
|
||||
)
|
||||
|
||||
@el.find('[name="' + @attribute.name + '_completion"]').on(
|
||||
'keyup',
|
||||
(e) =>
|
||||
item = $(e.target).val()
|
||||
|
||||
#@log('CC', e.keyCode, item)
|
||||
|
||||
# clean input field on ESC
|
||||
if e.keyCode is 27
|
||||
$(e.target).val('')
|
||||
item = ''
|
||||
|
||||
# ignore arrow keys
|
||||
return if e.keyCode is 37
|
||||
return if e.keyCode is 38
|
||||
return if e.keyCode is 39
|
||||
return if e.keyCode is 40
|
||||
|
||||
# ignore shift
|
||||
return if e.keyCode is 16
|
||||
|
||||
# ignore ctrl
|
||||
return if e.keyCode is 17
|
||||
|
||||
# ignore alt
|
||||
return if e.keyCode is 18
|
||||
|
||||
# hide dropdown
|
||||
@el.find('.recipientList').html('')
|
||||
@el.find('.recipientList-organisationMembers').remove()
|
||||
if !item && !@attribute.disableCreateUser
|
||||
@el.find('.recipientList').append( @buildUserNew() )
|
||||
|
||||
# show dropdown
|
||||
if item && ( !@attribute.minLengt || @attribute.minLengt <= item.length )
|
||||
execute = => @searchUser(item)
|
||||
@delay( execute, 400, 'userSearch' )
|
||||
)
|
||||
|
||||
searchUser: (term) =>
|
||||
|
||||
@ajax(
|
||||
id: 'searchUser' + @key
|
||||
type: 'GET'
|
||||
url: @attribute.source
|
||||
data:
|
||||
query: term
|
||||
processData: true
|
||||
success: (data, status, xhr) =>
|
||||
# load assets
|
||||
App.Collection.loadAssets( data.assets )
|
||||
|
||||
# build markup
|
||||
for item in data.result
|
||||
|
||||
# organization
|
||||
if item.type is 'Organization'
|
||||
organization = App.Organization.fullLocal( item.id )
|
||||
@el.find('.recipientList').append( @buildOrganizationItem(organization) )
|
||||
|
||||
# users of organization
|
||||
if organization.member_ids
|
||||
@el.find('.dropdown-menu').append( @buildOrganizationMembers(organization) )
|
||||
|
||||
# users
|
||||
if item.type is 'User'
|
||||
user = App.User.fullLocal( item.id )
|
||||
@el.find('.recipientList').append( @buildUserItem(user) )
|
||||
|
||||
if !@attribute.disableCreateUser
|
||||
@el.find('.recipientList').append( @buildUserNew() )
|
||||
)
|
||||
|
||||
showOrganisationMembers: (e) =>
|
||||
e.stopPropagation()
|
||||
|
||||
listEntry = $(e.currentTarget)
|
||||
organisationId = listEntry.data('organisation-id')
|
||||
|
||||
@recipientList = @$('.recipientList')
|
||||
@organisationList = @$("##{ organisationId }")
|
||||
|
||||
# move organisation-list to the right and slide it in
|
||||
|
||||
$.Velocity.hook(@organisationList, 'translateX', '100%')
|
||||
@organisationList.removeClass('hide')
|
||||
|
||||
@organisationList.velocity
|
||||
properties:
|
||||
translateX: 0
|
||||
options:
|
||||
speed: 300
|
||||
|
||||
# fade out list
|
||||
@recipientList.velocity
|
||||
properties:
|
||||
translateX: '-100%'
|
||||
options:
|
||||
speed: 300
|
||||
complete: => @recipientList.height(@organisationList.height())
|
||||
|
||||
hideOrganisationMembers: (e) =>
|
||||
e && e.stopPropagation()
|
||||
|
||||
return if !@organisationList
|
||||
|
||||
# fade list back in
|
||||
@recipientList.velocity
|
||||
properties:
|
||||
translateX: 0
|
||||
options:
|
||||
speed: 300
|
||||
|
||||
# reset list height
|
||||
|
||||
@recipientList.height('')
|
||||
|
||||
# slide out organisation-list and hide it
|
||||
@organisationList.velocity
|
||||
properties:
|
||||
translateX: '100%'
|
||||
options:
|
||||
speed: 300
|
||||
complete: => @organisationList.addClass('hide')
|
||||
|
||||
newUser: (e) =>
|
||||
e.preventDefault()
|
||||
new UserNew(
|
||||
parent: @
|
||||
)
|
||||
|
||||
class UserNew extends App.ControllerModal
|
||||
constructor: ->
|
||||
super
|
||||
@head = 'New User'
|
||||
@cancel = true
|
||||
@button = true
|
||||
|
||||
controller = new App.ControllerForm(
|
||||
el: @el.find('#form-user')
|
||||
model: App.User
|
||||
screen: 'edit'
|
||||
autofocus: true
|
||||
)
|
||||
|
||||
@show( controller.form )
|
||||
|
||||
onSubmit: (e) ->
|
||||
|
||||
e.preventDefault()
|
||||
params = @formParam(e.target)
|
||||
|
||||
# if no login is given, use emails as fallback
|
||||
if !params.login && params.email
|
||||
params.login = params.email
|
||||
|
||||
# find role_id
|
||||
if !params.role_ids || _.isEmpty( params.role_ids )
|
||||
role = App.Role.findByAttribute( 'name', 'Customer' )
|
||||
params.role_ids = role.id
|
||||
@log 'notice', 'updateAttributes', params
|
||||
|
||||
user = new App.User
|
||||
user.load(params)
|
||||
|
||||
errors = user.validate()
|
||||
if errors
|
||||
@log 'error', errors
|
||||
@formValidate( form: e.target, errors: errors )
|
||||
return
|
||||
|
||||
# save user
|
||||
ui = @
|
||||
user.save(
|
||||
done: ->
|
||||
|
||||
# force to reload object
|
||||
callbackReload = (user) ->
|
||||
ui.parent.el.find('[name=customer_id]').val( user.id ).trigger('change')
|
||||
|
||||
# start customer info controller
|
||||
ui.hide()
|
||||
App.User.full( @id, callbackReload , true )
|
||||
|
||||
fail: ->
|
||||
ui.hide()
|
||||
)
|
|
@ -25,189 +25,7 @@
|
|||
|
||||
<form role="form" class="ticket-create">
|
||||
<input type="hidden" name="formSenderType"/>
|
||||
<div class="form-group">
|
||||
<label for="customer_id">Customer</label>
|
||||
<div class="dropdown js-recipientDropdown">
|
||||
<div class="dropdown-toggle u-positionOrigin" data-toggle="dropdown">
|
||||
<input id="customer_id" name="customer_id_autocompletion" class="ui-autocomplete-input form-control" autocapitalize="off" placeholder="Enter Person or Organisation/Company" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">
|
||||
<span class="caret"></span>
|
||||
</div>
|
||||
<div class="dropdown-menu" aria-labelledby="customer_id">
|
||||
<ul class="recipientList" role="menu">
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation" data-organisation="AlbrechtBertschlerGmbG">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Albrecht & Bertschler GmbH
|
||||
<span class="recipientList-detail">- 4 Personen</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation" data-organisation="BENCHMARKhumanresources">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
BENCHMARK human resources
|
||||
<span class="recipientList-detail">- 2 Personen</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Belinda Matt
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Benjamin Wahlers
|
||||
<span class="recipientList-detail">- Wahlers Würste GmbH</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Benjamin Weiß
|
||||
<span class="recipientList-detail">- Zeughaus Werbeagentur GmbH</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Benteler SGL Composite Technology GmbH
|
||||
<span class="recipientList-detail"></span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white team icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Support Team Berlin
|
||||
<span class="recipientList-detail">- 5 Personen</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Bernecker + Rainer Industrie-Elektronik Ges.m.b.H.
|
||||
<span class="recipientList-detail">- 3 Personen</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Bertsch Ecopower GmbH
|
||||
<span class="recipientList-detail">- 1 Person</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry recipientList-new u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white plus icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
<%- @T('Create new Customer') %>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
|
||||
<ul class="recipientList-organisationMembers hide" id="AlbrechtBertschlerGmbG">
|
||||
<li class="recipientList-controls u-clickable js-back">
|
||||
<div class="recipientList-backClickArea centered">
|
||||
<div class="recipientList-backButton">
|
||||
<div class="white arrow left icon"></div>
|
||||
<%- @T('Back') %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Andreas Kramer
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Belinda Matt
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Damian Sprengler
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Katharina Nussman
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="recipientList-organisationMembers hide" id="BENCHMARKhumanresources">
|
||||
<li class="recipientList-controls u-clickable js-back">
|
||||
<div class="recipientList-backClickArea centered">
|
||||
<div class="recipientList-backButton">
|
||||
<div class="white arrow left icon"></div>
|
||||
<%- @T('Back') %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Hubert Lang
|
||||
<span class="recipientList-detail">- BENCHMARK human resources</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Mario Steibel
|
||||
<span class="recipientList-detail">- BENCHMARK human resources</span>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ticket-form-top"></div>
|
||||
<div class="article-form-top"></div>
|
||||
|
||||
|
@ -216,7 +34,6 @@
|
|||
<div class="ticket-form-bottom"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-actions horizontal">
|
||||
<a class="subtle-link standalone cancel" href="#/"><%- @T( 'Cancel & Go Back' ) %></a>
|
||||
<button type="submit" class="btn btn-create submit align-right"><%- @T( 'Create' ) %></button>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<div class="dropdown js-recipientDropdown">
|
||||
|
||||
<div class="dropdown-toggle u-positionOrigin" data-toggle="dropdown">
|
||||
<input type="hidden" name="<%- @attribute.name %>">
|
||||
<input name="<%- @attribute.name %>_completion" class="ui-autocomplete-input form-control" autocapitalize="off" placeholder="<%- @attribute.placeholder %>" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">
|
||||
</div>
|
||||
|
||||
<div class="dropdown-menu" aria-labelledby="customer_id">
|
||||
<ul class="recipientList" role="menu"></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,10 @@
|
|||
<li class="recipientList-entry u-clickable horizontal center js-organisation" data-organisation-id="<%- @organization.id %>">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
<%= @organization.displayName() %>
|
||||
<span class="recipientList-detail">- <%= @organization.member_ids.length %> <%- @T('People') %></span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
</li>
|
|
@ -0,0 +1,10 @@
|
|||
<ul class="recipientList-organisationMembers hide" id="<%- @organization.id %>">
|
||||
<li class="recipientList-controls u-clickable js-back">
|
||||
<div class="recipientList-backClickArea centered">
|
||||
<div class="recipientList-backButton">
|
||||
<div class="white arrow left icon"></div>
|
||||
<%- @T('Back') %>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,11 @@
|
|||
<li class="recipientList-entry u-clickable horizontal center js-user" data-user-id="<%= @user.id %>"">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
<%= @user.displayName() %>
|
||||
<% if @user.organization: %>
|
||||
<span class="recipientList-detail">- <%= @user.organization.displayName() %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</li>
|
|
@ -0,0 +1,8 @@
|
|||
<li class="recipientList-entry recipientList-new u-clickable horizontal center js-user-new">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white plus icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
<%- @T('Create new Customer') %>
|
||||
</div>
|
||||
</li>
|
|
@ -40,6 +40,203 @@
|
|||
<span class="big user avatar" data-firstname="Luisa" data-lastname="Hofner" data-userid="10"></span>
|
||||
<span class="big user avatar" data-firstname="Clara" data-lastname="Altman" data-userid="11"></span>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>User Selection</h2>
|
||||
|
||||
<form>
|
||||
<div class="form-group">
|
||||
|
||||
<label for="customer_id">Customer</label>
|
||||
|
||||
<div class="dropdown js-recipientDropdown">
|
||||
|
||||
<div class="dropdown-toggle u-positionOrigin" data-toggle="dropdown">
|
||||
<input id="customer_id_autocompletion2" name="customer_id_autocompletion" class="ui-autocomplete-input form-control" autocapitalize="off" placeholder="Enter Person or Organisation/Company" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">
|
||||
<span class="caret"></span>
|
||||
</div>
|
||||
|
||||
<div class="dropdown-menu" aria-labelledby="customer_id">
|
||||
|
||||
<ul class="recipientList" role="menu">
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation" data-organisation-id="AlbrechtBertschlerGmbG">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Albrecht & Bertschler GmbH
|
||||
<span class="recipientList-detail">- 4 Personen</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation" data-organisation-id="BENCHMARKhumanresources">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
BENCHMARK human resources
|
||||
<span class="recipientList-detail">- 2 Personen</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Belinda Matt
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Benjamin Wahlers
|
||||
<span class="recipientList-detail">- Wahlers Würste GmbH</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Benjamin Weiß
|
||||
<span class="recipientList-detail">- Zeughaus Werbeagentur GmbH</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Benteler SGL Composite Technology GmbH
|
||||
<span class="recipientList-detail"></span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white team icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Support Team Berlin
|
||||
<span class="recipientList-detail">- 5 Personen</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Bernecker + Rainer Industrie-Elektronik Ges.m.b.H.
|
||||
<span class="recipientList-detail">- 3 Personen</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center js-organisation">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white organisation icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
Bertsch Ecopower GmbH
|
||||
<span class="recipientList-detail">- 1 Person</span>
|
||||
</div>
|
||||
<div class="white right arrow icon"></div>
|
||||
|
||||
<li class="recipientList-entry recipientList-new u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white plus icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat">
|
||||
<%- @T('Create new Customer') %>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
|
||||
<ul class="recipientList-organisationMembers hide" id="AlbrechtBertschlerGmbG">
|
||||
<li class="recipientList-controls u-clickable js-back">
|
||||
<div class="recipientList-backClickArea centered">
|
||||
<div class="recipientList-backButton">
|
||||
<div class="white arrow left icon"></div>
|
||||
<%- @T('Back') %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Andreas Kramer
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Belinda Matt
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Damian Sprengler
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Katharina Nussman
|
||||
<span class="recipientList-detail">- Albrecht & Bertschler GmbG</span>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="recipientList-organisationMembers hide" id="BENCHMARKhumanresources">
|
||||
<li class="recipientList-controls u-clickable js-back">
|
||||
<div class="recipientList-backClickArea centered">
|
||||
<div class="recipientList-backButton">
|
||||
<div class="white arrow left icon"></div>
|
||||
<%- @T('Back') %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Hubert Lang
|
||||
<span class="recipientList-detail">- BENCHMARK human resources</span>
|
||||
</div>
|
||||
|
||||
<li class="recipientList-entry u-clickable horizontal center">
|
||||
<div class="recipientList-iconSpacer centered">
|
||||
<div class="white user icon"></div>
|
||||
</div>
|
||||
<div class="recipientList-name flex u-textTruncat ">
|
||||
Mario Steibel
|
||||
<span class="recipientList-detail">- BENCHMARK human resources</span>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Headlines</h2>
|
||||
|
|
|
@ -3,6 +3,70 @@
|
|||
class SearchController < ApplicationController
|
||||
before_filter :authentication_check
|
||||
|
||||
# GET /api/v1/search_user_org
|
||||
def search_user_org
|
||||
|
||||
# enable search only for agents and admins
|
||||
if !current_user.is_role('Agent') && !current_user.is_role('Admin')
|
||||
response_access_deny
|
||||
return true
|
||||
end
|
||||
|
||||
# get params
|
||||
query = params[:query]
|
||||
limit = params[:limit] || 10
|
||||
|
||||
# try search index backend
|
||||
assets = {}
|
||||
result = []
|
||||
if SearchIndexBackend.enabled?
|
||||
items = SearchIndexBackend.search( query, limit, ['User', 'Organization'] )
|
||||
items.each { |item|
|
||||
require item[:type].to_filename
|
||||
record = Kernel.const_get( item[:type] ).find( item[:id] )
|
||||
assets = record.assets(assets)
|
||||
result.push item
|
||||
}
|
||||
else
|
||||
# do query
|
||||
users = User.search(
|
||||
:query => query,
|
||||
:limit => limit,
|
||||
:current_user => current_user,
|
||||
)
|
||||
user_result = []
|
||||
users.each do |user|
|
||||
item = {
|
||||
:id => user.id,
|
||||
:type => user.class.to_s
|
||||
}
|
||||
result.push item
|
||||
assets = user.assets(assets)
|
||||
end
|
||||
|
||||
organizations = Organization.search(
|
||||
:query => query,
|
||||
:limit => limit,
|
||||
:current_user => current_user,
|
||||
)
|
||||
|
||||
organization_result = []
|
||||
organizations.each do |organization|
|
||||
item = {
|
||||
:id => organization.id,
|
||||
:type => organization.class.to_s
|
||||
}
|
||||
result.push item
|
||||
assets = organization.assets(assets)
|
||||
end
|
||||
end
|
||||
|
||||
render :json => {
|
||||
:assets => assets,
|
||||
:result => result,
|
||||
}
|
||||
end
|
||||
|
||||
# GET /api/v1/search
|
||||
def search
|
||||
|
||||
|
|
|
@ -30,10 +30,10 @@ returns
|
|||
|
||||
# try search index backend
|
||||
if SearchIndexBackend.enabled?
|
||||
ids = SearchIndexBackend.search( query, limit, 'Organization' )
|
||||
items = SearchIndexBackend.search( query, limit, 'Organization' )
|
||||
organizations = []
|
||||
ids.each { |id|
|
||||
organizations.push Organization.lookup( :id => id )
|
||||
items.each { |item|
|
||||
organizations.push Organization.lookup( :id => item[:id] )
|
||||
}
|
||||
return organizations
|
||||
end
|
||||
|
|
|
@ -78,13 +78,17 @@ returns
|
|||
query_extention['bool']['must'].push access_condition
|
||||
end
|
||||
|
||||
ids = SearchIndexBackend.search( query, limit, 'Ticket', query_extention )
|
||||
items = SearchIndexBackend.search( query, limit, 'Ticket', query_extention )
|
||||
if !full
|
||||
ids = []
|
||||
items.each {|item|
|
||||
ids.push item[:id]
|
||||
}
|
||||
return ids
|
||||
end
|
||||
tickets = []
|
||||
ids.each { |id|
|
||||
tickets.push Ticket.lookup( :id => id )
|
||||
items.each { |item|
|
||||
tickets.push Ticket.lookup( :id => item[:id] )
|
||||
}
|
||||
return tickets
|
||||
end
|
||||
|
|
|
@ -4,22 +4,6 @@ module User::Search
|
|||
|
||||
=begin
|
||||
|
||||
search tickets
|
||||
|
||||
result = Ticket.search(
|
||||
:current_user => User.find(123),
|
||||
:query => 'search something',
|
||||
:limit => 15,
|
||||
)
|
||||
|
||||
returns
|
||||
|
||||
result = [ticket_model1, ticket_model2]
|
||||
|
||||
=end
|
||||
|
||||
=begin
|
||||
|
||||
search user
|
||||
|
||||
result = User.search(
|
||||
|
@ -46,10 +30,10 @@ returns
|
|||
|
||||
# try search index backend
|
||||
if SearchIndexBackend.enabled?
|
||||
ids = SearchIndexBackend.search( query, limit, 'User' )
|
||||
items = SearchIndexBackend.search( query, limit, 'User' )
|
||||
users = []
|
||||
ids.each { |id|
|
||||
users.push User.lookup( :id => id )
|
||||
items.each { |item|
|
||||
users.push User.lookup( :id => item[:id] )
|
||||
}
|
||||
return users
|
||||
end
|
||||
|
|
|
@ -2,6 +2,9 @@ Zammad::Application.routes.draw do
|
|||
api_path = Rails.configuration.api_path
|
||||
|
||||
# search
|
||||
match api_path + '/search', :to => 'search#search', :via => [:get, :post]
|
||||
match api_path + '/search', :to => 'search#search', :via => [:get, :post]
|
||||
|
||||
# search_user_org
|
||||
match api_path + '/search_user_org', :to => 'search#search_user_org', :via => [:get, :post]
|
||||
|
||||
end
|
38
db/migrate/20140919000001_update_object_manager2.rb
Normal file
38
db/migrate/20140919000001_update_object_manager2.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
class UpdateObjectManager2 < ActiveRecord::Migration
|
||||
def up
|
||||
|
||||
ObjectManager::Attribute.add(
|
||||
:object => 'Ticket',
|
||||
:name => 'customer_id',
|
||||
:display => 'Customer',
|
||||
:data_type => 'user_autocompletion',
|
||||
:data_option => {
|
||||
:autocapitalize => false,
|
||||
:multiple => false,
|
||||
:null => false,
|
||||
:limit => 200,
|
||||
:placeholder => 'Enter Person or Organisation/Company',
|
||||
:minLengt => 2,
|
||||
:translate => false,
|
||||
},
|
||||
:editable => false,
|
||||
:active => true,
|
||||
:screens => {
|
||||
:create_top => {
|
||||
:Agent => {
|
||||
:null => false,
|
||||
},
|
||||
},
|
||||
:edit => {},
|
||||
},
|
||||
:pending_migration => false,
|
||||
:position => 10,
|
||||
:created_by_id => 1,
|
||||
:updated_by_id => 1,
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
def down
|
||||
end
|
||||
end
|
|
@ -113,8 +113,25 @@ remove whole data from index
|
|||
|
||||
return search result
|
||||
|
||||
result = SearchIndexBackend.search( 'search query', limit, ['User', 'Organization'] )
|
||||
|
||||
result = SearchIndexBackend.search( 'search query', limit, 'User' )
|
||||
|
||||
result = [
|
||||
{
|
||||
:id => 123,
|
||||
:type => 'User',
|
||||
},
|
||||
{
|
||||
:id => 125,
|
||||
:type => 'User',
|
||||
},
|
||||
{
|
||||
:id => 15,
|
||||
:type => 'Organization',
|
||||
}
|
||||
]
|
||||
|
||||
=end
|
||||
|
||||
def self.search( query, limit = 10, index = nil, query_extention = {} )
|
||||
|
@ -123,7 +140,11 @@ return search result
|
|||
url = build_url()
|
||||
return if !url
|
||||
if index
|
||||
url += "/#{index}/_search"
|
||||
if index.class == Array
|
||||
url += "/#{index.join(',')}/_search"
|
||||
else
|
||||
url += "/#{index}/_search"
|
||||
end
|
||||
else
|
||||
url += '/_search'
|
||||
end
|
||||
|
@ -176,7 +197,11 @@ return search result
|
|||
return ids if !data['hits']['hits']
|
||||
data['hits']['hits'].each { |item|
|
||||
puts "... #{item['_type'].to_s} #{item['_id'].to_s}"
|
||||
ids.push item['_id']
|
||||
data = {
|
||||
:id => item['_id'],
|
||||
:type => item['_type'],
|
||||
}
|
||||
ids.push data
|
||||
}
|
||||
ids
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue