diff --git a/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee b/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
index 3696718ac..a0e836fd1 100644
--- a/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
+++ b/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
@@ -227,284 +227,6 @@ class App.ControllerForm extends App.Controller
if App.UiElement[attribute.tag]
item = App.UiElement[attribute.tag].render(attribute, @params, @)
- # working_hour
- else if attribute.tag is 'time_before_last'
- if !attribute.value
- attribute.value = {}
- item = $( App.view('generic/time_before_last')( attribute: attribute ) )
- item.find( "[name=\"#{attribute.name}::direction\"]").find("option[value=\"#{attribute.value.direction}\"]").attr( 'selected', 'selected' )
- item.find( "[name=\"#{attribute.name}::count\"]").find("option[value=\"#{attribute.value.count}\"]").attr( 'selected', 'selected' )
- item.find( "[name=\"#{attribute.name}::area\"]").find("option[value=\"#{attribute.value.area}\"]").attr( 'selected', 'selected' )
-
- # ticket attribute set
- else if attribute.tag is 'ticket_attribute_set'
-
- # list of possible attributes
- item = $(
- App.view('generic/ticket_attribute_manage')(
- attribute: attribute
- )
- )
-
- addShownAttribute = ( key, value ) =>
- parts = key.split(/::/)
- key = parts[0]
- type = parts[1]
- if key is 'tickets.title'
- attribute_config = {
- name: attribute.name + '::tickets.title'
- display: 'Title'
- tag: 'input'
- type: 'text'
- null: false
- value: value
- remove: true
- }
- else if key is 'tickets.group_id'
- attribute_config = {
- name: attribute.name + '::tickets.group_id'
- display: 'Group'
- tag: 'select'
- multiple: false
- null: false
- nulloption: false
- relation: 'Group'
- value: value
- remove: true
- }
- else if key is 'tickets.owner_id' || key is 'tickets.customer_id'
- display = 'Owner'
- name = 'owner_id'
- if key is 'customer_id'
- display = 'Customer'
- name = 'customer_id'
- attribute_config = {
- name: attribute.name + '::tickets.' + name
- display: display
- tag: 'select'
- multiple: false
- null: false
- nulloption: false
- relation: 'User'
- value: value || null
- remove: true
- filter: ( all, type ) ->
- return all if type isnt 'collection'
- all = _.filter( all, (item) ->
- return if item.id is 1
- return item
- )
- all.unshift( {
- id: ''
- name: '--'
- } )
- all.unshift( {
- id: 1
- name: '*** not set ***'
- } )
- all.unshift( {
- id: 'current_user.id'
- name: '*** current user ***'
- } )
- all
- }
- else if key is 'tickets.organization_id'
- attribute_config = {
- name: attribute.name + '::tickets.organization_id'
- display: 'Organization'
- tag: 'select'
- multiple: false
- null: false
- nulloption: false
- relation: 'Organization'
- value: value || null
- remove: true
- filter: ( all, type ) ->
- return all if type isnt 'collection'
- all.unshift( {
- id: ''
- name: '--'
- } )
- all.unshift( {
- id: 'current_user.organization_id'
- name: '*** organization of current user ***'
- } )
- all
- }
- else if key is 'tickets.state_id'
- attribute_config = {
- name: attribute.name + '::tickets.state_id'
- display: 'State'
- tag: 'select'
- multiple: false
- null: false
- nulloption: false
- relation: 'TicketState'
- value: value
- translate: true
- remove: true
- }
- else if key is 'tickets.priority_id'
- attribute_config = {
- name: attribute.name + '::tickets.priority_id'
- display: 'Priority'
- tag: 'select'
- multiple: false
- null: false
- nulloption: false
- relation: 'TicketPriority'
- value: value
- translate: true
- remove: true
- }
- else
- attribute_config = {
- name: attribute.name + '::' + key
- display: 'FIXME!'
- tag: 'input'
- type: 'text'
- value: value
- remove: true
- }
- item.find('select[name=ticket_attribute_list] option[value="' + key + '"]').hide().prop('disabled', true)
-
- itemSub = @formGenItem( attribute_config )
- itemSub.find('.glyphicon-minus').bind('click', (e) ->
- e.preventDefault()
- value = $(e.target).closest('.controls').find('[name]').attr('name')
- if value
- value = value.replace("#{attribute.name}::", '')
- $(e.target).closest('.sub_attribute').find('select[name=ticket_attribute_list] option[value="' + value + '"]').show().prop('disabled', false)
- $(@).parent().parent().parent().remove()
- )
-# itemSub.append('')
- item.find('.ticket_attribute_item').append( itemSub )
-
- # list of existing attributes
- attribute_config = {
- name: 'ticket_attribute_list'
- display: 'Add Attribute'
- tag: 'select'
- multiple: false
- null: false
-# nulloption: true
- options: [
- {
- value: ''
- name: '-- Ticket --'
- selected: false
- disable: true
- },
- {
- value: 'tickets.title'
- name: 'Title'
- selected: false
- disable: false
- },
- {
- value: 'tickets.group_id'
- name: 'Group'
- selected: false
- disable: false
- },
- {
- value: 'tickets.state_id'
- name: 'State'
- selected: false
- disable: false
- },
- {
- value: 'tickets.priority_id'
- name: 'Priority'
- selected: true
- disable: false
- },
- {
- value: 'tickets.owner_id'
- name: 'Owner'
- selected: true
- disable: false
- },
-# # {
-# value: 'tag'
-# name: 'Tag'
-# selected: true
-# disable: false
-# },
-# {
-# value: '-a'
-# name: '-- ' + App.i18n.translateInline('Article') + ' --'
-# selected: false
-# disable: true
-# },
-# {
-# value: 'ticket_articles.from'
-# name: 'From'
-# selected: true
-# disable: false
-# },
-# {
-# value: 'ticket_articles.to'
-# name: 'To'
-# selected: true
-# disable: false
-# },
-# {
-# value: 'ticket_articles.cc'
-# name: 'Cc'
-# selected: true
-# disable: false
-# },
-# {
-# value: 'ticket_articles.subject'
-# name: 'Subject'
-# selected: true
-# disable: false
-# },
-# {
-# value: 'ticket_articles.body'
-# name: 'Text'
-# selected: true
-# disable: false
-# },
- {
- value: '-c'
- name: '-- ' + App.i18n.translateInline('Customer') + ' --'
- selected: false
- disable: true
- },
- {
- value: 'customers.id'
- name: 'Customer'
- selected: true
- disable: false
- },
- {
- value: 'organization.id'
- name: 'Organization'
- selected: true
- disable: false
- },
- ]
- default: ''
- translate: true
- class: 'medium'
- add: true
- }
- list = @formGenItem( attribute_config )
- list.find('.glyphicon-plus').bind('click', (e) ->
- e.preventDefault()
- value = $(e.target).closest('.controls').find('[name=ticket_attribute_list]').val()
- addShownAttribute( value, '' )
- )
- item.find('.ticket_attribute_list').prepend( list )
-
- # list of shown attributes
- show = []
- if attribute.value
- for key, value of attribute.value
- addShownAttribute( key, value )
-
# ticket attribute selection
else if attribute.tag is 'ticket_attribute_selection'
@@ -948,92 +670,8 @@ class App.ControllerForm extends App.Controller
for key, value of attribute.value
addShownAttribute( key, value )
- # timeplan
- else if attribute.tag is 'timeplan'
- item = $( App.view('generic/timeplan')( attribute: attribute ) )
- attribute_config = {
- name: "#{attribute.name}::days"
- tag: 'select'
- multiple: true
- null: false
- options: [
- {
- value: 'mon'
- name: 'Monday'
- selected: false
- disable: false
- },
- {
- value: 'tue'
- name: 'Tuesday'
- selected: false
- disable: false
- },
- {
- value: 'wed'
- name: 'Wednesday'
- selected: false
- disable: false
- },
- {
- value: 'thu'
- name: 'Thursday'
- selected: false
- disable: false
- },
- {
- value: 'fri'
- name: 'Friday'
- selected: false
- disable: false
- },
- {
- value: 'sat'
- name: 'Saturday'
- selected: false
- disable: false
- },
- {
- value: 'sun'
- name: 'Sunday'
- selected: false
- disable: false
- },
- ]
- default: attribute.default?.days
- }
- item.find('.days').append( @formGenItem( attribute_config ) )
-
- hours = {}
- for hour in [0..23]
- localHour = "0#{hour}"
- hours[hour] = localHour.substr(localHour.length-2,2)
- attribute_config = {
- name: "#{attribute.name}::hours"
- tag: 'select'
- multiple: true
- null: false
- options: hours
- default: attribute.default?.hours
- }
- item.find('.hours').append( @formGenItem( attribute_config ) )
-
- minutes = {}
- for minute in [0..5]
- minutes["#{minute}0"] = "#{minute}0"
- attribute_config = {
- name: "#{attribute.name}::minutes"
- tag: 'select'
- multiple: true
- null: false
- options: minutes
- default: attribute.default?.miuntes
- }
- item.find('.minutes').append( @formGenItem( attribute_config ) )
-
- # input
else
- item = $( App.view('generic/input')( attribute: attribute ) )
+ throw "Invalid UiElement.#{attribute.tag}"
if @handlers
item.bind('change', (e) =>
@@ -1193,9 +831,9 @@ class App.ControllerForm extends App.Controller
continue
# collect all params, push it to an array if already exists
- if param[key.name]
+ if param[key.name] isnt undefined
if typeof param[key.name] is 'string'
- param[key.name] = [ param[key.name], key.value]
+ param[key.name] = [param[key.name], key.value]
else
param[key.name].push key.value
else
@@ -1293,17 +931,15 @@ class App.ControllerForm extends App.Controller
inputSelectObject = {}
for key of param
parts = key.split '::'
- if parts[0] && parts[1] && !parts[2]
- if !inputSelectObject[ parts[0] ]
+ if parts[0] && parts[1]
+ if !(parts[0] of inputSelectObject)
inputSelectObject[ parts[0] ] = {}
- inputSelectObject[ parts[0] ][ parts[1] ] = param[ key ]
- delete param[ key ]
- if parts[0] && parts[1] && parts[2]
- if !inputSelectObject[ parts[0] ]
- inputSelectObject[ parts[0] ] = {}
- if !inputSelectObject[ parts[0] ][ parts[1] ]
- inputSelectObject[ parts[0] ][ parts[1] ] = {}
- inputSelectObject[ parts[0] ][ parts[1] ][ parts[2] ] = param[ key ]
+ if !parts[2]
+ inputSelectObject[ parts[0] ][ parts[1] ] = param[ key ]
+ else
+ if !(parts[1] of inputSelectObject[ parts[0] ])
+ inputSelectObject[ parts[0] ][ parts[1] ] = {}
+ inputSelectObject[ parts[0] ][ parts[1] ][ parts[2] ] = param[ key ]
delete param[ key ]
# set new object params
@@ -1311,7 +947,7 @@ class App.ControllerForm extends App.Controller
param[ key ] = inputSelectObject[ key ]
#App.Log.notice 'ControllerForm', 'formParam', form, param
- return param
+ param
@formId: ->
formId = new Date().getTime() + Math.floor( Math.random() * 99999 )
diff --git a/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee b/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee
index 7cec42683..0123a8ecd 100644
--- a/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee
+++ b/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee
@@ -196,6 +196,7 @@ class App.ControllerGenericIndex extends App.Controller
pageData: @pageData
genericObject: @genericObject
container: @container
+ large: @large
)
new: (e) ->
@@ -204,6 +205,7 @@ class App.ControllerGenericIndex extends App.Controller
pageData: @pageData
genericObject: @genericObject
container: @container
+ large: @large
)
description: (e) =>
diff --git a/app/assets/javascripts/app/controllers/_ui_element/autocompletion_ajax.js.coffee b/app/assets/javascripts/app/controllers/_ui_element/autocompletion_ajax.js.coffee
new file mode 100644
index 000000000..c08ad6ab5
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/_ui_element/autocompletion_ajax.js.coffee
@@ -0,0 +1,18 @@
+class App.UiElement.autocompletion_ajax
+ @render: (attribute, params = {}) ->
+ if params[attribute.name]
+ object = App[attribute.relation].find(params[attribute.name])
+ valueName = object.displayName()
+
+ # selectable search
+ searchableAjaxSelectObject = new App.SearchableAjaxSelect(
+ attribute:
+ value: params[attribute.name]
+ valueName: valueName
+ name: attribute.name
+ id: params.organization_id
+ placeholder: App.i18n.translateInline('Search...')
+ limt: 10
+ object: attribute.relation
+ )
+ searchableAjaxSelectObject.element()
diff --git a/app/assets/javascripts/app/controllers/_ui_element/input.js.coffee b/app/assets/javascripts/app/controllers/_ui_element/input.js.coffee
new file mode 100644
index 000000000..d34638671
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/_ui_element/input.js.coffee
@@ -0,0 +1,3 @@
+class App.UiElement.input
+ @render: (attribute) ->
+ $( App.view('generic/input')( attribute: attribute ) )
diff --git a/app/assets/javascripts/app/controllers/_ui_element/select.js.coffee b/app/assets/javascripts/app/controllers/_ui_element/select.js.coffee
index a290eb92a..66fc12e44 100644
--- a/app/assets/javascripts/app/controllers/_ui_element/select.js.coffee
+++ b/app/assets/javascripts/app/controllers/_ui_element/select.js.coffee
@@ -1,5 +1,5 @@
class App.UiElement.select extends App.UiElement.ApplicationUiElement
- @render: (attribute, params, form_controller) ->
+ @render: (attribute, params) ->
# set multiple option
if attribute.multiple
diff --git a/app/assets/javascripts/app/controllers/_ui_element/sla_times.js.coffee b/app/assets/javascripts/app/controllers/_ui_element/sla_times.js.coffee
index 76b18f04d..4ebe5febf 100644
--- a/app/assets/javascripts/app/controllers/_ui_element/sla_times.js.coffee
+++ b/app/assets/javascripts/app/controllers/_ui_element/sla_times.js.coffee
@@ -1,4 +1,46 @@
class App.UiElement.sla_times
- @render: (attribute) ->
+ @render: (attribute, params = {}) ->
- $( App.view('generic/sla_times')( attribute: attribute ) )
+ item = $( App.view('generic/sla_times')(
+ attribute: attribute
+ first_response_time: params.first_response_time
+ update_time: params.update_time
+ close_time: params.close_time
+ first_response_time_in_text: @toText(params.first_response_time)
+ update_time_in_text: @toText(params.update_time)
+ close_time_in_text: @toText(params.close_time)
+ ) )
+
+ item.find('.js-timeConvertFrom').bind('keyup', (e) =>
+ inText = $(e.target).val()
+ inMinutes = @toMinutes(inText)
+ if !inMinutes
+ $(e.target).addClass('has-error')
+ else
+ $(e.target).removeClass('has-error')
+ dest = $(e.target).closest('td').find('.js-timeConvertTo')
+ dest.val(inMinutes)
+ )
+
+ item
+
+ @toMinutes: (hh) ->
+ hh = hh.split(':')
+ hour = parseInt(hh[0])
+ minute = parseInt(hh[1])
+ return if hour is NaN
+ return if minute is NaN
+ (hour * 60) + minute
+
+ @toText: (m) ->
+ m = parseInt(m)
+ return if !m
+ minutes = m % 60
+ hours = Math.floor(m / 60)
+
+ if minutes < 10
+ minutes = "0#{minutes}"
+ if hours < 10
+ hours = "0#{hours}"
+
+ "#{hours}:#{minutes}"
diff --git a/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.js.coffee b/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.js.coffee
new file mode 100644
index 000000000..378333ff9
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/_ui_element/ticket_selector.js.coffee
@@ -0,0 +1,267 @@
+class App.UiElement.ticket_selector extends App.UiElement.ApplicationUiElement
+ @render: (attribute, params = {}) ->
+
+ # list of attributes
+ groups =
+ tickets:
+ name: 'Ticket'
+ model: 'Ticket'
+ users:
+ name: 'Customer'
+ model: 'User'
+ organizations:
+ name: 'Organization'
+ model: 'Organization'
+
+ elements =
+ tickets:
+ title:
+ tag: 'input'
+ operator: ['contains', 'contains not']
+ number:
+ tag: 'input'
+ operator: ['contains', 'contains not']
+ group_id:
+ relation: 'Group'
+ tag: 'select'
+ multible: true
+ operator: ['is', 'is not']
+ priority_id:
+ relation: 'Priority'
+ tag: 'select'
+ multible: true
+ operator: ['is', 'is not']
+ state_id:
+ relation: 'State'
+ tag: 'select'
+ multible: true
+ operator: ['is', 'is not']
+ owner_id:
+ tag: 'user_selection'
+ relation: 'User'
+ operator: ['is', 'is not']
+ customer_id:
+ tag: 'user_selection'
+ relation: 'User'
+ operator: ['is', 'is not']
+ organization_id:
+ tag: ''
+ relation: 'Organization'
+ operator: ['is', 'is not']
+ tag:
+ tag: 'tag'
+ multible: true
+ operator: ['is', 'is not']
+ created_at:
+ tag: 'timestamp'
+ operator: ['before', 'after']
+ updated_at:
+ tag: 'timestamp'
+ operator: ['before', 'after']
+ escalation_time:
+ tag: 'timestamp'
+ operator: ['before', 'after']
+ users:
+ firstname:
+ tag: 'input'
+ operator: ['contains', 'contains not']
+ lastname:
+ tag: 'input'
+ operator: ['contains', 'contains not']
+ email:
+ tag: 'input'
+ operator: ['contains', 'contains not']
+ login:
+ tag: 'input'
+ operator: ['contains', 'contains not']
+ created_at:
+ tag: 'time_selector_enhanced'
+ operator: ['before', 'after']
+ updated_at:
+ tag: 'time_selector_enhanced'
+ operator: ['before', 'after']
+ organizations:
+ name:
+ tag: 'input'
+ operator: ['contains', 'contains not']
+ shared:
+ tag: 'boolean'
+ operator: ['is', 'is not']
+ created_at:
+ tag: 'time_selector_enhanced'
+ operator: ['before', 'after']
+ updated_at:
+ tag: 'time_selector_enhanced'
+ operator: ['before', 'after']
+
+ # megre config
+ for groupKey, groupMeta of groups
+ for elementKey, elementGroup of elements
+ if elementKey is groupKey
+ configure_attributes = App[groupMeta.model].configure_attributes
+ for attributeName, attributeConfig of elementGroup
+ for attribute in configure_attributes
+ if attribute.name is attributeName
+ attributeConfig.config = attribute
+
+ selector = @buildAttributeSelector(groups, elements)
+
+ # return item
+ item = $( App.view('generic/ticket_selector')( attribute: attribute ) )
+ item.find('.js-attributeSelector').prepend(selector)
+
+ # add filter
+ item.find('.js-add').bind('click', (e) =>
+ element = $(e.target).closest('.js-filterElement')
+ elementClone = element.clone(true)
+ element.after(elementClone)
+ elementClone.find('.js-attributeSelector select').trigger('change')
+ )
+
+ # remove filter
+ item.find('.js-remove').bind('click', (e) =>
+ $(e.target).closest('.js-filterElement').remove()
+ @rebuildAttributeSelectors(item)
+ )
+
+ # change filter
+ item.find('.js-attributeSelector select').bind('change', (e) =>
+ groupAndAttribute = $(e.target).find('option:selected').attr('value')
+ elementRow = $(e.target).closest('.js-filterElement')
+
+ console.log('CHANGE', groupAndAttribute, $(e.target))
+
+ @rebuildAttributeSelectors(item, elementRow, groupAndAttribute)
+ @rebuildOperater(item, elementRow, groupAndAttribute, elements)
+ @buildValue(item, elementRow, groupAndAttribute, elements)
+ )
+
+ # build inital params
+ console.log('P', params)
+ if !_.isEmpty(params.condition)
+ selectorExists = false
+ for position of params.condition.attribute
+
+ # get stored params
+ groupAndAttribute = params.condition.attribute[position]
+ if params.condition[groupAndAttribute]
+ selectorExists = true
+ operator = params.condition[groupAndAttribute].operator
+ value = params.condition[groupAndAttribute].value
+
+ # get selector rows
+ elementFirst = item.find('.js-filterElement').first()
+ elementLast = item.find('.js-filterElement').last()
+
+ # clone, rebuild and append
+ elementClone = elementFirst.clone(true)
+ @rebuildAttributeSelectors(item, elementClone, groupAndAttribute)
+ @rebuildOperater(item, elementClone, groupAndAttribute, elements, operator)
+ @buildValue(item, elementClone, groupAndAttribute, elements, value)
+ elementLast.after(elementClone)
+
+ # remove first dummy row
+ if selectorExists
+ item.find('.js-filterElement').first().remove()
+ item
+
+ @getElementConfig: (groupAndAttribute, elements) ->
+ for elementGroup, elementConfig of elements
+ for elementKey, elementItem of elementConfig
+ if "#{elementGroup}.#{elementKey}" is groupAndAttribute
+ return elementItem
+ false
+
+ @buildValue: (elementFull, elementRow, groupAndAttribute, elements, value) ->
+
+ # do nothing if item already exists
+ name = "condition::#{groupAndAttribute}::value"
+ return if elementRow.find("[name=\"#{name}\"]").get(0)
+
+ # build new item
+ attributeConfig = @getElementConfig(groupAndAttribute, elements)
+ item = ''
+ if attributeConfig && attributeConfig.config && App.UiElement[attributeConfig.config.tag]
+ config = _.clone(attributeConfig.config)
+ config['name'] = name
+ config['value'] = value
+ if 'multiple' of config
+ config.multiple = true
+ config.nulloption = false
+ item = App.UiElement[attributeConfig.config.tag].render(config, {})
+ elementRow.find('.js-value').html(item)
+
+ @buildAttributeSelector: (groups, elements) ->
+ selection = $('')
+ for groupKey, groupMeta of groups
+ displayName = App.i18n.translateInline(groupMeta.name)
+ selection.closest('select').append("")
+ optgroup = selection.find("optgroup.js-#{groupKey}")
+ for elementKey, elementGroup of elements
+ if elementKey is groupKey
+ for attributeName, attributeConfig of elementGroup
+ if attributeConfig.config && attributeConfig.config.display
+ displayName = App.i18n.translateInline(attributeConfig.config.display)
+ else
+ displayName = App.i18n.translateInline(attributeName)
+ optgroup.append("")
+ selection
+
+ @rebuildAttributeSelectors: (elementFull, elementRow, groupAndAttribute) ->
+
+ # enable all
+ elementFull.find('.js-attributeSelector select option').removeAttr('disabled')
+
+ # disable all used attributes
+ elementFull.find('.js-attributeSelector select').each(->
+ keyLocal = $(@).val()
+ elementFull.find('.js-attributeSelector select option[value="' + keyLocal + '"]').attr('disabled', true)
+ elementFull.find('.js-hiddenAttribute').val(keyLocal)
+ )
+
+ # disable - if we only have one attribute
+ if elementFull.find('.js-attributeSelector select').length > 1
+ elementFull.find('.js-remove').removeClass('is-disabled')
+ else
+ elementFull.find('.js-remove').addClass('is-disabled')
+
+ # set attribute
+ if groupAndAttribute
+ elementRow.find('.js-attributeSelector select').val(groupAndAttribute)
+ elementRow.find('[name="condition::attribute"]').val("#{groupAndAttribute}")
+
+ @buildOperator: (elementFull, elementRow, groupAndAttribute, elements, current_operator) ->
+ selection = $("")
+ attributeConfig = @getElementConfig(groupAndAttribute, elements)
+ for operator in attributeConfig.operator
+ operatorName = App.i18n.translateInline(operator)
+ selected = ''
+ if current_operator is operator
+ selected = 'selected="selected"'
+ selection.append("")
+ selection
+
+ @rebuildOperater: (elementFull, elementRow, groupAndAttribute, elements, current_operator) ->
+ return if !groupAndAttribute
+
+ # do nothing if item already exists
+ name = "condition::#{groupAndAttribute}::operator"
+ return if elementRow.find("[name=\"#{name}\"]").get(0)
+
+ # render new operator
+ operator = @buildOperator(elementFull, elementRow, groupAndAttribute, elements, current_operator)
+ elementRow.find('.js-operator select').replaceWith(operator)
+
+ @humanText: (condition) ->
+ return [] if _.isEmpty(condition)
+ rules = []
+ for position of condition.attribute
+
+ # get stored params
+ groupAndAttribute = condition.attribute[position]
+ if condition[groupAndAttribute]
+ selectorExists = true
+ operator = condition[groupAndAttribute].operator
+ value = condition[groupAndAttribute].value
+ rules.push "Where #{groupAndAttribute} #{operator} #{value}."
+ rules
\ No newline at end of file
diff --git a/app/assets/javascripts/app/controllers/sla.js.coffee b/app/assets/javascripts/app/controllers/sla.js.coffee
index ca0ea6ba5..663c4f5fe 100644
--- a/app/assets/javascripts/app/controllers/sla.js.coffee
+++ b/app/assets/javascripts/app/controllers/sla.js.coffee
@@ -1,27 +1,119 @@
class Index extends App.ControllerContent
+ events:
+ 'click .js-new': 'new'
+ 'click .js-edit': 'edit'
+ 'click .js-delete': 'delete'
+ 'click .js-description': 'description'
+
constructor: ->
super
# check authentication
return if !@authenticate()
- new App.ControllerGenericIndex(
- el: @el
- id: @id
- genericObject: 'Sla'
- pageData:
- title: 'SLA'
- home: 'slas'
- object: 'SLA'
- objects: 'SLAs'
- navupdate: '#slas'
- notes: [
-# 'SLA are ...'
- ]
- buttons: [
- { name: 'New SLA', 'data-type': 'new', class: 'btn--success' }
- ]
- container: @el.closest('.content')
+ @load()
+ #@subscribeId = App.Calendar.subscribe(@render)
+
+ load: =>
+ @ajax(
+ id: 'sla_index'
+ type: 'GET'
+ url: @apiPath + '/slas'
+ processData: true
+ success: (data, status, xhr) =>
+
+ # load assets
+ App.Collection.loadAssets(data.assets)
+
+ @render(data)
)
+ render: =>
+ slas = App.Sla.search(
+ sortBy: 'name'
+ )
+ for sla in slas
+ if sla.first_response_time
+ sla.first_response_time_in_text = @toText(sla.first_response_time)
+ if sla.update_time
+ sla.update_time_in_text = @toText(sla.update_time)
+ if sla.solution_time
+ sla.solution_time_in_text = @toText(sla.solution_time)
+ sla.rules = App.UiElement.ticket_selector.humanText(sla.condition)
+
+ # show description button, only if content exists
+ showDescription = false
+ if App.Sla.description
+ if !_.isEmpty(slas)
+ showDescription = true
+ else
+ description = marked(App.Sla.description)
+
+ @html App.view('sla/index')(
+ slas: slas
+ showDescription: showDescription
+ description: description
+ )
+
+ release: =>
+ if @subscribeId
+ App.Calendar.unsubscribe(@subscribeId)
+
+ new: (e) ->
+ e.preventDefault()
+ new App.ControllerGenericNew(
+ pageData:
+ title: 'SLAs'
+ object: 'Sla'
+ objects: 'SLAs'
+ genericObject: 'Sla'
+ container: @el.closest('.content')
+ callback: @load
+ large: true
+ )
+
+ edit: (e) ->
+ e.preventDefault()
+ id = $(e.target).closest('.action').data('id')
+ new App.ControllerGenericEdit(
+ id: id
+ pageData:
+ title: 'SLAs'
+ object: 'Sla'
+ objects: 'SLAs'
+ genericObject: 'Sla'
+ callback: @load
+ container: @el.closest('.content')
+ large: true
+ )
+
+ delete: (e) =>
+ e.preventDefault()
+ id = $(e.target).closest('.action').data('id')
+ item = App.Sla.find(id)
+ new App.ControllerGenericDestroyConfirm(
+ item: item
+ container: @el.closest('.content')
+ callback: @load
+ )
+
+ description: (e) =>
+ new App.ControllerGenericDescription(
+ description: App.Calendar.description
+ container: @el.closest('.content')
+ )
+
+ toText: (m) ->
+ m = parseInt(m)
+ return if !m
+ minutes = m % 60
+ hours = Math.floor(m / 60)
+
+ if minutes < 10
+ minutes = "0#{minutes}"
+ if hours < 10
+ hours = "0#{hours}"
+
+ "#{hours}:#{minutes}"
+
App.Config.set( 'Sla', { prio: 2900, name: 'SLAs', parent: '#manage', target: '#manage/slas', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )
\ No newline at end of file
diff --git a/app/assets/javascripts/app/controllers/users.js.coffee b/app/assets/javascripts/app/controllers/users.js.coffee
index e17ba7a40..62ab5be40 100644
--- a/app/assets/javascripts/app/controllers/users.js.coffee
+++ b/app/assets/javascripts/app/controllers/users.js.coffee
@@ -1,8 +1,8 @@
class Index extends App.Controller
elements:
- '.js-search' : 'searchInput'
+ '.js-search': 'searchInput'
events:
- 'click [data-type="new"]': 'new'
+ 'click [data-type=new]': 'new'
constructor: ->
super
diff --git a/app/assets/javascripts/app/models/organization.js.coffee b/app/assets/javascripts/app/models/organization.js.coffee
index 910a6cafb..4d3d2a839 100644
--- a/app/assets/javascripts/app/models/organization.js.coffee
+++ b/app/assets/javascripts/app/models/organization.js.coffee
@@ -6,8 +6,9 @@ class App.Organization extends App.Model
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false, info: true },
{ name: 'shared', display: 'Shared organization', tag: 'boolean', note: 'Customers in the organization can view each other items.', type: 'boolean', default: true, null: false, info: false },
{ name: 'note', display: 'Note', tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, null: true, info: true },
- { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1, info: false },
{ name: 'active', display: 'Active', tag: 'active', default: true, info: false },
+ { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1, info: false },
+ { name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1, info: false },
]
@configure_overview = [
'name',
diff --git a/app/assets/javascripts/app/models/sla.js.coffee b/app/assets/javascripts/app/models/sla.js.coffee
index e621675a7..d3d334b4a 100644
--- a/app/assets/javascripts/app/models/sla.js.coffee
+++ b/app/assets/javascripts/app/models/sla.js.coffee
@@ -3,12 +3,10 @@ class App.Sla extends App.Model
@extend Spine.Model.Ajax
@url: @apiPath + '/slas'
@configure_attributes = [
- { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
- { name: 'first_response_time', display: 'First Response Time', tag: 'input', type: 'text', limit: 100, null: true, note: 'In minutes, only business times are counted.' },
- { name: 'update_time', display: 'Update Time', tag: 'input', type: 'text', limit: 100, null: true, note: 'In minutes, only business times are counted.' },
- { name: 'close_time', display: 'Solution Time', tag: 'input', type: 'text', limit: 100, null: true, note: 'In minutes, only business times are counted.' },
- { name: 'calendar_id', display: 'Calendar', tag: 'select', relation: 'Calendar', null: false },
- { name: 'condition', display: 'Conditions where SLA is used', tag: 'ticket_attribute_selection', null: true },
+ { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
+ { name: 'condition', display: 'Selector', tag: 'ticket_selector', null: false, note: 'Create rules that single out the tickets for the Service Level Agreement.' },
+ { name: 'calendar_id', display: 'Calendar', tag: 'select', relation: 'Calendar', null: false },
+ { name: 'sla_times', display: 'SLA Times', tag: 'sla_times', null: true },
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 },
{ name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 },
{ name: 'updated_by_id', display: 'Updated by', relation: 'User', readonly: 1 },
diff --git a/app/assets/javascripts/app/models/ticket.js.coffee b/app/assets/javascripts/app/models/ticket.js.coffee
index 7b6343b96..c816cf275 100644
--- a/app/assets/javascripts/app/models/ticket.js.coffee
+++ b/app/assets/javascripts/app/models/ticket.js.coffee
@@ -3,14 +3,14 @@ class App.Ticket extends App.Model
@extend Spine.Model.Ajax
@url: @apiPath + '/tickets'
@configure_attributes = [
- { name: 'number', display: '#', tag: 'input', type: 'text', limit: 100, null: true, read_only: true, style: 'width: 60px' },
- { name: 'customer_id', display: 'Customer', tag: 'input', type: 'text', limit: 100, null: false, autocapitalize: false, relation: 'User' },
- { name: 'organization_id', display: 'Organization', relation: 'Organization', tagreadonly: 1 },
- { name: 'group_id', display: 'Group', tag: 'select', multiple: false, limit: 100, null: false, relation: 'Group', style: 'width: 10%', edit: true },
- { name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, limit: 100, null: true, relation: 'User', style: 'width: 12%', edit: true },
- { name: 'title', display: 'Title', tag: 'input', type: 'text', limit: 100, null: false, parentClass: 'noTruncate' },
- { name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, relation: 'TicketState', default: 'new', style: 'width: 12%', edit: true, customer: true, },
- { name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, relation: 'TicketPriority', default: '2 normal', style: 'width: 12%', edit: true, customer: true, },
+ { name: 'number', display: '#', tag: 'input', type: 'text', limit: 100, null: true, read_only: true, style: 'width: 60px' },
+ { name: 'customer_id', display: 'Customer', tag: 'input', type: 'text', limit: 100, null: false, autocapitalize: false, relation: 'User' },
+ { name: 'organization_id', display: 'Organization', tag: 'select', relation: 'Organization', tagreadonly: 1 },
+ { name: 'group_id', display: 'Group', tag: 'select', multiple: false, limit: 100, null: false, relation: 'Group', style: 'width: 10%', edit: true },
+ { name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, limit: 100, null: true, relation: 'User', style: 'width: 12%', edit: true },
+ { name: 'title', display: 'Title', tag: 'input', type: 'text', limit: 100, null: false, parentClass: 'noTruncate' },
+ { name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, relation: 'TicketState', default: 'new', style: 'width: 12%', edit: true, customer: true, },
+ { name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, relation: 'TicketPriority', default: '2 normal', style: 'width: 12%', edit: true, customer: true, },
{ name: 'last_contact', display: 'Last contact', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
{ name: 'last_contact_agent', display: 'Last contact (Agent)', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
{ name: 'last_contact_customer', display: 'Last contact (Customer)', tag: 'datetime', null: true, style: 'width: 12%', parentClass: 'noTruncate' },
diff --git a/app/assets/javascripts/app/models/user.js.coffee b/app/assets/javascripts/app/models/user.js.coffee
index cedbf80c4..14d0ea475 100644
--- a/app/assets/javascripts/app/models/user.js.coffee
+++ b/app/assets/javascripts/app/models/user.js.coffee
@@ -23,6 +23,7 @@ class App.User extends App.Model
{ name: 'role_ids', display: 'Roles', tag: 'checkbox', multiple: true, null: false, relation: 'Role' },
{ name: 'group_ids', display: 'Groups', tag: 'checkbox', multiple: true, null: true, relation: 'Group', invite_agent: true },
{ name: 'active', display: 'Active', tag: 'active', default: true },
+ { name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 },
{ name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 },
]
@configure_overview = [
diff --git a/app/assets/javascripts/app/views/calendar/index.jst.eco b/app/assets/javascripts/app/views/calendar/index.jst.eco
index 338994e79..a169ad80b 100644
--- a/app/assets/javascripts/app/views/calendar/index.jst.eco
+++ b/app/assets/javascripts/app/views/calendar/index.jst.eco
@@ -19,41 +19,41 @@
-
<%- @Icon('status', 'ok inline') %> <%= calendar.name %>
+ <%- @Icon('status', 'ok inline') %> <%= calendar.name %>
<%- @T('Timezone') %>: <%= calendar.timezone %>
<%- @T('Business Hours') %>:
- <%- @T('Monday') %> | <% if _.isEmpty(calendar.business_hours['mon']): %>-<% else: %><% for from, till of calendar.business_hours['mon']: %><%= from %>:<%= till %> | <% end %><% end %> |
+ <%- @T('Monday') %> | <% if _.isEmpty(calendar.business_hours['mon']): %>-<% else: %><% for from, till of calendar.business_hours['mon']: %><%= from %>-<%= till %> | <% end %><% end %> |
- <%- @T('Tuesday') %> | <% if _.isEmpty(calendar.business_hours['tue']): %>-<% else: %><% for from, till of calendar.business_hours['tue']: %><%= from %>:<%= till %> | <% end %><% end %> |
+ <%- @T('Tuesday') %> | <% if _.isEmpty(calendar.business_hours['tue']): %>-<% else: %><% for from, till of calendar.business_hours['tue']: %><%= from %>-<%= till %> | <% end %><% end %> |
- <%- @T('Wednesday') %> | <% if _.isEmpty(calendar.business_hours['wed']): %>-<% else: %><% for from, till of calendar.business_hours['wed']: %><%= from %>:<%= till %> | <% end %><% end %> |
+ <%- @T('Wednesday') %> | <% if _.isEmpty(calendar.business_hours['wed']): %>-<% else: %><% for from, till of calendar.business_hours['wed']: %><%= from %>-<%= till %> | <% end %><% end %> |
- <%- @T('Thursday') %> | <% if _.isEmpty(calendar.business_hours['thu']): %>-<% else: %><% for from, till of calendar.business_hours['thu']: %><%= from %>:<%= till %> | <% end %><% end %> |
+ <%- @T('Thursday') %> | <% if _.isEmpty(calendar.business_hours['thu']): %>-<% else: %><% for from, till of calendar.business_hours['thu']: %><%= from %>-<%= till %> | <% end %><% end %> |
- <%- @T('Friday') %> | <% if _.isEmpty(calendar.business_hours['fri']): %>-<% else: %><% for from, till of calendar.business_hours['fri']: %><%= from %>:<%= till %> | <% end %><% end %> |
+ <%- @T('Friday') %> | <% if _.isEmpty(calendar.business_hours['fri']): %>-<% else: %><% for from, till of calendar.business_hours['fri']: %><%= from %>-<%= till %> | <% end %><% end %> |
- <%- @T('Saturday') %> | <% if _.isEmpty(calendar.business_hours['sat']): %>-<% else: %><% for from, till of calendar.business_hours['sat']: %><%= from %>:<%= till %> | <% end %><% end %> |
+ <%- @T('Saturday') %> | <% if _.isEmpty(calendar.business_hours['sat']): %>-<% else: %><% for from, till of calendar.business_hours['sat']: %><%= from %>-<%= till %> | <% end %><% end %> |
- <%- @T('Sunday') %> | <% if _.isEmpty(calendar.business_hours['sun']): %>-<% else: %><% for from, till of calendar.business_hours['sun']: %><%= from %>:<%= till %> | <% end %><% end %> |
+ <%- @T('Sunday') %> | <% if _.isEmpty(calendar.business_hours['sun']): %>-<% else: %><% for from, till of calendar.business_hours['sun']: %><%= from %>-<%= till %> | <% end %><% end %> |
<%- @T('Public Holidays') %>:
- <% for holiday, meta of calendar.public_holidays_preview: %>
- class="is-inactive"<% end %>><%- @Tdate(holiday) %> | <%= meta.summary %> |
- <% end %>
+ <% for holiday, meta of calendar.public_holidays_preview: %>
+ class="is-inactive"<% end %>><%- @Tdate(holiday) %> | <%= meta.summary %> |
+ <% end %>
diff --git a/app/controllers/slas_controller.rb b/app/controllers/slas_controller.rb
index 17b1d809c..d6baddb7f 100644
--- a/app/controllers/slas_controller.rb
+++ b/app/controllers/slas_controller.rb
@@ -48,7 +48,28 @@ curl http://localhost/api/v1/slas.json -v -u #{login}:#{password}
def index
return if deny_if_not_role(Z_ROLENAME_ADMIN)
- model_index_render(Sla, params)
+
+ assets = {}
+
+ # calendars
+ calendar_ids = []
+ Calendar.all.each {|calendar|
+ calendar_ids.push calendar.id
+ assets = calendar.assets(assets)
+ }
+
+ # slas
+ sla_ids = []
+ Sla.all.each {|sla|
+ sla_ids.push sla.id
+ assets = sla.assets(assets)
+ }
+
+ render json: {
+ calendar_ids: calendar_ids,
+ sla_ids: sla_ids,
+ assets: assets,
+ }, status: :ok
end
=begin
diff --git a/test/browser/manage_test.rb b/test/browser/manage_test.rb
index 5c53a4e43..d69e11e9a 100644
--- a/test/browser/manage_test.rb
+++ b/test/browser/manage_test.rb
@@ -45,7 +45,7 @@ class ManageTest < TestCase
sla_create(
data: {
name: 'some sla' + random,
- first_response_time: 61
+ first_response_time_in_text: '1:01'
}
)
watch_for(
@@ -54,7 +54,7 @@ class ManageTest < TestCase
)
sleep 1
- click( css: '.table-overview tr:last-child td' )
+ click( css: '.content:not(.hide) .action:last-child .js-edit' )
sleep 1
set(
@@ -62,8 +62,8 @@ class ManageTest < TestCase
value: 'some sla update ' + random,
)
set(
- css: '.modal input[name="first_response_time"]',
- value: 121,
+ css: '.modal input[name="first_response_time_in_text"]',
+ value: '2:01',
)
click( css: '.modal button.js-submit' )
@@ -73,7 +73,7 @@ class ManageTest < TestCase
)
sleep 4
- click( css: '[data-type="destroy"]:last-child' )
+ click( css: '.content:not(.hide) .action:last-child .js-delete' )
sleep 2
click( css: '.modal button.js-submit' )
diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb
index 02bff8d11..3d602116f 100644
--- a/test/browser_test_helper.rb
+++ b/test/browser_test_helper.rb
@@ -1576,8 +1576,8 @@ wait untill text in selector disabppears
sla_create(
:browser => browser2,
:data => {
- :name => 'some sla' + random,
- :first_response_time => 61
+ :name => 'some sla' + random,
+ :first_response_time_in_text => 61
},
)
@@ -1592,14 +1592,14 @@ wait untill text in selector disabppears
instance.find_elements( { css: 'a[href="#manage"]' } )[0].click
instance.find_elements( { css: 'a[href="#manage/slas"]' } )[0].click
sleep 2
- instance.find_elements( { css: 'a[data-type="new"]' } )[0].click
+ instance.find_elements( { css: 'a.js-new' } )[0].click
sleep 2
element = instance.find_elements( { css: '.modal input[name=name]' } )[0]
element.clear
element.send_keys( data[:name] )
- element = instance.find_elements( { css: '.modal input[name=first_response_time]' } )[0]
+ element = instance.find_elements( { css: '.modal input[name=first_response_time_in_text]' } )[0]
element.clear
- element.send_keys( data[:first_response_time] )
+ element.send_keys( data[:first_response_time_in_text] )
instance.find_elements( { css: '.modal button.js-submit' } )[0].click
(1..8).each {
element = instance.find_elements( { css: 'body' } )[0]