Merge branch 'develop' of git.znuny.com:zammad/zammad into develop
This commit is contained in:
commit
72cef05b9e
9 changed files with 721 additions and 37 deletions
50
.gitignore
vendored
50
.gitignore
vendored
|
@ -4,49 +4,51 @@
|
||||||
# or operating system, you probably want to add a global ignore instead:
|
# or operating system, you probably want to add a global ignore instead:
|
||||||
# git config --global core.excludesfile ~/.gitignore_global
|
# git config --global core.excludesfile ~/.gitignore_global
|
||||||
|
|
||||||
|
# Ignore .swp files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
# Ignore bundler config
|
# Ignore bundler config
|
||||||
/.bundle
|
/.bundle
|
||||||
|
|
||||||
|
# Ignore mac stuff
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Ignore Rubymine config
|
||||||
|
/.idea
|
||||||
|
|
||||||
|
# Ignore .project files
|
||||||
|
/.project
|
||||||
|
|
||||||
|
# Ignore .rbenv-vars
|
||||||
|
/.rbenv-vars
|
||||||
|
|
||||||
# Ignore database config
|
# Ignore database config
|
||||||
/config/database.yml
|
/config/database.yml
|
||||||
|
|
||||||
|
# Ignore coverage stuff
|
||||||
|
/coverage
|
||||||
|
|
||||||
# Ignore the default SQLite database.
|
# Ignore the default SQLite database.
|
||||||
/db/*.sqlite3
|
/db/*.sqlite3
|
||||||
|
|
||||||
# Ignore local changes to schema.rb (e. g. through extentions)
|
# Ignore local changes to schema.rb (e. g. through extentions)
|
||||||
/db/schema.rb
|
/db/schema.rb
|
||||||
|
|
||||||
|
# Ignore custom gem file
|
||||||
|
/Gemfile.local
|
||||||
|
|
||||||
|
# Ignore node modules
|
||||||
|
/node_modules
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
/log
|
/log
|
||||||
/tmp/*
|
|
||||||
/tmp/pids/*
|
|
||||||
/public/assets/*.*
|
/public/assets/*.*
|
||||||
/public/assets/app
|
/public/assets/app
|
||||||
/public/assets/custom
|
/public/assets/custom
|
||||||
|
/tmp/*
|
||||||
|
/tmp/pids/*
|
||||||
|
|
||||||
# except /tmp/pids/ which is needed for certain Zammad processes
|
# except /tmp/pids/ which is needed for certain Zammad processes
|
||||||
!/tmp
|
!/tmp
|
||||||
!/tmp/pids
|
!/tmp/pids
|
||||||
!/tmp/pids/.keep
|
!/tmp/pids/.keep
|
||||||
|
|
||||||
# Ignore custom gem file
|
|
||||||
/Gemfile.local
|
|
||||||
|
|
||||||
# Ignore .project files
|
|
||||||
/.project
|
|
||||||
|
|
||||||
# Ignore local database settings
|
|
||||||
/config/database.yml
|
|
||||||
|
|
||||||
# Ignore mac stuff
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Ignore .swp files
|
|
||||||
.*.swp
|
|
||||||
|
|
||||||
# Ignore coverage stuff
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# Ignore Rubymine config
|
|
||||||
/.idea
|
|
||||||
node_modules
|
|
||||||
|
|
4
Gemfile
4
Gemfile
|
@ -12,6 +12,7 @@ gem 'activerecord-session_store'
|
||||||
gem 'json'
|
gem 'json'
|
||||||
|
|
||||||
# Supported DBs
|
# Supported DBs
|
||||||
|
gem 'activerecord-nulldb-adapter', group: :nulldb
|
||||||
gem 'mysql2', group: :mysql
|
gem 'mysql2', group: :mysql
|
||||||
gem 'pg', group: :postgres
|
gem 'pg', group: :postgres
|
||||||
|
|
||||||
|
@ -113,7 +114,8 @@ group :development, :test do
|
||||||
gem 'github_changelog_generator'
|
gem 'github_changelog_generator'
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'puma'
|
gem 'puma', group: :puma
|
||||||
|
gem 'unicorn', group: :unicorn
|
||||||
|
|
||||||
# load onw gem's
|
# load onw gem's
|
||||||
local_gemfile = File.join(File.dirname(__FILE__), 'Gemfile.local')
|
local_gemfile = File.join(File.dirname(__FILE__), 'Gemfile.local')
|
||||||
|
|
|
@ -30,6 +30,8 @@ GEM
|
||||||
activemodel (= 4.2.7.1)
|
activemodel (= 4.2.7.1)
|
||||||
activesupport (= 4.2.7.1)
|
activesupport (= 4.2.7.1)
|
||||||
arel (~> 6.0)
|
arel (~> 6.0)
|
||||||
|
activerecord-nulldb-adapter (0.3.6)
|
||||||
|
activerecord (>= 2.0.0)
|
||||||
activerecord-session_store (1.0.0)
|
activerecord-session_store (1.0.0)
|
||||||
actionpack (>= 4.0, < 5.1)
|
actionpack (>= 4.0, < 5.1)
|
||||||
activerecord (>= 4.0, < 5.1)
|
activerecord (>= 4.0, < 5.1)
|
||||||
|
@ -152,6 +154,7 @@ GEM
|
||||||
inflection (1.0.0)
|
inflection (1.0.0)
|
||||||
json (1.8.3)
|
json (1.8.3)
|
||||||
jwt (1.5.4)
|
jwt (1.5.4)
|
||||||
|
kgio (2.11.0)
|
||||||
koala (2.4.0)
|
koala (2.4.0)
|
||||||
addressable
|
addressable
|
||||||
faraday
|
faraday
|
||||||
|
@ -271,6 +274,7 @@ GEM
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.18.1, < 2.0)
|
thor (>= 0.18.1, < 2.0)
|
||||||
rainbow (2.1.0)
|
rainbow (2.1.0)
|
||||||
|
raindrops (0.17.0)
|
||||||
rake (11.2.2)
|
rake (11.2.2)
|
||||||
rb-fsevent (0.9.7)
|
rb-fsevent (0.9.7)
|
||||||
rb-inotify (0.9.7)
|
rb-inotify (0.9.7)
|
||||||
|
@ -371,6 +375,9 @@ GEM
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.7.2)
|
unf_ext (0.0.7.2)
|
||||||
unicode-display_width (1.1.1)
|
unicode-display_width (1.1.1)
|
||||||
|
unicorn (5.2.0)
|
||||||
|
kgio (~> 2.6)
|
||||||
|
raindrops (~> 0.7)
|
||||||
websocket (1.2.3)
|
websocket (1.2.3)
|
||||||
writeexcel (1.0.5)
|
writeexcel (1.0.5)
|
||||||
zendesk_api (1.14.0)
|
zendesk_api (1.14.0)
|
||||||
|
@ -385,6 +392,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
activerecord-nulldb-adapter
|
||||||
activerecord-session_store
|
activerecord-session_store
|
||||||
autoprefixer-rails
|
autoprefixer-rails
|
||||||
biz
|
biz
|
||||||
|
@ -447,6 +455,7 @@ DEPENDENCIES
|
||||||
therubyracer
|
therubyracer
|
||||||
twitter
|
twitter
|
||||||
uglifier
|
uglifier
|
||||||
|
unicorn
|
||||||
writeexcel
|
writeexcel
|
||||||
zendesk_api
|
zendesk_api
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,372 @@
|
||||||
class App.TicketOverview extends App.Controller
|
class App.TicketOverview extends App.Controller
|
||||||
className: 'overviews'
|
className: 'overviews'
|
||||||
activeFocus: 'nav'
|
activeFocus: 'nav'
|
||||||
|
mouse:
|
||||||
|
x: null
|
||||||
|
y: null
|
||||||
|
|
||||||
|
elements:
|
||||||
|
'.js-batch-overlay': 'batchOverlay'
|
||||||
|
'.js-batch-overlay-backdrop': 'batchOverlayBackdrop'
|
||||||
|
'.js-batch-cancel': 'batchCancel'
|
||||||
|
'.js-batch-macro-circle': 'batchMacroCircle'
|
||||||
|
'.js-batch-assign-circle': 'batchAssignCircle'
|
||||||
|
'.js-batch-assign': 'batchAssign'
|
||||||
|
'.js-batch-macro': 'batchMacro'
|
||||||
|
|
||||||
|
events:
|
||||||
|
'mousedown .item': 'startDragItem'
|
||||||
|
'mouseenter .js-batch-overlay-entry': 'highlightBatchEntry'
|
||||||
|
'mouseleave .js-batch-overlay-entry': 'unhighlightBatchEntry'
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
|
startDragItem: (event) ->
|
||||||
|
@grabbedItem = $(event.currentTarget)
|
||||||
|
offset = @grabbedItem.offset()
|
||||||
|
@batchDragger = $(App.view('ticket_overview/batch_dragger')())
|
||||||
|
@grabbedItemClone = @grabbedItem.clone()
|
||||||
|
@grabbedItemClone.data('offset', @grabbedItem.offset())
|
||||||
|
@grabbedItemClone.addClass('batch-dragger-item js-main-item')
|
||||||
|
@batchDragger.append @grabbedItemClone
|
||||||
|
|
||||||
|
@batchDragger.data
|
||||||
|
startX: event.pageX
|
||||||
|
startY: event.pageY
|
||||||
|
dx: Math.min(event.pageX - offset.left, 180)
|
||||||
|
dy: event.pageY - offset.top
|
||||||
|
moved: false
|
||||||
|
|
||||||
|
$(document).on 'mousemove.item', @dragItem
|
||||||
|
$(document).one 'mouseup.item', @endDragItem
|
||||||
|
# TODO: fire @cancelDrag on ESC
|
||||||
|
|
||||||
|
dragItem: (event) =>
|
||||||
|
event.preventDefault()
|
||||||
|
pos = @batchDragger.data()
|
||||||
|
threshold = 3
|
||||||
|
x = event.pageX - pos.dx
|
||||||
|
y = event.pageY - pos.dy
|
||||||
|
dir = if event.pageY > pos.startY then 1 else -1
|
||||||
|
|
||||||
|
if !pos.moved
|
||||||
|
if Math.abs(event.pageX - pos.startX) > threshold or Math.abs(event.pageY - pos.startY) > threshold
|
||||||
|
@batchDragger.data 'moved', true
|
||||||
|
# check grabbed items batch checkbox to make sure its checked
|
||||||
|
# (could be grabbed without checking the checkbox it)
|
||||||
|
@grabbedItemWasntChecked = !@grabbedItem.find('[name="bulk"]').prop('checked')
|
||||||
|
@grabbedItem.find('[name="bulk"]').prop('checked', true)
|
||||||
|
@grabbedItemClone.find('[name="bulk"]').prop('checked', true)
|
||||||
|
|
||||||
|
additionalItems = @el.find('[name="bulk"]:checked').parents('.item').not(@grabbedItem)
|
||||||
|
additionalItemsClones = additionalItems.clone()
|
||||||
|
@draggedItems = @grabbedItemClone.add(additionalItemsClones)
|
||||||
|
# store offsets for later use
|
||||||
|
additionalItemsClones.each (i, item) -> $(@).data('offset', additionalItems.eq(i).offset())
|
||||||
|
@batchDragger.prepend additionalItemsClones.addClass('batch-dragger-item').get().reverse()
|
||||||
|
if(additionalItemsClones.length)
|
||||||
|
@batchDragger.find('.js-batch-dragger-count').text(@draggedItems.length)
|
||||||
|
|
||||||
|
$('#app').append @batchDragger
|
||||||
|
|
||||||
|
@draggedItems.each (i, item) =>
|
||||||
|
dx = $(item).data('offset').left - $(item).offset().left - x
|
||||||
|
dy = $(item).data('offset').top - $(item).offset().top - y
|
||||||
|
$.Velocity.hook item, 'translateX', "#{dx}px"
|
||||||
|
$.Velocity.hook item, 'translateY', "#{dy}px"
|
||||||
|
|
||||||
|
@moveDraggedItems(-dir)
|
||||||
|
|
||||||
|
@mouseY = event.pageY
|
||||||
|
@showBatchOverlay()
|
||||||
|
else
|
||||||
|
return
|
||||||
|
|
||||||
|
$.Velocity.hook @batchDragger, 'translateX', "#{x}px"
|
||||||
|
$.Velocity.hook @batchDragger, 'translateY', "#{y}px"
|
||||||
|
|
||||||
|
endDragItem: (event) =>
|
||||||
|
$(document).off 'mousemove.item'
|
||||||
|
$(document).off 'mouseup.item'
|
||||||
|
pos = @batchDragger.data()
|
||||||
|
|
||||||
|
if !@hoveredBatchEntry
|
||||||
|
@cleanUpDrag()
|
||||||
|
return
|
||||||
|
|
||||||
|
$.Velocity.hook @batchDragger, 'transformOriginX', "#{pos.dx}px"
|
||||||
|
$.Velocity.hook @batchDragger, 'transformOriginY', "#{pos.dy}px"
|
||||||
|
@hoveredBatchEntry.velocity
|
||||||
|
properties:
|
||||||
|
scale: 1.1
|
||||||
|
options:
|
||||||
|
duration: 200
|
||||||
|
complete: =>
|
||||||
|
@hoveredBatchEntry.velocity "reverse",
|
||||||
|
duration: 200
|
||||||
|
complete: =>
|
||||||
|
# clean scale
|
||||||
|
@hoveredBatchEntry.removeAttr('style')
|
||||||
|
@cleanUpDrag(true)
|
||||||
|
@performBatchAction @hoveredBatchEntry.attr('data-action')
|
||||||
|
@hoveredBatchEntry = null
|
||||||
|
@batchDragger.velocity
|
||||||
|
properties:
|
||||||
|
scale: 0
|
||||||
|
options:
|
||||||
|
duration: 200
|
||||||
|
|
||||||
|
cancelDrag: ->
|
||||||
|
$(document).off 'mousemove.item'
|
||||||
|
$(document).off 'mouseup.item'
|
||||||
|
@cleanUpDrag()
|
||||||
|
|
||||||
|
cleanUpDrag: (success) ->
|
||||||
|
@hideBatchOverlay()
|
||||||
|
$('.batch-dragger').remove()
|
||||||
|
|
||||||
|
if @grabbedItemWasntChecked
|
||||||
|
@grabbedItem.find('[name="bulk"]').prop('checked', false)
|
||||||
|
|
||||||
|
if success
|
||||||
|
# uncheck all checked items
|
||||||
|
@el.find('[name="bulk"]:checked').prop('checked', false)
|
||||||
|
@el.find('[name="bulk_all"]').prop('checked', false)
|
||||||
|
|
||||||
|
moveDraggedItems: (dir) ->
|
||||||
|
@draggedItems.velocity
|
||||||
|
properties:
|
||||||
|
translateX: 0
|
||||||
|
translateY: (i) => dir * i * @batchDragger.height()/2
|
||||||
|
options:
|
||||||
|
easing: 'ease-in-out'
|
||||||
|
duration: 300
|
||||||
|
|
||||||
|
@batchDragger.find('.js-batch-dragger-count').velocity
|
||||||
|
properties:
|
||||||
|
translateY: if dir < 0 then 0 else -@batchDragger.height()+8
|
||||||
|
options:
|
||||||
|
easing: 'ease-in-out'
|
||||||
|
duration: 300
|
||||||
|
|
||||||
|
performBatchAction: (action) ->
|
||||||
|
console.log "perform action #{action} on checked items"
|
||||||
|
|
||||||
|
showBatchOverlay: ->
|
||||||
|
@batchOverlay.show()
|
||||||
|
@batchOverlayBackdrop.velocity { opacity: [1, 0] }, { duration: 500 }
|
||||||
|
@batchOverlayShown = true
|
||||||
|
$(document).on 'mousemove.batchoverlay', @controlBatchOverlay
|
||||||
|
|
||||||
|
hideBatchOverlay: ->
|
||||||
|
$(document).off 'mousemove.batchoverlay'
|
||||||
|
@batchOverlayShown = false
|
||||||
|
@batchOverlayBackdrop.velocity { opacity: [0, 1] }, { duration: 300, queue: false }
|
||||||
|
@hideBatchCircles =>
|
||||||
|
@batchOverlay.hide()
|
||||||
|
|
||||||
|
if @batchAssignShown
|
||||||
|
@hideBatchAssign()
|
||||||
|
|
||||||
|
if @batchMacroShown
|
||||||
|
@hideBatchMacro()
|
||||||
|
|
||||||
|
controlBatchOverlay: (event) =>
|
||||||
|
# store to detect if the mouse is hovering a drag-action entry
|
||||||
|
# after an animation ended -> @highlightBatchEntryAtMousePosition
|
||||||
|
@mouse.x = event.pageX
|
||||||
|
@mouse.y = event.pageY
|
||||||
|
|
||||||
|
if event.pageY <= window.innerHeight/5*2
|
||||||
|
mouseInArea = 'top'
|
||||||
|
else if event.pageY > window.innerHeight/5*2 && event.pageY <= window.innerHeight/5*3
|
||||||
|
mouseInArea = 'middle'
|
||||||
|
else
|
||||||
|
mouseInArea = 'bottom'
|
||||||
|
|
||||||
|
switch mouseInArea
|
||||||
|
when 'top'
|
||||||
|
if !@batchMacroShown
|
||||||
|
@hideBatchCircles()
|
||||||
|
@showBatchMacro()
|
||||||
|
@moveDraggedItems(1)
|
||||||
|
|
||||||
|
when 'middle'
|
||||||
|
if @batchAssignShown
|
||||||
|
@hideBatchAssign()
|
||||||
|
|
||||||
|
if @batchMacroShown
|
||||||
|
@hideBatchMacro()
|
||||||
|
|
||||||
|
if !@batchCirclesShown
|
||||||
|
@showBatchCircles()
|
||||||
|
|
||||||
|
when 'bottom'
|
||||||
|
if !@batchAssignShown
|
||||||
|
@hideBatchCircles()
|
||||||
|
@showBatchAssign()
|
||||||
|
@moveDraggedItems(-1)
|
||||||
|
|
||||||
|
showBatchCircles: ->
|
||||||
|
@batchCirclesShown = true
|
||||||
|
|
||||||
|
@batchMacroCircle.velocity
|
||||||
|
properties:
|
||||||
|
translateY: [0, '-150%']
|
||||||
|
opacity: [1, 0]
|
||||||
|
options:
|
||||||
|
easing: [1,-.55,.2,1.37]
|
||||||
|
duration: 500
|
||||||
|
visibility: 'visible'
|
||||||
|
delay: 200
|
||||||
|
|
||||||
|
@batchAssignCircle.velocity
|
||||||
|
properties:
|
||||||
|
translateY: [0, '150%']
|
||||||
|
opacity: [1, 0]
|
||||||
|
options:
|
||||||
|
easing: [1,-.55,.2,1.37]
|
||||||
|
duration: 500
|
||||||
|
visibility: 'visible'
|
||||||
|
delay: 200
|
||||||
|
|
||||||
|
hideBatchCircles: (callback) ->
|
||||||
|
@batchMacroCircle.velocity
|
||||||
|
properties:
|
||||||
|
translateY: ['-150%', 0]
|
||||||
|
opacity: [0, 1]
|
||||||
|
options:
|
||||||
|
duration: 300
|
||||||
|
visibility: 'hidden'
|
||||||
|
queue: false
|
||||||
|
|
||||||
|
@batchAssignCircle.velocity
|
||||||
|
properties:
|
||||||
|
translateY: ['150%', 0]
|
||||||
|
opacity: [0, 1]
|
||||||
|
options:
|
||||||
|
duration: 300
|
||||||
|
complete: callback
|
||||||
|
visibility: 'hidden'
|
||||||
|
queue: false
|
||||||
|
|
||||||
|
@batchCirclesShown = false
|
||||||
|
|
||||||
|
showBatchAssign: ->
|
||||||
|
return if !@batchOverlayShown # user might have dropped the item already
|
||||||
|
@batchAssignShown = true
|
||||||
|
|
||||||
|
@batchAssign.velocity
|
||||||
|
properties:
|
||||||
|
translateY: [0, '100%']
|
||||||
|
opacity: [1, 0]
|
||||||
|
options:
|
||||||
|
easing: [1,-.55,.2,1.37]
|
||||||
|
duration: 500
|
||||||
|
visibility: 'visible'
|
||||||
|
complete: @highlightBatchEntryAtMousePosition
|
||||||
|
delay: if @batchCirclesShown then 0 else 200
|
||||||
|
|
||||||
|
@batchCancel.css
|
||||||
|
top: 0
|
||||||
|
bottom: 'auto'
|
||||||
|
|
||||||
|
@batchCancel.velocity
|
||||||
|
properties:
|
||||||
|
translateY: [0, '100%']
|
||||||
|
opacity: [1, 0]
|
||||||
|
options:
|
||||||
|
easing: [1,-.55,.2,1.37]
|
||||||
|
duration: 500
|
||||||
|
visibility: 'visible'
|
||||||
|
delay: if @batchCirclesShown then 0 else 200
|
||||||
|
|
||||||
|
hideBatchAssign: ->
|
||||||
|
@batchAssign.velocity
|
||||||
|
properties:
|
||||||
|
translateY: ['100%', 0]
|
||||||
|
opacity: [0, 1]
|
||||||
|
options:
|
||||||
|
duration: 300
|
||||||
|
visibility: 'hidden'
|
||||||
|
queue: false
|
||||||
|
|
||||||
|
@batchCancel.velocity
|
||||||
|
properties:
|
||||||
|
translateY: ['100%', 0]
|
||||||
|
opacity: [0, 1]
|
||||||
|
options:
|
||||||
|
duration: 300
|
||||||
|
visibility: 'hidden'
|
||||||
|
queue: false
|
||||||
|
|
||||||
|
@batchAssignShown = false
|
||||||
|
|
||||||
|
showBatchMacro: ->
|
||||||
|
return if !@batchOverlayShown # user might have dropped the item already
|
||||||
|
@batchMacroShown = true
|
||||||
|
|
||||||
|
@batchMacro.velocity
|
||||||
|
properties:
|
||||||
|
translateY: [0, '-100%']
|
||||||
|
opacity: [1, 0]
|
||||||
|
options:
|
||||||
|
easing: [1,-.55,.2,1.37]
|
||||||
|
duration: 500
|
||||||
|
visibility: 'visible'
|
||||||
|
complete: @highlightBatchEntryAtMousePosition
|
||||||
|
delay: if @batchCirclesShown then 0 else 200
|
||||||
|
|
||||||
|
@batchCancel.css
|
||||||
|
top: 'auto'
|
||||||
|
bottom: 0
|
||||||
|
@batchCancel.velocity
|
||||||
|
properties:
|
||||||
|
translateY: [0, '-100%']
|
||||||
|
opacity: [1, 0]
|
||||||
|
options:
|
||||||
|
easing: [1,-.55,.2,1.37]
|
||||||
|
duration: 500
|
||||||
|
visibility: 'visible'
|
||||||
|
delay: if @batchCirclesShown then 0 else 200
|
||||||
|
|
||||||
|
hideBatchMacro: ->
|
||||||
|
@batchMacro.velocity
|
||||||
|
properties:
|
||||||
|
translateY: ['-100%', 0]
|
||||||
|
opacity: [0, 1]
|
||||||
|
options:
|
||||||
|
duration: 300
|
||||||
|
visibility: 'hidden'
|
||||||
|
queue: false
|
||||||
|
|
||||||
|
@batchCancel.velocity
|
||||||
|
properties:
|
||||||
|
translateY: ['-100%', 0]
|
||||||
|
opacity: [0, 1]
|
||||||
|
options:
|
||||||
|
duration: 300
|
||||||
|
visibility: 'hidden'
|
||||||
|
queue: false
|
||||||
|
|
||||||
|
@batchMacroShown = false
|
||||||
|
|
||||||
|
highlightBatchEntryAtMousePosition: =>
|
||||||
|
entryAtPoint = $(document.elementFromPoint(@mouse.x, @mouse.y)).closest('.js-batch-overlay-entry')
|
||||||
|
if(entryAtPoint.length)
|
||||||
|
@hoveredBatchEntry = entryAtPoint.addClass('is-hovered')
|
||||||
|
|
||||||
|
highlightBatchEntry: (event) ->
|
||||||
|
@hoveredBatchEntry = $(event.currentTarget).addClass('is-hovered')
|
||||||
|
|
||||||
|
unhighlightBatchEntry: (event) ->
|
||||||
|
@hoveredBatchEntry = null
|
||||||
|
$(event.currentTarget).removeClass('is-hovered')
|
||||||
|
|
||||||
render: ->
|
render: ->
|
||||||
elLocal = $(App.view('ticket_overview')())
|
elLocal = $(App.view('ticket_overview/index')())
|
||||||
|
|
||||||
@navBarControllerVertical = new Navbar
|
@navBarControllerVertical = new Navbar
|
||||||
el: elLocal.find('.overview-header')
|
el: elLocal.find('.overview-header')
|
||||||
|
@ -397,10 +756,10 @@ class Table extends App.Controller
|
||||||
)
|
)
|
||||||
table = $(table)
|
table = $(table)
|
||||||
table.delegate('[name="bulk_all"]', 'click', (e) ->
|
table.delegate('[name="bulk_all"]', 'click', (e) ->
|
||||||
if $(e.target).attr('checked')
|
if $(e.currentTarget).prop('checked')
|
||||||
$(e.target).closest('table').find('[name="bulk"]').attr('checked', true)
|
$(e.currentTarget).closest('table').find('[name="bulk"]').prop('checked', true)
|
||||||
else
|
else
|
||||||
$(e.target).closest('table').find('[name="bulk"]').attr('checked', false)
|
$(e.currentTarget).closest('table').find('[name="bulk"]').prop('checked', false)
|
||||||
)
|
)
|
||||||
@$('.table-overview').append(table)
|
@$('.table-overview').append(table)
|
||||||
else
|
else
|
||||||
|
@ -440,6 +799,25 @@ class Table extends App.Controller
|
||||||
@bulkForm.hide()
|
@bulkForm.hide()
|
||||||
else
|
else
|
||||||
@bulkForm.show()
|
@bulkForm.show()
|
||||||
|
|
||||||
|
if @lastChecked && e.shiftKey
|
||||||
|
# check items in a row
|
||||||
|
currentItem = $(e.currentTarget).parents('.item')
|
||||||
|
lastCheckedItem = @lastChecked.parents('.item')
|
||||||
|
items = currentItem.parent().children()
|
||||||
|
|
||||||
|
if currentItem.index() > lastCheckedItem.index()
|
||||||
|
# current item is below last checked item
|
||||||
|
startId = lastCheckedItem.index()
|
||||||
|
endId = currentItem.index()
|
||||||
|
else
|
||||||
|
# current item is above last checked item
|
||||||
|
startId = currentItem.index()
|
||||||
|
endId = lastCheckedItem.index()
|
||||||
|
|
||||||
|
items.slice(startId+1, endId).find('[name="bulk"]').prop('checked', (-> !@checked))
|
||||||
|
|
||||||
|
@lastChecked = $(e.currentTarget)
|
||||||
callbackIconHeader = (headers) ->
|
callbackIconHeader = (headers) ->
|
||||||
attribute =
|
attribute =
|
||||||
name: 'icon'
|
name: 'icon'
|
||||||
|
@ -523,8 +901,8 @@ class Table extends App.Controller
|
||||||
|
|
||||||
# deselect bulk_all if one item is uncheck observ
|
# deselect bulk_all if one item is uncheck observ
|
||||||
@$('.table-overview').delegate('[name="bulk"]', 'click', (e) ->
|
@$('.table-overview').delegate('[name="bulk"]', 'click', (e) ->
|
||||||
if !$(e.target).attr('checked')
|
if !$(e.target).prop('checked')
|
||||||
$(e.target).parents().find('[name="bulk_all"]').attr('checked', false)
|
$(e.target).parents().find('[name="bulk_all"]').prop('checked', false)
|
||||||
)
|
)
|
||||||
|
|
||||||
getSelected: ->
|
getSelected: ->
|
||||||
|
@ -540,7 +918,7 @@ class Table extends App.Controller
|
||||||
ticketId = $(element).val()
|
ticketId = $(element).val()
|
||||||
for ticketIdSelected in ticketIDs
|
for ticketIdSelected in ticketIDs
|
||||||
if ticketIdSelected is ticketId
|
if ticketIdSelected is ticketId
|
||||||
$(element).attr('checked', true)
|
$(element).prop('checked', true)
|
||||||
)
|
)
|
||||||
|
|
||||||
viewmode: (e) =>
|
viewmode: (e) =>
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<div class="sidebar"></div>
|
|
||||||
<div class="main flex">
|
|
||||||
<div class="overview-header"></div>
|
|
||||||
<div class="overview-table"></div>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="batch-dragger zIndex-10">
|
||||||
|
<div class="batch-dragger-counter js-batch-dragger-count"></div>
|
||||||
|
</div>
|
|
@ -0,0 +1,50 @@
|
||||||
|
<div class="sidebar"></div>
|
||||||
|
<div class="main flex">
|
||||||
|
<div class="overview-header"></div>
|
||||||
|
<div class="overview-table"></div>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay js-batch-overlay">
|
||||||
|
<div class="batch-overlay-backdrop js-batch-overlay-backdrop"></div>
|
||||||
|
<div class="batch-overlay-cancel js-batch-cancel">
|
||||||
|
<%- @T('drag here to cancel') %>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-circle batch-overlay-circle--top js-batch-macro-circle">
|
||||||
|
<div class="batch-overlay-circle-label"><%- @T('run macro') %></div>
|
||||||
|
<%- @Icon('arrow-up') %>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-circle batch-overlay-circle--bottom js-batch-assign-circle">
|
||||||
|
<%- @Icon('arrow-down') %>
|
||||||
|
<div class="batch-overlay-circle-label"><%- @T('assign tickets') %></div>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-assign batch-overlay-box js-batch-assign">
|
||||||
|
<div class="batch-overlay-assign-entry js-batch-overlay-entry" data-action="assign">
|
||||||
|
<span class="avatar size-80 avatar--unique" style="background-position: -100px -150px;">HH</span>
|
||||||
|
<div class="batch-overlay-assign-entry-name">Hans Huber</div>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-assign-entry js-batch-overlay-entry" data-action="assign">
|
||||||
|
<span class="avatar avatar--organization size-80 avatar--unique" style="background-position: -200px -60px;">
|
||||||
|
<%- @Icon('organization') %>
|
||||||
|
</span>
|
||||||
|
<div class="batch-overlay-assign-entry-name">Zammad</div>
|
||||||
|
<div class="batch-overlay-assign-entry-detail">3 Personen</div>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-assign-entry js-batch-overlay-entry" data-action="assign">
|
||||||
|
<span class="avatar size-80 avatar--unique" style="background-position: -30px -10px;">FD</span>
|
||||||
|
<div class="batch-overlay-assign-entry-name">Felicity Dickens</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-macro batch-overlay-box js-batch-macro">
|
||||||
|
<div class="batch-overlay-macro-entry js-batch-overlay-entry" data-action="macro">
|
||||||
|
<div class="batch-overlay-macro-entry-name">Close</div>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-macro-entry js-batch-overlay-entry" data-action="macro">
|
||||||
|
<div class="batch-overlay-macro-entry-name">Close & Tag as Spam</div>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-macro-entry js-batch-overlay-entry" data-action="macro">
|
||||||
|
<div class="batch-overlay-macro-entry-name">Close & Reply we're on Holidays</div>
|
||||||
|
</div>
|
||||||
|
<div class="batch-overlay-macro-entry js-batch-overlay-entry" data-action="macro">
|
||||||
|
<div class="batch-overlay-macro-entry-name">Escalate to 2nd level</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -3580,6 +3580,23 @@ footer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--organization {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.icon-organization {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.size-80 {
|
||||||
|
.icon-organization {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.icon-logo {
|
.icon-logo {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -8321,6 +8338,207 @@ output {
|
||||||
margin: 20px 0 32px;
|
margin: 20px 0 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.batch-overlay {
|
||||||
|
@extend .fit;
|
||||||
|
z-index: 1;
|
||||||
|
color: white;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 0.07em;
|
||||||
|
font-size: 0.95em;
|
||||||
|
line-height: 1.3;
|
||||||
|
display: none;
|
||||||
|
will-change: display;
|
||||||
|
cursor: grabbing;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-backdrop {
|
||||||
|
@extend .fit;
|
||||||
|
background: hsla(231,20%,8%,.8);
|
||||||
|
opacity: 0;
|
||||||
|
will-change: opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-circle {
|
||||||
|
margin: 35px auto;
|
||||||
|
background: hsl(207,7%,29%);
|
||||||
|
border-radius: 100%;
|
||||||
|
border: 4px solid white;
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
padding: 20px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
visibility: hidden;
|
||||||
|
|
||||||
|
&--top {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--bottom {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: currentColor;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
width: 50%;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-cancel {
|
||||||
|
background: hsla(0,0%,100%,.21);
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 2px dashed hsla(0,0%,100%,.3);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 28px;
|
||||||
|
margin: 50px 200px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
will-change: opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-box {
|
||||||
|
background: hsl(232,9%,17%);
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 100%;
|
||||||
|
padding: 37px 25px;
|
||||||
|
position: absolute;
|
||||||
|
visibility: hidden;
|
||||||
|
will-change: opacity, transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-assign {
|
||||||
|
padding-bottom: 87px; // 37px + 50px
|
||||||
|
bottom: -50px; // extra space for bounce animation
|
||||||
|
|
||||||
|
&-entry {
|
||||||
|
padding: 13px;
|
||||||
|
|
||||||
|
&.is-hovered {
|
||||||
|
.avatar {
|
||||||
|
border-color: $highlight-color;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
border: 4px solid hsl(231,5%,30%);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
transition: transform 120ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-detail {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-macro {
|
||||||
|
padding-top: 87px; // 37px + 50px
|
||||||
|
top: -50px; // extra space for bounce animation
|
||||||
|
|
||||||
|
&-entry {
|
||||||
|
margin: 13px;
|
||||||
|
border: 4px solid hsl(231,5%,30%);
|
||||||
|
background: hsl(233,9%,24%);
|
||||||
|
border-radius: 100%;
|
||||||
|
height: 120px;
|
||||||
|
width: 120px;
|
||||||
|
padding: 13px 13px 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.9em;
|
||||||
|
|
||||||
|
&.is-hovered {
|
||||||
|
border-color: $highlight-color;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-dragger {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
width: 250px;
|
||||||
|
height: 40px;
|
||||||
|
will-change: transform;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: hsl(200,100%,91%);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 11px 0 9px 11px;
|
||||||
|
box-shadow: 0 0 10px hsla(0,0%,0%,.28);
|
||||||
|
will-change: transform;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
display: block;
|
||||||
|
padding: 0 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:nth-child(3) {
|
||||||
|
flex-shrink: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(n+4) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-counter {
|
||||||
|
position: absolute;
|
||||||
|
right: -8px;
|
||||||
|
bottom: -8px;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
border-radius: 99px;
|
||||||
|
z-index: 1;
|
||||||
|
color: white;
|
||||||
|
background: $highlight-color;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 0 10px hsla(0,0%,0%,.28);
|
||||||
|
will-change: transform;
|
||||||
|
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
----------------
|
----------------
|
||||||
|
|
27
config/unicorn.rb
Normal file
27
config/unicorn.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
worker_processes 4
|
||||||
|
timeout 30
|
||||||
|
stderr_path 'log/unicorn_error.log'
|
||||||
|
stdout_path 'log/unicorn_access.log'
|
||||||
|
pid 'tmp/pids/unicorn.pid'
|
||||||
|
|
||||||
|
before_fork do |server, _worker|
|
||||||
|
##
|
||||||
|
# When sent a USR2, Unicorn will suffix its pidfile with .oldbin and
|
||||||
|
# immediately start loading up a new version of itself (loaded with a new
|
||||||
|
# version of our app). When this new Unicorn is completely loaded
|
||||||
|
# it will begin spawning workers. The first worker spawned will check to
|
||||||
|
# see if an .oldbin pidfile exists. If so, this means we've just booted up
|
||||||
|
# a new Unicorn and need to tell the old one that it can now die. To do so
|
||||||
|
# we send it a QUIT.
|
||||||
|
#
|
||||||
|
# Using this method we get 0 downtime deploys.
|
||||||
|
|
||||||
|
old_pid = 'tmp/pids/unicorn.pid.oldbin'
|
||||||
|
if File.exist?(old_pid) && server.pid != old_pid
|
||||||
|
begin
|
||||||
|
Process.kill('QUIT', File.read(old_pid).to_i)
|
||||||
|
rescue Errno::ENOENT, Errno::ESRCH
|
||||||
|
logger.info 'Unicorn master already killed. Someone else did our job for us.'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue