From 14fb64ba0a58953f943998b1c43db7180ff57a1f Mon Sep 17 00:00:00 2001 From: Ryan Lue Date: Mon, 11 Jun 2018 15:28:03 +0800 Subject: [PATCH] Implement ux_flow_next_up feature for macros (soft fix for #641) --- .../_application_controller.coffee | 6 + .../app/controllers/ticket_zoom.coffee | 6 +- .../ticket_zoom/attribute_bar.coffee | 50 ++++++-- .../javascripts/app/models/macro.coffee | 53 ++++++-- .../javascripts/app/models/overview.coffee | 8 ++ app/models/macro.rb | 1 + ...611070839_add_ux_flow_next_up_to_macros.rb | 5 + test/browser/agent_ticket_macro_test.rb | 113 +++++++++++++++--- 8 files changed, 207 insertions(+), 35 deletions(-) create mode 100644 db/migrate/20180611070839_add_ux_flow_next_up_to_macros.rb diff --git a/app/assets/javascripts/app/controllers/_application_controller.coffee b/app/assets/javascripts/app/controllers/_application_controller.coffee index 2119be3c4..56f4a520a 100644 --- a/app/assets/javascripts/app/controllers/_application_controller.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller.coffee @@ -138,6 +138,12 @@ class App.Controller extends Spine.Controller App.Event.trigger('menu:render') @delay(delay, 150) + closeTab: (key = @taskKey, dest) => + return if !key? + App.TaskManager.remove(key) + dest ?= App.TaskManager.nextTaskUrl() || '#' + @navigate dest + scrollTo: (x = 0, y = 0, delay = 0) -> a = -> window.scrollTo(x, y) diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.coffee index ded662c88..6cf19802e 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom.coffee @@ -421,7 +421,7 @@ class App.TicketZoom extends App.Controller object_id: @ticket_id overview_id: @overview_id el: elLocal.find('.js-ticketTitleContainer') - taskKey: @taskKey + taskKey: @taskKey ) new App.TicketZoomMeta( @@ -434,7 +434,7 @@ class App.TicketZoom extends App.Controller el: elLocal.find('.js-attributeBar') overview_id: @overview_id callback: @submit - taskKey: @taskKey + taskKey: @taskKey ) #if @shown # @attributeBar.start() @@ -448,7 +448,7 @@ class App.TicketZoom extends App.Controller formMeta: @formMeta form_id: @form_id defaults: @taskGet('article') - taskKey: @taskKey + taskKey: @taskKey ui: @ ) diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee index a177d46cc..8f960bcd5 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee @@ -4,7 +4,7 @@ class App.TicketZoomAttributeBar extends App.Controller '.js-reset': 'resetButton' events: - 'mousedown .js-openDropdownMacro': 'toggleDropdownMacro' + 'mousedown .js-openDropdownMacro': 'toggleMacroMenu' 'click .js-openDropdownMacro': 'stopPropagation' 'mouseup .js-dropdownActionMacro': 'performTicketMacro' 'mouseenter .js-dropdownActionMacro': 'onActionMacroMouseEnter' @@ -69,24 +69,54 @@ class App.TicketZoomAttributeBar extends App.Controller return if macroLastUpdated is @macroLastUpdated @render() - toggleDropdownMacro: => - if @buttonDropdown.hasClass 'is-open' - @closeMacroDropdown() - else - @buttonDropdown.addClass 'is-open' - $(document).bind 'click.buttonDropdown', @closeMacroDropdown + toggleMacroMenu: => + if @buttonDropdown.hasClass('is-open') then @closeMacroMenu() else @openMacroMenu() - closeMacroDropdown: => + openMacroMenu: => + @buttonDropdown.addClass 'is-open' + $(document).bind 'click.buttonDropdown', @closeMacroMenu + + closeMacroMenu: => @buttonDropdown.removeClass 'is-open' $(document).unbind 'click.buttonDropdown' performTicketMacro: (e) => macroId = $(e.currentTarget).data('id') - console.log 'perform action', @$(e.currentTarget).text(), macroId macro = App.Macro.find(macroId) @callback(e, macro.perform) - @closeMacroDropdown() + @closeMacroMenu() + @replaceTabWith(macro.ux_flow_next_up) + + replaceTabWith: (dest) => + switch dest + when 'none' + return + when 'next_task' + @closeTab() + when 'next_from_overview' + @closeTab() + @openNextTicketInOverview() + + openNextTicketInOverview: (overview = @overview_id, ticket = @ticket.id) => + # coerce ids to objects + overview = App.Overview.find(overview) if !(overview instanceof App.Overview) + ticket = App.Ticket.find(ticket) if !(ticket instanceof App.Ticket) + return if !overview? || !ticket? + + nextTicket = overview.nextTicket(ticket.id) + return if !nextTicket? + + # open task via task manager to preserve overview information + App.TaskManager.execute( + key: "Ticket-#{nextTicket.id}" + controller: 'TicketZoom' + params: + ticket_id: nextTicket.id + overview_id: overview.id + ) + + @navigate "ticket/zoom/#{nextTicket.id}" onActionMacroMouseEnter: (e) => @$(e.currentTarget).addClass('is-active') diff --git a/app/assets/javascripts/app/models/macro.coffee b/app/assets/javascripts/app/models/macro.coffee index a755a2ca1..28fa67d62 100644 --- a/app/assets/javascripts/app/models/macro.coffee +++ b/app/assets/javascripts/app/models/macro.coffee @@ -1,13 +1,52 @@ class App.Macro extends App.Model - @configure 'Macro', 'name', 'perform', 'note', 'active' + @configure 'Macro', 'name', 'perform', 'ux_flow_next_up', 'note', 'active' @extend Spine.Model.Ajax @url: @apiPath + '/macros' @configure_attributes = [ - { name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false }, - { name: 'perform', display: 'Execute changes on objects.', tag: 'ticket_perform_action', null: true }, - { name: 'updated_at', display: 'Updated', tag: 'datetime', readonly: 1 }, - { name: 'note', display: 'Note', tag: 'textarea', limit: 250, null: true }, - { name: 'active', display: 'Active', tag: 'active', default: true }, + { + name: 'name', + display: 'Name', + tag: 'input', + type: 'text', + limit: 100, + null: false + }, + { + name: 'perform', + display: 'Actions', + tag: 'ticket_perform_action', + null: true + }, + { + name: 'ux_flow_next_up', + display: 'Once completed...', + tag: 'select', + default: 'none', + options: { + none: 'Stay on tab', + next_task: 'Close tab', + next_from_overview: 'Advance to next ticket from overview' + } + }, + { + name: 'updated_at', + display: 'Updated', + tag: 'datetime', + readonly: 1 + }, + { + name: 'note', + display: 'Note', + tag: 'textarea', + limit: 250, + null: true + }, + { + name: 'active', + display: 'Active', + tag: 'active', + default: true + }, ] @configure_delete = true @configure_clone = true @@ -18,4 +57,4 @@ class App.Macro extends App.Model @description = ''' Macros are.... -''' \ No newline at end of file +''' diff --git a/app/assets/javascripts/app/models/overview.coffee b/app/assets/javascripts/app/models/overview.coffee index 7fb9f5ecc..5ee336061 100644 --- a/app/assets/javascripts/app/models/overview.coffee +++ b/app/assets/javascripts/app/models/overview.coffee @@ -76,6 +76,14 @@ You can also create overvies and limit them to specific agents or to groups of a uiUrl: -> "#ticket/view/#{@link}" + nextTicket: (startTicket) => + # coerce id to Ticket object + startTicket = App.Ticket.find(startTicket) if !(startTicket instanceof App.Ticket) + + tickets = App.OverviewListCollection.get(@link).tickets + currentIndex = _.findIndex(tickets, (t) -> t.id == startTicket.id) + tickets[currentIndex + 1] + @groupByAttributes: -> groupByAttributes = {} for key, attribute of App.Ticket.attributesGet() diff --git a/app/models/macro.rb b/app/models/macro.rb index 40c25f861..a29b9b3c7 100644 --- a/app/models/macro.rb +++ b/app/models/macro.rb @@ -7,4 +7,5 @@ class Macro < ApplicationModel store :perform validates :name, presence: true + validates :ux_flow_next_up, inclusion: { in: %w[none next_task next_from_overview] } end diff --git a/db/migrate/20180611070839_add_ux_flow_next_up_to_macros.rb b/db/migrate/20180611070839_add_ux_flow_next_up_to_macros.rb new file mode 100644 index 000000000..9661930c3 --- /dev/null +++ b/db/migrate/20180611070839_add_ux_flow_next_up_to_macros.rb @@ -0,0 +1,5 @@ +class AddUxFlowNextUpToMacros < ActiveRecord::Migration[5.1] + def change + add_column :macros, :ux_flow_next_up, :string, default: 'none', null: false + end +end diff --git a/test/browser/agent_ticket_macro_test.rb b/test/browser/agent_ticket_macro_test.rb index 94a5730d8..4b854c068 100644 --- a/test/browser/agent_ticket_macro_test.rb +++ b/test/browser/agent_ticket_macro_test.rb @@ -2,33 +2,116 @@ require 'browser_test_helper' class AgentTicketMacroTest < TestCase - def test_macro + # def test_macro + # @browser = browser_instance + # login( + # username: 'agent1@example.com', + # password: 'test', + # url: browser_url, + # ) + # tasks_close_all() + + # ticket1 = ticket_create( + # data: { + # customer: 'nico', + # group: 'Users', + # title: 'some subject - macro#1', + # body: 'some body - macro#1', + # }, + # ) + + # click(css: '.active.content .js-submitDropdown .js-openDropdownMacro') + # click(css: '.active.content .js-submitDropdown .js-dropdownActionMacro') + + # # verify tags + # tags_verify( + # tags: { + # 'spam' => true, + # 'tag1' => false, + # } + # ) + # end + + def test_macro_ux_flow_next_up @browser = browser_instance login( - username: 'agent1@example.com', + username: 'master@example.com', password: 'test', url: browser_url, ) tasks_close_all() - ticket1 = ticket_create( + # Setup: Create two tickets + ticket_create( data: { - customer: 'nico', + customer: 'nicole.braun', group: 'Users', - title: 'some subject - macro#1', - body: 'some body - macro#1', + title: 'Sample Ticket 1', + body: 'Lorem ipsum dolor sit amet consectetur adipisicing elit.', }, ) - click(css: '.active.content .js-submitDropdown .js-openDropdownMacro') - click(css: '.active.content .js-submitDropdown .js-dropdownActionMacro') - - # verify tags - tags_verify( - tags: { - 'spam' => true, - 'tag1' => false, - } + ticket_create( + data: { + customer: 'nicole.braun', + group: 'Users', + title: 'Sample Ticket 2', + body: 'Suspendisse volutpat lectus sem, in fermentum orci semper sit amet.', + }, ) + + # Setup: Create three macros (one for each ux_flow_next_up option) + click(css: 'a[href="#manage"]') + click(css: '.sidebar a[href="#manage/macros"]') + macro_options = ['Stay on tab', 'Close tab', 'Advance to next ticket from overview'] + macro_options.each.with_index do |o, i| + click(css: '.page-header-meta > a[data-type="new"]') + sendkey(css: '.modal-body input[name="name"]', value: "Test Macro #{i + 1}") + select(css: '.modal-body select[name="ux_flow_next_up"]', value: o) + click(css: '.modal-footer button[type="submit"]') + end + + click(css: 'a[title$="Sample Ticket 1"]') + + # Assert: Run the first macro and verify the tab is still open + click(css: '.active.content .js-submitDropdown .js-openDropdownMacro') + click(css: '.active.content .js-submitDropdown .js-dropdownActionMacro[data-id="2"]') + match(css: '.tasks > a.is-active > .nav-tab-name', value: 'Sample Ticket 1',) + + # Setup: Close all tabs and reopen only the first ticket + tasks_close_all() + click(css: 'a[href="#ticket/view"]') + begin + remaining_retries = 1 + click(css: 'a[href="#ticket/view/all_unassigned"]') + # responsive design means some elements are un-clickable at certain viewport sizes + rescue Selenium::WebDriver::Error::WebDriverError => e + raise e if remaining_retries.zero? + (remaining_retries -= 1) && click(css: 'a.tab.js-tab[href="#ticket/view/all_unassigned"]') + end + click(css: 'td[title="Sample Ticket 1"]') + + # Assert: Run the second macro and verify the tab is closed + click(css: '.active.content .js-submitDropdown .js-openDropdownMacro') + click(css: '.active.content .js-submitDropdown .js-dropdownActionMacro[data-id="3"]') + exists_not(css: '.tasks > a') + + # Setup: Reopen the first ticket via a Ticket Overview + click(css: 'a[href="#ticket/view"]') + begin + remaining_retries = 1 + click(css: 'a[href="#ticket/view/all_unassigned"]') + # responsive design means some elements are un-clickable at certain viewport sizes + rescue Selenium::WebDriver::Error::WebDriverError => e + raise e if remaining_retries.zero? + (remaining_retries -= 1) && click(css: 'a.tab.js-tab[href="#ticket/view/all_unassigned"]') + end + click(css: 'td[title="Sample Ticket 1"]') + + # Assert: Run the third macro and verify the second ticket opens + click(css: '.active.content .js-submitDropdown .js-openDropdownMacro') + click(css: '.active.content .js-submitDropdown .js-dropdownActionMacro[data-id="4"]') + match_not(css: '.tasks > a.task > .nav-tab-name', value: 'Sample Ticket 1',) + match(css: '.tasks > a.is-active > .nav-tab-name', value: 'Sample Ticket 2',) end end