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 611a00964..03c50c403 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_generic.js.coffee @@ -71,7 +71,7 @@ class App.ControllerGenericEdit extends App.ControllerModal submit: (e) -> e.preventDefault() - params = @formParam(e.target) + params = @formParam(e.target) @item.load(params) # validate @@ -169,20 +169,14 @@ class App.ControllerGenericIndex extends App.Controller objects: objects overview: overview attributes: attributes - groupBy: 'state' + bindRow: + events: + 'click': @edit ) - binds = {} - for item in attributes - if item.dataType - if !binds[item.dataType] - callback = item.callback || @edit - @el.on( 'click', "[data-type=#{item.dataType}]", callback ) - binds[item.dataType] = true - - edit: (e) => + edit: (id, e) => e.preventDefault() - item = $(e.target).item( App[ @genericObject ] ) + item = App[ @genericObject ].find(id) if @editCallback @editCallback(item) diff --git a/app/assets/javascripts/app/controllers/_application_controller_table.js.coffee b/app/assets/javascripts/app/controllers/_application_controller_table.js.coffee index daf97c260..5d27c6c2a 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_table.js.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_table.js.coffee @@ -4,48 +4,64 @@ class App.ControllerTable extends App.Controller @[key] = value @table = @tableGen(params) + if @el @el.append( @table ) ### + # table simple based on model + + rowClick = -> (id, e) + e.preventDefault() + console.log('rowClick', id) + rowMouseover = -> (id, e) + e.preventDefault() + console.log('rowMouseover', id) + rowMouseout = -> (id, e) + e.preventDefault() + console.log('rowMouseout', id) + rowDblClick = -> (id, e) + e.preventDefault() + console.log('rowDblClick', id) + + colClick = -> (id, e) + e.preventDefault() + console.log('colClick', e.target) + + checkboxClick = -> (id, e) + e.preventDefault() + console.log('checkboxClick', e.target) + new App.ControllerTable( - header: ['Host', 'User', 'Adapter', 'Active'] overview: ['host', 'user', 'adapter', 'active'] model: App.Channel objects: data + groupBy: 'group' checkbox: false radio: false + bindRow: + events: + 'click': rowClick + 'mouseover': rowMouseover + 'mouseout': rowMouseout + 'dblclick': rowDblClick + bindCol: + host: + events: + 'click': colEvent + bindCheckbox: + events: + 'click': rowClick + 'mouseover': rowMouseover + 'mouseout': rowMouseout + 'dblclick': rowDblClick ) - - new App.ControllerTable( - overview_extended: [ - { name: 'number', link: true } - { name: 'title', link: true } - { name: 'customer', class: 'user-popover', data: { id: true } } - { name: 'state', translate: true } - { name: 'priority', translate: true } - { name: 'group' }, - { name: 'owner', class: 'user-popover', data: { id: true } } - { name: 'created_at', callback: @frontendTime } - { name: 'last_contact', callback: @frontendTime } - { name: 'last_contact_agent', callback: @frontendTime } - { name: 'last_contact_customer', callback: @frontendTime } - { name: 'first_response', callback: @frontendTime } - { name: 'close_time', callback: @frontendTime } - ], - model: App.Ticket - objects: tickets - checkbox: false - radio: false - ) - ### tableGen: (data) -> overview = data.overview || data.model.configure_overview || [] attributes = data.attributes || data.model.configure_attributes || {} - header = data.header destroy = data.model.configure_delete # check if table is empty @@ -53,108 +69,132 @@ class App.ControllerTable extends App.Controller table = '

-' + App.i18n.translateContent( 'none' ) + '-

