Skip to content
1 change: 1 addition & 0 deletions app/assets/javascripts/application.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#= require nested_form_fields
#= require modal_create
#= require datepicker
#= require header
#= require worktimes
#= require plannings
#= require plannings_panel
Expand Down
16 changes: 16 additions & 0 deletions app/assets/javascripts/header.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2006-2017, Puzzle ITC GmbH. This file is part of
# PuzzleTime and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/puzzle/puzzletime.

do ->
# are these key codes? :)
kmi_sequence = [38,38,40,40,37,39,37,39,66,65];
kmi_input = []

document.addEventListener 'keydown', (e) ->
kmi_input.push(e.keyCode)
kmi_input.shift() while kmi_input.length > kmi_sequence.length

if kmi_input.toString() is kmi_sequence.toString()
document.getElementById('navbar-app-title').classList.add('rainbow')
73 changes: 73 additions & 0 deletions app/assets/javascripts/modules/selection_watcher.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright (c) 2006-2017, Puzzle ITC GmbH. This file is part of
# PuzzleTime and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/puzzle/puzzletime.


app = window.App ||= {}

class app.SelectionWatcherTrigger
constructor: (event, watchSelectors..., observedClass) ->
@event = event
@watchedElements = watchSelectors.join(', ')
@observedClass = observedClass

class app.SelectionWatcherAction
constructor: (url) ->
@url = url

# Update Form by running AJAX request when event fires on watched elements
class app.SelectionWatcher
constructor: (trigger, actions...) ->
@trigger = trigger
@actions = actions

@_bind()

_bindClassObserver: ->
observer = new MutationObserver (mutationsList) =>
for mutation in mutationsList
return unless mutation.type is 'attributes' and mutation.attributeName is 'class'

el = mutation.target
if $(el).is(@trigger.watchedElements)
console.log('fire action (class change)')
@_runActionsWithSerializedClass()
break # avoid firing multiple times per batch

observer.observe document.body,
attributes: true
subtree: true
attributeFilter: ['class']

_bind: ->
if @trigger.event in ['classChange']
@_bindClassObserver()
else
# unbind action before binding, as else we might
# run into problems with turbolinks caching
$(document).off(@trigger.event, @trigger.watchedElements)

# use a promise chain to sequentially execute actions
$(document).on @trigger.event, @trigger.watchedElements, (event) =>
console.log('fire action')
@_runActionsWithSerializedClass()

_runActionsWithSerializedClass: ->
query = @_serializeClassMatches()
@actions.reduce (promise, action) ->
promise.then ->
new Promise (resolve, reject) ->
$.getScript("#{action.url}?#{query}")
.done(resolve)
.fail(reject)
, Promise.resolve()

_serializeClassMatches: ->
classSelector = ".#{@trigger.observedClass}"
matching = $(@trigger.watchedElements).filter(classSelector)

cellIds = matching.map (i, el) -> $(el).data('id')
cellIds = $.makeArray(cellIds).filter (id) -> id?

$.param({ 'cell_ids[]': cellIds })
13 changes: 13 additions & 0 deletions app/assets/stylesheets/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,16 @@
#breadcrumb {
margin-top: $line-height-computed;
}

@keyframes rainbowText {
0% { color: red; }
20% { color: orange; }
40% { color: yellow; }
60% { color: green; }
80% { color: blue; }
100% { color: violet; }
}

.rainbow {
animation: rainbowText 2s infinite;
}
13 changes: 13 additions & 0 deletions app/controllers/order_plannings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@
class OrderPlanningsController < Plannings::OrdersController
skip_load_and_authorize_resource

# AJAX
def preview_total_selected
cell_ids = params[:cell_ids] || []
Rails.logger.info("cell_ids: #{cell_ids.inspect}")
Rails.logger.info("params: #{params.inspect}")
@cells = Planning.where(id: cell_ids)
@total_selected_hours = @cells.sum(&:percent) * 8 / 100.0

respond_to do |format|
format.js
end
end

private

def order
Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/_headerbar.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#headerbar.navbar.navbar-default{role: 'navigation', class: ('lonely' unless @user)}
.container-fluid
.navbar-header
.navbar-brand
.navbar-brand#navbar-app-title
= image_tag 'logo.svg', size: '41x40'
PuzzleTime
= link_to Puzzletime.version, Puzzletime.changelog_url, class: 'version'
Expand Down
3 changes: 2 additions & 1 deletion app/views/plannings/base/_header.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
%header.planning-board-header
%h1.h3= @board.caption
.planned{ id: "planned_#{dom_id(@board.subject)}" }
= render 'header_planned'
= render 'header_planned' if !multi
= render 'multi_header_planned' if multi
= render 'date_filter' if filter
17 changes: 16 additions & 1 deletion app/views/plannings/base/_header_planned.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,23 @@
Stunden verplant
.header-planned-row.inperiod-sum
.header-planned-prefix
Ausgewählter Zeitbereich:
Durch Filter angezeigter Zeitbereich:
.header-planned-amount
= format_number(@board.total_planned_hours(true), 0)
.header-planned-postfix
Stunden verplant
.header-planned-row.selected-sum
.header-planned-prefix
Selektierter Zeitbereich:
.header-planned-amount
= render 'preview_total_selected'
.header-planned-postfix
Stunden verplant


- content_for(:javascript) do
var recalculate_total_selected_amount_trigger = new window.App.SelectionWatcherTrigger('classChange', '.day', 'ui-selected')

var calculate_total_action = new window.App.SelectionWatcherAction("#{preview_total_selected_order_order_plannings_path(order_id: @board.order.id, format: :js)}")

new window.App.SelectionWatcher(recalculate_total_selected_amount_trigger, calculate_total_action);
20 changes: 20 additions & 0 deletions app/views/plannings/base/_multi_header_planned.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-# Copyright (c) 2006-2017, Puzzle ITC GmbH. This file is part of
-# PuzzleTime and licensed under the Affero General Public License version 3
-# or later. See the COPYING file at the top-level directory or at
-# https://github.com/puzzle/puzzletime.

.header-planned-container
.header-planned-row.total-sum
.header-planned-prefix
Total:
.header-planned-amount
= format_number(@board.total_planned_hours, 0) + ' / ' + format_number(@board.total_plannable_hours, 0)
.header-planned-postfix
Stunden verplant
.header-planned-row.inperiod-sum
.header-planned-prefix
Durch Filter angezeigter Zeitbereich:
.header-planned-amount
= format_number(@board.total_planned_hours(true), 0)
.header-planned-postfix
Stunden verplant
2 changes: 1 addition & 1 deletion app/views/plannings/base/_show_multi.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#boards.layout-column
- @boards.each_with_index do |board, i|
- @board = board
= render 'header', filter: i == 0
= render 'header', filter: i == 0, multi: true

.planning-board{ id: "board_#{dom_id(board.subject)}" }
= render 'board'
Expand Down
2 changes: 1 addition & 1 deletion app/views/plannings/base/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


- content_for(:content_header) do
= render 'header', filter: true
= render 'header', filter: true, multi: false

.planning-board
= render 'board'
Expand Down
11 changes: 11 additions & 0 deletions app/views/plannings/employees/_multi_header_planned.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-# Copyright (c) 2006-2017, Puzzle ITC GmbH. This file is part of
-# PuzzleTime and licensed under the Affero General Public License version 3
-# or later. See the COPYING file at the top-level directory or at
-# https://github.com/puzzle/puzzletime.


= with_tooltip("#{format_number(@board.total_planned_hours, 0)}h (inkl. Abwesenheiten) von #{format_number(@board.total_plannable_hours, 0)}h (ohne Feiertage)") do
= format_number(@board.total_planned_hours, 0)
von
= format_number(@board.total_plannable_hours, 0)
Stunden verplant
2 changes: 2 additions & 0 deletions app/views/plannings/orders/_preview_total_selected.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#plannings-total-selected-amount
= @total_selected_hours
1 change: 1 addition & 0 deletions app/views/plannings/orders/preview_total_selected.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$("#plannings-total-selected-amount").html('#{j(format_number(@total_selected_hours, 2))}');
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@

resource :order_plannings, only: %i[index show update destroy] do
get 'new', on: :member, as: 'new'
collection do
get :preview_total_selected
end
end

resource :completed, only: %i[edit update], controller: 'orders/completed'
Expand Down
21 changes: 21 additions & 0 deletions test/integration/plannings_orders_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,27 @@ class PlanningsOrdersTest < ActionDispatch::IntegrationTest
text: '14')
end

test 'dragging over entries correctly sets the worktime in hours of the selected area' do
assert_percents ['50', '', '', '', '', ''], row_mark
assert_percents ['25', '', '', '', '', ''], row_pascal

page.assert_selector("#planned_order_#{orders(:puzzletime).id} .selected-sum .header-planned-amount", text: '0.00')

drag(row_mark.all('.day')[0], row_mark.all('.day')[2])

page.assert_selector('.-selected', count: 3)
page.assert_selector("#planned_order_#{orders(:puzzletime).id} .selected-sum .header-planned-amount", text: '4.00')
find('.planning-cancel').click

drag(row_mark.all('.day')[0], row_pascal.all('.day')[0])

page.assert_selector('.-selected', count: 2)
page.assert_selector("#planned_order_#{orders(:puzzletime).id} .selected-sum .header-planned-amount", text: '6.00')
find('.planning-cancel').click

page.assert_selector("#planned_order_#{orders(:puzzletime).id} .selected-sum .header-planned-amount", text: '0.00')
end

private

def workdays_next_n_months(n, date = Time.zone.today)
Expand Down