' return $(table) - # define table header - if header - header_new = [] - for key in header - header_new.push { - display: key - } - header = header_new - else if !data.overview_extended - header = [] - for row in overview - found = false - if attributes - for attribute in attributes - if row is attribute.name - found = true - header.push attribute - else - rowWithoutId = row + '_id' - if rowWithoutId is attribute.name - found = true - header.push attribute - if !found - header.push { - name: row - display: row - } + # group by + if data.groupBy - # collect data of col. types - dataTypesForCols = [] - for row in overview + # remove group by attribute from header + overview = _.filter( + overview + (item) => + return item if item isnt data.groupBy + return + ) - if !_.isEmpty(attributes) - for attribute in attributes - found = false - if row is attribute.name - found = true - dataTypesAttribute = _.clone(attribute) - else if row + '_id' is attribute.name - found = true - dataTypesAttribute = _.clone(attribute) - dataTypesAttribute['name'] = row - if found - dataTypesAttribute['type'] = 'link' - if !dataTypesAttribute['dataType'] - dataTypesAttribute['dataType'] = 'edit' - dataTypesForCols.push dataTypesAttribute - else - dataTypesForCols.push { - name: row - type: 'link' - dataType: 'edit' - } + # get new order + groupObjects = _.groupBy( + data.objects + (item) => + return '' if !item[data.groupBy] + return item[data.groupBy].displayName() if item[data.groupBy].displayName + item[data.groupBy] + ) + groupOrder = [] + for group, value of groupObjects + groupOrder.push group - # extended table format - if data.overview_extended - if !header - header = [] - for row in data.overview_extended - for attribute in attributes - if row.name is attribute.name - header.push attribute - else - rowWithoutId = row.name + '_id' - if rowWithoutId is attribute.name - header.push attribute + # sort new groups + groupOrder = _.sortBy( + groupOrder + (item) => + item + ) - dataTypesForCols = data.overview_extended + # create new data array + data.objects = [] + for group in groupOrder + data.objects = data.objects.concat groupObjects[group] + groupObjects[group] = [] # release old array - # generate content data - for object in data.objects + # get header data + header = [] + for item in overview + headerFound = false + for attribute in attributes + if attribute.name is item + headerFound = true + header.push attribute + else + rowWithoutId = item + '_id' + if attribute.name is rowWithoutId + headerFound = true + header.push attribute - # check if info for each col. is already there - for row in dataTypesForCols - - # lookup relation - if !object[row.name] - rowWithoutId = row.name + '_id' - for attribute in attributes - if rowWithoutId is attribute.name - if attribute.relation && App[ attribute.relation ] - if App[ attribute.relation ].exists( object[rowWithoutId] ) - record = App[ attribute.relation ].find( object[rowWithoutId] ) - object[row.name] = record.name - - @log 'debug', 'table', 'header', header, 'overview', dataTypesForCols, 'objects', data.objects + # get content + @log 'debug', 'table', 'header', header, 'overview', 'objects', data.objects table = App.view('generic/table')( header: header - overview: dataTypesForCols objects: data.objects checkbox: data.checkbox radio: data.radio groupBy: data.groupBy destroy: destroy + callbacks: data.callbackAttributes ) # convert to jquery object table = $(table) + cursorMap = + click: 'pointer' + dblclick: 'pointer' + #mouseover: 'alias' + + # bind col. + if data.bindCol + for name, item of data.bindCol + if item.events + position = 0 + if data.checkbox + position += 1 + hit = false + + for headerName in header + if !hit + position += 1 + if headerName.name is name || headerName.name is "#{name}_id" + hit = true + + if hit + for event, callback of item.events + do (table, event, callback) => + if cursorMap[event] + table.find("tbody > tr > td:nth-child(#{position}) > span").css( 'cursor', cursorMap[event] ) + table.on( event, "tbody > tr > td:nth-child(#{position}) > span", + (e) => + e.stopPropagation() + id = $(e.target).parents('tr').data('id') + callback(id, e) + ) + + # bind row + if data.bindRow + if data.bindRow.events + for event, callback of data.bindRow.events + do (table, event, callback) => + if cursorMap[event] + table.find('tbody > tr').css( 'cursor', cursorMap[event] ) + table.on( event, 'tbody > tr', + (e) => + id = $(e.target).parents('tr').data('id') + callback(id, e) + ) + + # bind bindCheckbox + if data.bindCheckbox + if data.bindCheckbox.events + for event, callback of data.bindCheckbox.events + do (table, event, callback) => + table.delegate('input[name="bulk"]', event, (e) -> + e.stopPropagation() + id = $(e.target).parents('tr').data('id') + checked = $(e.target).prop('checked') + callback(id, checked, e) + ) + # bind on delete dialog if data.model && destroy table.delegate('[data-type="destroy"]', 'click', (e) -> + e.stopPropagation() e.preventDefault() itemId = $(e.target).parents('tr').data('id') item = data.model.find(itemId) @@ -165,11 +205,24 @@ class App.ControllerTable extends App.Controller # enable checkbox bulk selection if data.checkbox - table.delegate('[name="bulk_all"]', 'click', (e) -> + table.delegate('input[name="bulk_all"]', 'click', (e) -> + e.stopPropagation() if $(e.target).prop('checked') - $(e.target).parents().find('[name="bulk"]').prop( 'checked', true ); + $(e.target).parents('table').find('[name="bulk"]').each( -> + if !$(@).prop('checked') + #$(@).prop('checked', true) + $(@).trigger('click') + ) else - $(e.target).parents().find('[name="bulk"]').prop( 'checked', false ); + $(e.target).parents('table').find('[name="bulk"]').each( -> + if $(@).prop('checked') + #$(@).prop('checked', false) + $(@).trigger('click') + ) ) - return table + time = => + @frontendTimeUpdate() + @delay(time, 80) + + table diff --git a/app/assets/javascripts/app/controllers/_channel/email.js.coffee b/app/assets/javascripts/app/controllers/_channel/email.js.coffee index 34f81008f..08af8863e 100644 --- a/app/assets/javascripts/app/controllers/_channel/email.js.coffee +++ b/app/assets/javascripts/app/controllers/_channel/email.js.coffee @@ -1,8 +1,3 @@ -$.fn.item = (genericObject) -> - elementID = $(@).data('id') - elementID or= $(@).parents('[data-id]').data('id') - genericObject.find(elementID) - class App.ChannelEmail extends App.ControllerTabs constructor: -> super @@ -46,7 +41,6 @@ class App.ChannelEmail extends App.ControllerTabs class App.ChannelEmailFilter extends App.Controller events: 'click [data-type=new]': 'new' - 'click [data-type=edit]': 'edit' constructor: -> super @@ -59,9 +53,12 @@ class App.ChannelEmailFilter extends App.Controller template = $( '
' + App.i18n.translateContent('New') + '
' ) new App.ControllerTable( - el: template.find('.overview'), - model: App.PostmasterFilter, - objects: data, + el: template.find('.overview') + model: App.PostmasterFilter + objects: data + bindRow: + events: + 'click': @edit ) @html template @@ -69,10 +66,9 @@ class App.ChannelEmailFilter extends App.Controller e.preventDefault() new App.ChannelEmailFilterEdit( {} ) - edit: (e) => + edit: (id, e) => e.preventDefault() - item = $(e.target).item( App.PostmasterFilter ) - new App.ChannelEmailFilterEdit( object: item ) + new App.ChannelEmailFilterEdit( object: App.PostmasterFilter.find(id) ) class App.ChannelEmailFilterEdit extends App.ControllerModal constructor: -> @@ -135,7 +131,6 @@ class App.ChannelEmailFilterEdit extends App.ControllerModal class App.ChannelEmailAddress extends App.Controller events: 'click [data-type=new]': 'new' - 'click [data-type=edit]': 'edit' constructor: -> super @@ -151,6 +146,9 @@ class App.ChannelEmailAddress extends App.Controller el: template.find('.overview') model: App.EmailAddress objects: data + bindRow: + events: + 'click': @edit ) @html template @@ -159,9 +157,9 @@ class App.ChannelEmailAddress extends App.Controller e.preventDefault() new App.ChannelEmailAddressEdit( {} ) - edit: (e) => + edit: (id, e) => e.preventDefault() - item = $(e.target).item( App.EmailAddress ) + item = App.EmailAddress.find(id) new App.ChannelEmailAddressEdit( object: item ) class App.ChannelEmailAddressEdit extends App.ControllerModal @@ -223,7 +221,6 @@ class App.ChannelEmailAddressEdit extends App.ControllerModal class App.ChannelEmailSignature extends App.Controller events: 'click [data-type=new]': 'new' - 'click [data-type=edit]': 'edit' constructor: -> super @@ -238,17 +235,19 @@ class App.ChannelEmailSignature extends App.Controller el: template.find('.overview') model: App.Signature objects: data + bindRow: + events: + 'click': @edit ) - @html template new: (e) => e.preventDefault() new App.ChannelEmailSignatureEdit( {} ) - edit: (e) => + edit: (id, e) => e.preventDefault() - item = $(e.target).item( App.Signature ) + item = App.Signature.find(id) new App.ChannelEmailSignatureEdit( object: item ) class App.ChannelEmailSignatureEdit extends App.ControllerModal @@ -310,31 +309,23 @@ class App.ChannelEmailSignatureEdit extends App.ControllerModal class App.ChannelEmailInbound extends App.Controller events: 'click [data-type=new]': 'new' - 'click [data-type=edit]': 'edit' constructor: -> super - App.Channel.subscribe( @render, initFetch: true ) render: => - channels = App.Channel.all() - - data = [] - for channel in channels - if channel.area is 'Email::Inbound' - channel.host = channel.options['host'] - channel.user = channel.options['user'] - data.push channel + channels = App.Channel.search( filter: { area: 'Email::Inbound' } ) template = $( '
' + App.i18n.translateContent('New') + '
' ) new App.ControllerTable( - el: template.find('.overview'), - header: ['Host', 'User', 'Adapter', 'Active'], - overview: ['host', 'user', 'adapter', 'active'], - model: App.Channel, - objects: data, + el: template.find('.overview') + model: App.Channel + objects: channels + bindRow: + events: + 'click': @edit ) @html template @@ -342,9 +333,9 @@ class App.ChannelEmailInbound extends App.Controller e.preventDefault() new App.ChannelEmailInboundEdit( {} ) - edit: (e) => + edit: (id, e) => e.preventDefault() - item = $(e.target).item( App.Channel ) + item = App.Channel.find(id) new App.ChannelEmailInboundEdit( object: item ) @@ -354,29 +345,13 @@ class App.ChannelEmailInboundEdit extends App.ControllerModal @render(@object) render: (data = {}) -> - - if !data['options'] - data['options'] = {} - data['options']['ssl'] = true - data['active'] = true - - configure_attributes = [ - { name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: { IMAP: 'IMAP', POP3: 'POP3' } , class: 'span4', default: data['adapter'] }, - { name: 'host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, class: 'span4', autocapitalize: false, default: (data['options']&&data['options']['host']) }, - { name: 'user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, class: 'span4', autocapitalize: false, default: (data['options']&&data['options']['user']) }, - { name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, class: 'span4', autocapitalize: false, default: (data['options']&&data['options']['password']) }, - { name: 'ssl', display: 'SSL', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' }, translate: true, class: 'span4', default: (data['options']&&data['options']['ssl']) }, - { name: 'folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, class: 'span4', autocapitalize: false, default: (data['options']&&data['options']['folder']) }, - { name: 'group_id', display: 'Group', tag: 'select', multiple: false, null: false, filter: @edit_form, nulloption: false, relation: 'Group', class: 'span4', default: data['group_id'] }, - { name: 'active', display: 'Active', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' } , translate: true, class: 'span4', default: data['active'] }, - ] if @object @html App.view('generic/admin/edit')( head: 'Email Channel' ) @form = new App.ControllerForm( el: @el.find('#object_edit') - model: { configure_attributes: configure_attributes, className: '' } + model: App.Channel autofocus: true ) else @@ -385,7 +360,7 @@ class App.ChannelEmailInboundEdit extends App.ControllerModal ) @form = new App.ControllerForm( el: @el.find('#object_new') - model: { configure_attributes: configure_attributes, className: '' } + model: App.Channel autofocus: true ) @modalShow() @@ -395,21 +370,10 @@ class App.ChannelEmailInboundEdit extends App.ControllerModal # get params params = @formParam(e.target) + params['area'] = 'Email::Inbound' object = @object || new App.Channel - object.load( - area: 'Email::Inbound' - adapter: params['adapter'] - group_id: params['group_id'] - options: { - host: params['host'] - user: params['user'] - password: params['password'] - ssl: params['ssl'] - folder: params['folder'] - }, - active: params['active'] - ) + object.load(params) # validate form errors = @form.validate( params ) @@ -463,7 +427,7 @@ class App.ChannelEmailOutbound extends App.Controller channel_used = channel configure_attributes = [ - { name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: adapters , class: 'span4', default: adapter_used }, + { name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: adapters , default: adapter_used }, ] new App.ControllerForm( el: @el.find('#form-email-adapter'), @@ -476,10 +440,10 @@ class App.ChannelEmailOutbound extends App.Controller if adapter_used is 'SMTP' configure_attributes = [ - { name: 'host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, class: 'span4', autocapitalize: false, default: (channel_used['options']&&channel_used['options']['host']) }, - { name: 'user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, class: 'span4', autocapitalize: false, default: (channel_used['options']&&channel_used['options']['user']) }, - { name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, class: 'span4', autocapitalize: false, default: (channel_used['options']&&channel_used['options']['password']) }, - { name: 'ssl', display: 'SSL', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' } , class: 'span4', translate: true, default: (channel_used['options']&&channel_used['options']['ssl']) }, + { name: 'host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, default: (channel_used['options']&&channel_used['options']['host']) }, + { name: 'user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, default: (channel_used['options']&&channel_used['options']['user']) }, + { name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, default: (channel_used['options']&&channel_used['options']['password']) }, + { name: 'ssl', display: 'SSL', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' } , translate: true, default: (channel_used['options']&&channel_used['options']['ssl']) }, { name: 'port', display: 'Port', tag: 'input', type: 'text', limit: 5, null: false, class: 'span1', autocapitalize: false, default: ((channel_used['options']&&channel_used['options']['port']) || 25) }, ] @form = new App.ControllerForm( diff --git a/app/assets/javascripts/app/controllers/_dashboard/ticket.js.coffee b/app/assets/javascripts/app/controllers/_dashboard/ticket.js.coffee index 5d1b7e6fd..c758aca99 100644 --- a/app/assets/javascripts/app/controllers/_dashboard/ticket.js.coffee +++ b/app/assets/javascripts/app/controllers/_dashboard/ticket.js.coffee @@ -1,6 +1,5 @@ class App.DashboardTicket extends App.Controller events: - 'click [data-type=edit]': 'zoom' 'click [data-type=settings]': 'settings' 'click [data-type=page]': 'page' @@ -18,11 +17,11 @@ class App.DashboardTicket extends App.Controller # render @fetch() - fetch: => + fetch: (force) => # use cache of first page cache = App.Store.get( @key ) - if cache + if !force && cache @load( cache ) # init fetch via ajax, all other updates on time via websockets @@ -55,12 +54,13 @@ class App.DashboardTicket extends App.Controller App.Overview.unbind('local:rerender') App.Overview.bind 'local:rerender', (record) => @log 'notice', 'rerender...', record + data.overview = record @render(data) App.Overview.unbind('local:refetch') App.Overview.bind 'local:refetch', (record) => @log 'notice', 'refetch...', record - @fetch() + @fetch(true) @render( data ) @@ -72,8 +72,8 @@ class App.DashboardTicket extends App.Controller @overview = data.overview @tickets_count = data.tickets_count @ticket_ids = data.ticket_ids - # FIXME 10 - pages_total = parseInt( ( @tickets_count / 10 ) + 0.99999 ) || 1 + per_page = @overview.view.per_page || 10 + pages_total = parseInt( ( @tickets_count / per_page ) + 0.99999 ) || 1 html = App.view('dashboard/ticket')( overview: @overview, pages_total: pages_total, @@ -92,13 +92,39 @@ class App.DashboardTicket extends App.Controller if @ticket_ids[ i - 1 ] @tickets_in_table.push App.Ticket.retrieve( @ticket_ids[ i - 1 ] ) - shown_all_attributes = @ticketTableAttributes( App.Overview.find(@overview.id).view.d ) + openTicket = (id,e) => + ticket = App.Ticket.retrieve(id) + @navigate ticket.uiUrl() + callbackTicketTitleAdd = (value, object, attribute, attributes, refObject) => + attribute.title = object.title + callbackLinkToTicket = (value, object, attribute, attributes, refObject) => + attribute.link = object.uiUrl() + callbackResetLink = (value, object, attribute, attributes, refObject) => + attribute.link = undefined + callbackUserPopover = (value, object, attribute, attributes, refObject) => + attribute.class = 'user-popover' + attribute.data = + id: refObject.id + new App.ControllerTable( + overview: @overview.view.d el: html.find('.table-overview'), - overview_extended: shown_all_attributes, - model: App.Ticket, + model: App.Ticket objects: @tickets_in_table, - checkbox: false, + checkbox: false + groupBy: @overview.group_by + bindRow: + events: + 'click': openTicket + callbackAttributes: + customer_id: + [ callbackResetLink, callbackUserPopover ] + owner_id: + [ callbackResetLink, callbackUserPopover ] + title: + [ callbackLinkToTicket, callbackTicketTitleAdd ] + number: + [ callbackLinkToTicket, callbackTicketTitleAdd ] ) @html html @@ -122,8 +148,9 @@ class App.DashboardTicket extends App.Controller settings: (e) => e.preventDefault() - new Settings( - overview: App.Overview.find(@overview.id) + new App.OverviewSettings( + overview_id: @overview.id + view_mode: 'd' ) page: (e) => @@ -132,139 +159,3 @@ class App.DashboardTicket extends App.Controller @start_page = id @fetch() -class Settings extends App.ControllerModal - constructor: -> - super - @render() - - render: -> - - @html App.view('dashboard/ticket_settings')( - overview: @overview, - ) - @configure_attributes_article = [ -# { name: 'from', display: 'From', tag: 'input', type: 'text', limit: 100, null: false, class: 'span8', }, -# { name: 'to', display: 'To', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', item_class: 'hide' }, -# { name: 'type_id', display: 'Type', tag: 'select', multiple: false, null: true, relation: 'TicketArticleType', default: '9', class: 'medium', item_class: 'pull-left' }, -# { name: 'internal', display: 'Visibility', tag: 'radio', default: false, null: true, options: { true: 'internal', false: 'public' }, class: 'medium', item_class: 'pull-left' }, - { - name: 'per_page', - display: 'Items per page', - tag: 'select', - multiple: false, - null: false, -# default: @overview.view.d.per_page, - options: { - 5: 5, - 10: 10, - 15: 15, - 20: 20, - }, - class: 'medium', -# item_class: 'pull-left', - }, - { - name: 'attributes', - display: 'Attributes', - tag: 'checkbox', - default: @overview.view.d, - null: false, - translate: true - options: { - number: 'Number' - title: 'Title' - customer: 'Customer' - state: 'State' - priority: 'Priority' - group: 'Group' - owner: 'Owner' - created_at: 'Age' - last_contact: 'Last Contact' - last_contact_agent: 'Last Contact Agent' - last_contact_customer: 'Last Contact Customer' - first_response: 'First Response' - close_time: 'Close Time' - escalation_time: 'Escalation in' - article_count: 'Article Count' - }, - class: 'medium', -# item_class: 'pull-left', - }, - { - name: 'order_by', - display: 'Order', - tag: 'select', - default: @overview.order.by, - null: false, - translate: true - options: { - number: 'Number' - title: 'Title' - customer: 'Customer' - state: 'State' - priority: 'Priority' - group: 'Group' - owner: 'Owner' - created_at: 'Age' - last_contact: 'Last Contact' - last_contact_agent: 'Last Contact Agent' - last_contact_customer: 'Last Contact Customer' - first_response: 'First Response' - close_time: 'Close Time' - escalation_time: 'Escalation in' - article_count: 'Article Count' - }, - class: 'medium', - }, - { - name: 'order_by_direction', - display: 'Direction', - tag: 'select', - default: @overview.order.direction, - null: false, - translate: true - options: { - ASC: 'up', - DESC: 'down', - }, - class: 'medium', - }, - ] - - new App.ControllerForm( - el: @el.find('#form-setting'), - model: { configure_attributes: @configure_attributes_article }, - autofocus: false, - ) - - @modalShow() - - submit: (e) => - e.preventDefault() - params = @formParam(e.target) - - # check if refetch is needed - @reload_needed = 0 - if @overview.view['d']['per_page'] isnt params['per_page'] - @overview.view['d']['per_page'] = params['per_page'] - @reload_needed = 1 - - if @overview.order['by'] isnt params['order_by'] - @overview.order['by'] = params['order_by'] - @reload_needed = 1 - - if @overview.order['direction'] isnt params['order_by_direction'] - @overview.order['direction'] = params['order_by_direction'] - @reload_needed = 1 - - @overview.view['d'] = params['attributes'] - - @overview.save( - done: => - if @reload_needed - @overview.trigger('local:refetch') - else - @overview.trigger('local:rerender') - ) - - @modalHide() diff --git a/app/assets/javascripts/app/controllers/ticket_overview.js.coffee b/app/assets/javascripts/app/controllers/ticket_overview.js.coffee index 35bb81530..6ef3ee9b9 100644 --- a/app/assets/javascripts/app/controllers/ticket_overview.js.coffee +++ b/app/assets/javascripts/app/controllers/ticket_overview.js.coffee @@ -1,7 +1,6 @@ class Index extends App.Controller constructor: -> super - @render() render: -> @@ -55,11 +54,11 @@ class Table extends App.ControllerContent # render @fetch() - fetch: => + fetch: (force) => # use cache of first page cache = App.Store.get( @key ) - if cache + if !force && cache @load(cache) # init fetch via ajax, all other updates on time via websockets @@ -106,7 +105,7 @@ class Table extends App.ControllerContent App.Overview.unbind('local:refetch') App.Overview.bind 'local:refetch', (record) => @log 'notice', 'refetch...', record - @fetch() + @fetch(true) @ticket_list_show = [] for ticket_id in @ticket_ids @@ -123,6 +122,8 @@ class Table extends App.ControllerContent render: -> + + # if customer and no ticket exists, show the following message only if !@ticket_list_show[0] && @isRole('Customer') @html App.view('customer_not_ticket_exists')() @@ -131,6 +132,7 @@ class Table extends App.ControllerContent @selected = @bulkGetSelected() # set page title + @overview = App.Overview.find( @overview.id ) @title @overview.name # render init page @@ -182,27 +184,51 @@ class Table extends App.ControllerContent ) @el.find('.table-overview').append(table) else - shown_all_attributes = @ticketTableAttributes( App.Overview.find( @overview.id ).view.s ) - groupBy = undefined - if @overview.group_by - group_by = - name: @overview.group_by - id: @overview.group_by + '_id' + openTicket = (id,e) => + ticket = App.Ticket.retrieve(id) + @navigate ticket.uiUrl() + callbackTicketTitleAdd = (value, object, attribute, attributes, refObject) => + attribute.title = object.title + callbackLinkToTicket = (value, object, attribute, attributes, refObject) => + attribute.link = object.uiUrl() + callbackResetLink = (value, object, attribute, attributes, refObject) => + attribute.link = undefined + callbackUserPopover = (value, object, attribute, attributes, refObject) => + attribute.class = 'user-popover' + attribute.data = + id: refObject.id + callbackCheckbox = (id, checked, e) => + if @el.find('table').find('input[name="bulk"]:checked').length == 0 + @el.find('.bulk-action').addClass('hide') + else + @el.find('.bulk-action').removeClass('hide') - # remove group by attribute from show attributes list - shown_all_attributes = _.filter( - shown_all_attributes - (item) => - return item if item.name isnt @overview.group_by - return - ) new App.ControllerTable( - el: @el.find('.table-overview') - overview_extended: shown_all_attributes - model: App.Ticket - objects: @ticket_list_show - checkbox: checkbox - groupBy: group_by + overview: @overview.view.s + el: @el.find('.table-overview') + model: App.Ticket + objects: @ticket_list_show + checkbox: checkbox + groupBy: @overview.group_by + bindRow: + events: + 'click': openTicket + #bindCol: + # customer_id: + # events: + # 'mouseover': popOver + callbackAttributes: + customer_id: + [ callbackResetLink, callbackUserPopover ] + owner_id: + [ callbackResetLink, callbackUserPopover ] + title: + [ callbackLinkToTicket, callbackTicketTitleAdd ] + number: + [ callbackLinkToTicket, callbackTicketTitleAdd ] + bindCheckbox: + events: + 'click': callbackCheckbox ) @bulkSetSelected( @selected ) @@ -215,12 +241,13 @@ class Table extends App.ControllerContent # start bulk action observ @el.find('.bulk-action').append( @bulk_form() ) - if @el.find('.table-overview').find('[name="bulk"]:checked').length isnt 0 + if @el.find('.table-overview').find('input[name="bulk"]:checked').length isnt 0 @el.find('.bulk-action').removeClass('hide') # show/hide bulk action - @el.find('.table-overview').delegate('[name="bulk"], [name="bulk_all"]', 'click', (e) => - if @el.find('.table-overview').find('[name="bulk"]:checked').length == 0 + @el.find('.table-overview').delegate('input[name="bulk"], input[name="bulk_all"]', 'click', (e) => + console.log('YES') + if @el.find('.table-overview').find('input[name="bulk"]:checked').length == 0 # hide @el.find('.bulk-action').addClass('hide') @@ -324,6 +351,7 @@ class Table extends App.ControllerContent @fetch() ) ) + @el.find('.table-overview').find('[name="bulk"]:checked').prop('checked', false) App.Event.trigger 'notify', { type: 'success' msg: App.i18n.translateContent('Bulk-Action executed!') @@ -343,14 +371,15 @@ class Table extends App.ControllerContent settings: (e) => e.preventDefault() - new Settings( - overview: App.Overview.find(@overview.id), - view_mode: @view_mode, + new App.OverviewSettings( + overview_id: @overview.id + view_mode: @view_mode ) -class Settings extends App.ControllerModal +class App.OverviewSettings extends App.ControllerModal constructor: -> super + @overview = App.Overview.find(@overview_id) @render() render: -> @@ -358,119 +387,154 @@ class Settings extends App.ControllerModal @html App.view('dashboard/ticket_settings')( overview: @overview, ) - @configure_attributes_article = [ -# { name: 'from', display: 'From', tag: 'input', type: 'text', limit: 100, null: false, class: 'span8', }, -# { name: 'to', display: 'To', tag: 'input', type: 'text', limit: 100, null: true, class: 'span7', item_class: 'hide' }, -# { name: 'type_id', display: 'Type', tag: 'select', multiple: false, null: true, relation: 'TicketArticleType', default: '9', class: 'medium', item_class: 'pull-left' }, -# { name: 'internal', display: 'Visibility', tag: 'radio', default: false, null: true, options: { true: 'internal', false: 'public' }, class: 'medium', item_class: 'pull-left' }, - { - name: 'per_page' - display: 'Items per page' - tag: 'select' - multiple: false - null: false -# default: @overview.view[@view_mode].per_page - options: - 15: 15 - 20: 20 - 25: 25 - 30: 30 - 35: 35 - class: 'medium' -# item_class: 'pull-left' - }, - { - name: 'attributes' - display: 'Attributes' - tag: 'checkbox' - default: @overview.view[@view_mode] - null: false - translate: true - options: -# true: 'internal' -# false: 'public' - number: 'Number' - title: 'Title' - customer: 'Customer' - state: 'State' - priority: 'Priority' - group: 'Group' - owner: 'Owner' - created_at: 'Age' - last_contact: 'Last Contact' - last_contact_agent: 'Last Contact Agent' - last_contact_customer: 'Last Contact Customer' - first_response: 'First Response' - close_time: 'Close Time' - escalation_time: 'Escalation in' - article_count: 'Article Count' - class: 'medium' - }, - { - name: 'order_by' - display: 'Order' - tag: 'select' - default: @overview.order.by - null: false - translate: true - options: - number: 'Number' - title: 'Title' - customer: 'Customer' - state: 'State' - priority: 'Priority' - group: 'Group' - owner: 'Owner' - created_at: 'Age' - last_contact: 'Last Contact' - last_contact_agent: 'Last Contact Agent' - last_contact_customer: 'Last Contact Customer' - first_response: 'First Response' - close_time: 'Close Time' - escalation_time: 'Escalation in' - article_count: 'Article Count' - class: 'medium' - }, - { - name: 'order_by_direction' - display: 'Direction' - tag: 'select' - default: @overview.order.direction - null: false - translate: true - options: - ASC: 'up' - DESC: 'down' - class: 'medium' - }, - { - name: 'group_by' - display: 'Group by' - tag: 'select' - default: @overview.group_by - null: true - nulloption: true - translate: true - options: - customer: 'Customer' - state: 'State' - priority: 'Priority' - group: 'Group' - owner: 'Owner' - class: 'medium' - }, -# { -# name: 'condition', -# display: 'Conditions', -# tag: 'select', -# multiple: false, -# null: false, -# relation: 'TicketArticleType', -# default: '9', -# class: 'medium', -# item_class: 'pull-left', -# }, - ] + @configure_attributes_article = [] + if @view_mode is 'd' + @configure_attributes_article.push({ + name: 'view::per_page', + display: 'Items per page', + tag: 'select', + multiple: false, + null: false, + default: @overview.view.per_page + options: { + 5: ' 5' + 10: '10' + 15: '15' + 20: '20' + 25: '25' + }, + class: 'medium', + }) + @configure_attributes_article.push({ + name: "view::#{@view_mode}" + display: 'Attributes' + tag: 'checkbox' + default: @overview.view[@view_mode] + null: false + translate: true + options: [ + { + value: 'number' + name: 'Number' + }, + { + value: 'title' + name: 'Title' + }, + { + value: 'customer' + name: 'Customer' + }, + { + value: 'organization' + name: 'Organization' + }, + { + value: 'state' + name: 'State' + }, + { + value: 'priority' + name: 'Priority' + }, + { + value: 'group' + name: 'Group' + }, + { + value: 'owner' + name: 'Owner' + }, + { + value: 'created_at' + name: 'Age' + }, + { + value: 'last_contact' + name: 'Last Contact' + }, + { + value: 'last_contact_agent' + name: 'Last Contact Agent' + }, + { + value: 'last_contact_customer' + name: 'Last Contact Customer' + }, + { + value: 'first_response' + name: 'First Response' + }, + { + value: 'close_time' + name: 'Close Time' + }, + { + value: 'escalation_time' + name: 'Escalation in' + }, + { + value: 'article_count' + name: 'Article Count' + }, + ] + class: 'medium' + }, + { + name: 'order::by' + display: 'Order' + tag: 'select' + default: @overview.order.by + null: false + translate: true + options: + number: 'Number' + title: 'Title' + customer: 'Customer' + organization: 'Organization' + state: 'State' + priority: 'Priority' + group: 'Group' + owner: 'Owner' + created_at: 'Age' + last_contact: 'Last Contact' + last_contact_agent: 'Last Contact Agent' + last_contact_customer: 'Last Contact Customer' + first_response: 'First Response' + close_time: 'Close Time' + escalation_time: 'Escalation in' + article_count: 'Article Count' + class: 'medium' + }, + { + name: 'order::direction' + display: 'Direction' + tag: 'select' + default: @overview.order.direction + null: false + translate: true + options: + ASC: 'up' + DESC: 'down' + class: 'medium' + }, + { + name: 'group_by' + display: 'Group by' + tag: 'select' + default: @overview.group_by + null: true + nulloption: true + translate: true + options: + customer: 'Customer' + organization: 'Organization' + state: 'State' + priority: 'Priority' + group: 'Group' + owner: 'Owner' + class: 'medium' + }) new App.ControllerForm( el: @el.find('#form-setting') @@ -486,19 +550,18 @@ class Settings extends App.ControllerModal # check if refetch is needed @reload_needed = 0 - if @overview.order['by'] isnt params['order_by'] - @overview.order['by'] = params['order_by'] + if @overview.order.by isnt params.order.by + @overview.order.by = params.order.by @reload_needed = 1 - if @overview.order['direction'] isnt params['order_by_direction'] - @overview.order['direction'] = params['order_by_direction'] + if @overview.order.direction isnt params.order.direction + @overview.order.direction = params.order.direction @reload_needed = 1 - if @overview['group_by'] isnt params['group_by'] - @overview['group_by'] = params['group_by'] - @reload_needed = 1 + for key, value of params.view + @overview.view[key] = value - @overview.view[@view_mode] = params['attributes'] + @overview.group_by = params.group_by @overview.save( done: => diff --git a/app/assets/javascripts/app/lib/app_post/i18n.js.coffee b/app/assets/javascripts/app/lib/app_post/i18n.js.coffee index af9add9e2..5e3b17761 100644 --- a/app/assets/javascripts/app/lib/app_post/i18n.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/i18n.js.coffee @@ -144,7 +144,10 @@ class _i18nSingleton extends Spine.Module translate: ( string, args... ) => # return '' on undefined + if typeof string is 'boolean' + string = string.toString() return '' if string is undefined + return '' if string is '' # return translation if @map[string] isnt undefined diff --git a/app/assets/javascripts/app/models/_application_model.js.coffee b/app/assets/javascripts/app/models/_application_model.js.coffee index dd43bd222..520b0d94e 100644 --- a/app/assets/javascripts/app/models/_application_model.js.coffee +++ b/app/assets/javascripts/app/models/_application_model.js.coffee @@ -16,6 +16,9 @@ class App.Model extends Spine.Model uiUrl: -> '#' + translate: -> + App[ @constructor.className ].configure_translate + objectDisplayName: -> @constructor.className @@ -67,11 +70,24 @@ class App.Model extends Spine.Model if !attribute.readonly # check required // if null is defined && null is false - if 'null' of attribute && !attribute[null] + if 'null' of attribute && !attribute[null] - # key exists not in hash || value is '' || value is undefined - if !( attribute.name of data['params'] ) || data['params'][attribute.name] is '' || data['params'][attribute.name] is undefined - errors[attribute.name] = 'is required' + # check :: fields + parts = attribute.name.split '::' + if parts[0] && !parts[1] + + # key exists not in hash || value is '' || value is undefined + if !( attribute.name of data['params'] ) || data['params'][attribute.name] is '' || data['params'][attribute.name] is undefined + errors[attribute.name] = 'is required' + + else if parts[0] && parts[1] && !parts[2] + + # key exists not in hash || value is '' || value is undefined + if !data.params[parts[0]] || !( parts[1] of data.params[parts[0]] ) || data.params[parts[0]][parts[1]] is '' || data.params[parts[0]][parts[1]] is undefined + errors[attribute.name] = 'is required' + + else + throw "can't parse '#{attribute.name}'" # check confirm password if attribute.type is 'password' && data['params'][attribute.name] && "#{attribute.name}_confirm" of data['params'] @@ -82,7 +98,9 @@ class App.Model extends Spine.Model errors["#{attribute.name}_confirm"] = '' # return error object - return errors if !_.isEmpty(errors) + if !_.isEmpty(errors) + console.log 'error', 'validation vailed', errors + return errors # return no errors return @@ -327,5 +345,3 @@ class App.Model extends Spine.Model return ) collection - - diff --git a/app/assets/javascripts/app/models/channel.js.coffee b/app/assets/javascripts/app/models/channel.js.coffee index 7c460627d..b58ea7c98 100644 --- a/app/assets/javascripts/app/models/channel.js.coffee +++ b/app/assets/javascripts/app/models/channel.js.coffee @@ -2,4 +2,25 @@ class App.Channel extends App.Model @configure 'Channel', 'adapter', 'area', 'options', 'group_id', 'active', 'updated_at' @extend Spine.Model.Ajax @url: @apiPath + '/channels' - @configure_delete = true \ No newline at end of file + @configure_delete = true + + @configure_attributes = [ + { name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: { IMAP: 'IMAP', POP3: 'POP3' } }, + { name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false }, + { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false }, + { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false }, + { name: 'options::ssl', display: 'SSL', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' }, translate: true, default: true}, + { name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false }, + { name: 'group_id', display: 'Group', tag: 'select', multiple: false, null: false, nulloption: true, relation: 'Group' }, + { name: 'active', display: 'Active', tag: 'select', multiple: false, null: false, options: { true: 'yes', false: 'no' }, translate: true, default: true }, + ] + @configure_overview = [ + 'adapter', 'options::host', 'options::user', 'group' + ] + + @_fillUp: (data) -> + + # group + data.group = App.Group.find( data.group_id ) + + data \ No newline at end of file diff --git a/app/assets/javascripts/app/models/overview.js.coffee b/app/assets/javascripts/app/models/overview.js.coffee index 8dca90e08..ae7b2c1ae 100644 --- a/app/assets/javascripts/app/models/overview.js.coffee +++ b/app/assets/javascripts/app/models/overview.js.coffee @@ -18,21 +18,64 @@ class App.Overview extends App.Model default: ['number', 'title', 'state', 'created_at'] null: false translate: true - options: - number: 'Number' - title: 'Title' - customer: 'Customer' - state: 'State' - priority: 'Priority' - group: 'Group' - owner: 'Owner' - created_at: 'Age' - last_contact: 'Last Contact' - last_contact_agent: 'Last Contact Agent' - last_contact_customer: 'Last Contact Customer' - first_response: 'First Response' - close_time: 'Close Time' - article_count: 'Article Count' + options: [ + { + value: 'number' + name: 'Number' + }, + { + value: 'title' + name: 'Title' + }, + { + value: 'customer' + name: 'Customer' + }, + { + value: 'state' + name: 'State' + }, + { + value: 'priority' + name: 'Priority' + }, + { + value: 'group' + name: 'Group' + }, + { + value: 'owner' + name: 'Owner' + }, + { + value: 'created_at' + name: 'Age' + }, + { + value: 'last_contact' + name: 'Last Contact' + }, + { + value: 'last_contact_agent' + name: 'Last Contact Agent' + }, + { + value: 'last_contact_customer' + name: 'Last Contact Customer' + }, + { + value: 'first_response' + name: 'First Response' + }, + { + value: 'close_time' + name: 'Close Time' + }, + { + value: 'article_count' + name: 'Article Count' + }, + ] class: 'medium' }, diff --git a/app/assets/javascripts/app/models/ticket.js.coffee b/app/assets/javascripts/app/models/ticket.js.coffee index 901e6b102..242fc38f2 100644 --- a/app/assets/javascripts/app/models/ticket.js.coffee +++ b/app/assets/javascripts/app/models/ticket.js.coffee @@ -5,19 +5,21 @@ class App.Ticket extends App.Model @configure_attributes = [ { name: 'number', display: '#', tag: 'input', type: 'text', limit: 100, null: true, read_only: true, style: 'width: 8%' }, { name: 'customer_id', display: 'Customer', tag: 'input', type: 'text', limit: 100, null: false, class: 'span8', autocapitalize: false, help: 'Select the customer of the Ticket or create one.', link: '»' }, + { name: 'organization_id', display: 'Organization', tagreadonly: 1 }, { name: 'group_id', display: 'Group', tag: 'select', multiple: false, limit: 100, null: false, class: 'span8', relation: 'Group', style: 'width: 10%' }, { name: 'owner_id', display: 'Owner', tag: 'select', multiple: false, limit: 100, null: true, class: 'span8', relation: 'User', style: 'width: 12%' }, { name: 'title', display: 'Title', tag: 'input', type: 'text', limit: 100, null: false, class: 'span8' }, { name: 'state_id', display: 'State', tag: 'select', multiple: false, null: false, relation: 'TicketState', default: 'new', class: 'medium', style: 'width: 12%' }, { name: 'priority_id', display: 'Priority', tag: 'select', multiple: false, null: false, relation: 'TicketPriority', default: '2 normal', class: 'medium', style: 'width: 12%' }, - { name: 'created_at', display: 'Created', tag: 'time', style: 'width: 12%' }, - { name: 'last_contact', display: 'Last contact', tag: 'time', null: true, style: 'width: 12%' }, - { name: 'last_contact_agent', display: 'Last contact (Agent)', tag: 'time', null: true, style: 'width: 12%' }, - { name: 'last_contact_customer', display: 'Last contact (Customer)', tag: 'time', null: true, style: 'width: 12%' }, - { name: 'first_response', display: 'First response', tag: 'time', null: true, style: 'width: 12%' }, - { name: 'close_time', display: 'Close time', tag: 'time', null: true, style: 'width: 12%' }, - { name: 'escalation_time', display: 'Escalation in', tag: 'time', null: true, style: 'width: 12%' }, + { name: 'last_contact', display: 'Last contact', type: 'time', null: true, style: 'width: 12%' }, + { name: 'last_contact_agent', display: 'Last contact (Agent)', type: 'time', null: true, style: 'width: 12%' }, + { name: 'last_contact_customer', display: 'Last contact (Customer)', type: 'time', null: true, style: 'width: 12%' }, + { name: 'first_response', display: 'First response', type: 'time', null: true, style: 'width: 12%' }, + { name: 'close_time', display: 'Close time', type: 'time', null: true, style: 'width: 12%' }, + { name: 'escalation_time', display: 'Escalation in', type: 'time', null: true, style: 'width: 12%', class: 'escalation' }, { name: 'article_count', display: 'Article#', style: 'width: 12%' }, + { name: 'created_at', display: 'Created', type: 'time', style: 'width: 12%', readonly: 1 }, + { name: 'updated_at', display: 'Updated', type: 'time', style: 'width: 12%', readonly: 1 }, ] uiUrl: -> @@ -41,6 +43,13 @@ class App.Ticket extends App.Model else data.customer = App.User.find( data.customer_id ) + # organization_id + if data.organization_id + if !App.Organization.exists( data.organization_id ) + console.error("Can't find user for data.organization_id #{data.organization_id} for ticket #{data.id}") + else + data.organization = App.Organization.find( data.organization_id ) + # owner if data.owner_id if !App.User.exists( data.owner_id ) diff --git a/app/assets/javascripts/app/models/ticket_article_sender.js.coffee b/app/assets/javascripts/app/models/ticket_article_sender.js.coffee index 122a7aecd..c9654e59e 100644 --- a/app/assets/javascripts/app/models/ticket_article_sender.js.coffee +++ b/app/assets/javascripts/app/models/ticket_article_sender.js.coffee @@ -2,3 +2,4 @@ class App.TicketArticleSender extends App.Model @configure 'TicketArticleSender', 'name', 'updated_at' @extend Spine.Model.Ajax @url: @apiPath + '/ticket_article_senders' + @configure_translate = true \ No newline at end of file diff --git a/app/assets/javascripts/app/models/ticket_article_type.js.coffee b/app/assets/javascripts/app/models/ticket_article_type.js.coffee index c2975c575..bab7f7c54 100644 --- a/app/assets/javascripts/app/models/ticket_article_type.js.coffee +++ b/app/assets/javascripts/app/models/ticket_article_type.js.coffee @@ -2,3 +2,4 @@ class App.TicketArticleType extends App.Model @configure 'TicketArticleType', 'name', 'updated_at' @extend Spine.Model.Ajax @url: @apiPath + '/ticket_article_types' + @configure_translate = true \ No newline at end of file diff --git a/app/assets/javascripts/app/models/ticket_priority.js.coffee b/app/assets/javascripts/app/models/ticket_priority.js.coffee index 2c45303cb..6139d808d 100644 --- a/app/assets/javascripts/app/models/ticket_priority.js.coffee +++ b/app/assets/javascripts/app/models/ticket_priority.js.coffee @@ -2,3 +2,13 @@ class App.TicketPriority extends App.Model @configure 'TicketPriority', 'name', 'note', 'active', 'updated_at' @extend Spine.Model.Ajax @url: @apiPath + '/ticket_priorities' + @configure_attributes = [ + { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, 'null': false, translate: true }, + { name: 'active', display: 'Active', tag: 'boolean', type: 'boolean', 'default': true, 'null': false }, + { name: 'updated_at', display: 'Updated', type: 'time', readonly: 1 }, + { name: 'created_at', display: 'Created', type: 'time', readonly: 1 }, + ] + @configure_translate = true + @configure_overview = [ + 'name', + ] \ No newline at end of file diff --git a/app/assets/javascripts/app/models/ticket_state.js.coffee b/app/assets/javascripts/app/models/ticket_state.js.coffee index 251ad864c..c54cfb8c2 100644 --- a/app/assets/javascripts/app/models/ticket_state.js.coffee +++ b/app/assets/javascripts/app/models/ticket_state.js.coffee @@ -2,3 +2,13 @@ class App.TicketState extends App.Model @configure 'TicketState', 'name', 'note', 'active' @extend Spine.Model.Ajax @url: @apiPath + '/ticket_states' + @configure_attributes = [ + { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, 'null': false, translate: true }, + { name: 'active', display: 'Active', tag: 'boolean', type: 'boolean', 'default': true, 'null': false }, + { name: 'updated_at', display: 'Updated', type: 'time', readonly: 1 }, + { name: 'created_at', display: 'Created', type: 'time', readonly: 1 }, + ] + @configure_translate = true + @configure_overview = [ + 'name', + ] \ No newline at end of file diff --git a/app/assets/javascripts/app/views/generic/table.jst.eco b/app/assets/javascripts/app/views/generic/table.jst.eco index 15e03e7d9..9f319f1a9 100644 --- a/app/assets/javascripts/app/views/generic/table.jst.eco +++ b/app/assets/javascripts/app/views/generic/table.jst.eco @@ -7,8 +7,8 @@ <% if @radio: %> <% end %> -<% for row in @header: %> - style="<%= row.style %>"<% end %>><%- @T( row.display ) %> +<% for item in @header: %> + style="<%= item.style %>"<% end %>><%- @T( item.display ) %> <% end %> <% if @destroy: %> <%- @T('Delete') %> @@ -16,17 +16,25 @@ +<% position = 0 %> <% length = @header.length %> <% if @checkbox || @radio: %> <% length++ %> <% end %> -<% position = 0 %> <% groupLast = '' %> <% for object in @objects: %> <% if @groupBy: %> - <% if groupLast isnt object[@groupBy.id]: %> - <%- @P( object[@groupBy.name] ) %> - <% groupLast = object[@groupBy.id] %> + <% if object[@groupBy] && object[@groupBy].displayName: %> + <% groupByName = object[@groupBy].displayName() %> + <% if object[@groupBy].translate(): %> + <% groupByName = @T(groupByName) %> + <% end %> + <% else: %> + <% groupByName = object[@groupBy] || '-' %> + <% end %> + <% if groupLast isnt groupByName: %> + <%- @P( groupByName ) %> + <% groupLast = groupByName %> <% end %> <% end %> <% position++ %> @@ -37,33 +45,53 @@ <% if @radio: %> <% end %> - <% for row in @overview: %> - <% displayName = @P( object[row.name], row ) %> - <% if row.translate: %><% displayName = @T( displayName ) %><% end %> - <% if row.title: %> - <% displayNameTitle = displayName %> - <% if object[row.title]: %> - <% displayNameTitle = @P( object[row.title], row ) %> - <% end %> + <% for item in @header: %> + <% translation = false %> + <% value = object[item.name] %> + <% item_id = item.name.substr(item.name.length-3, item.name.length) %> + <% if item_id is '_id' && object[ item.name.substr(0, item.name.length-3) ]: %> + <% value = object[ item.name.substr(0, item.name.length-3) ] %> + <% refObject = object[ item.name.substr(0, item.name.length-3) ] %> <% end %> - title="<%= displayNameTitle %>"<% end %>> - <% if row.type is 'link': %> - class="<%= row.class %>"<% end %>> - <% else: %> - class="<%= row.class %>"<% end %> <% if row.data && row.data.id: %>data-id="<%= object[row.name].id %>"<% end %>> - <% end %> - <% if row.translate || row.callback: %><%- displayName %><% else: %><%= displayName %><% end %> - <% if row.type is 'link': %><% else: %><% end %> + <% if !value: %> + <% parts = item.name.split '::' %> + <% if parts[0] && parts[1] && object[ parts[0] ]: %> + <% value = object[ parts[0] ][ parts[1] ] %> + <% end %> + <% end %> + <% if value && value.displayNameLong : %> + <% translation = true %> + <% value = value.displayNameLong() %> + <% else if value && value.displayName : %> + <% translation = true %> + <% value = value.displayName() %> + <% end %> + <% item_clone = item %> + <% if @callbacks: %> + <% for attribute, callbacksAll of @callbacks: %> + <% if attribute is item.name || attribute is item_id: %> + <% for callback in callbacksAll: %> + <% callback( value, object, item_clone, @header, refObject ) %> + <% end %> + <% end %> + <% end %> + <% end %> + <% #console.log('HH', item_clone.name, item_clone.type, item_clone.translate, item_clone, object.translate(), refObject, translation) %> + + <% if item_clone.link: %><% end %> + <% if item_clone.translate || ( translation && !refObject && object.translate && object.translate() ) || ( translation && refObject && refObject.translate && refObject.translate() ) : %> + class="<%= item_clone.class %>"<% end %>><%- @T( @P( value, item_clone ) ) %> + <% else if item_clone.type is 'time': %> + ? + <% else: %> + class="<%= item_clone.class %>"<% end %> <% if item_clone.title: %>title="<%= item_clone.title %>"<% end %> <% if item_clone.data: %>data-id="<%= item_clone.data.id %><% end %>"><%= @P(value) %> + <% end %> + <% if item_clone.link: %><% end %> - <% end %> - -<% if @destroy: %> - - - -<% end %> + <% end %> + <% if @destroy: %> + + <% end %> <% end %> diff --git a/app/controllers/tests_controller.rb b/app/controllers/tests_controller.rb index 4310cc601..20a702216 100644 --- a/app/controllers/tests_controller.rb +++ b/app/controllers/tests_controller.rb @@ -16,6 +16,13 @@ class TestsController < ApplicationController end end + # GET /tests/table + def table + respond_to do |format| + format.html # index.html.erb + end + end + # GET /test/wait def wait sleep params[:sec].to_i diff --git a/app/views/tests/table.html.erb b/app/views/tests/table.html.erb new file mode 100644 index 000000000..3bdc03f94 --- /dev/null +++ b/app/views/tests/table.html.erb @@ -0,0 +1,17 @@ + + + + + + + + + +
+ +
\ No newline at end of file diff --git a/config/routes/test.rb b/config/routes/test.rb index 371c93ee2..55406c6aa 100644 --- a/config/routes/test.rb +++ b/config/routes/test.rb @@ -2,6 +2,7 @@ Zammad::Application.routes.draw do match '/tests-core', :to => 'tests#core', :via => :get match '/tests-form', :to => 'tests#form', :via => :get + match '/tests-table', :to => 'tests#table', :via => :get match '/tests/wait/:sec', :to => 'tests#wait', :via => :get end \ No newline at end of file diff --git a/test/browser/unit_test.rb b/test/browser/aab_unit_test.rb similarity index 97% rename from test/browser/unit_test.rb rename to test/browser/aab_unit_test.rb index f0d680b6b..8306516f3 100644 --- a/test/browser/unit_test.rb +++ b/test/browser/aab_unit_test.rb @@ -1,7 +1,7 @@ # encoding: utf-8 require 'browser_test_helper' -class UnitTest < TestCase +class AAbUnitTest < TestCase def test_core tests = [ {