Add ability to edit employee time sheet
Also disable some unwanted autocomplete logic, plus add ability to prevent autocomplete "change click" event
This commit is contained in:
parent
2e88cdde88
commit
7104e275c3
11 changed files with 467 additions and 302 deletions
|
@ -1,6 +1,6 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## TODO: This function signature is getting out of hand...
|
||||
<%def name="autocomplete(field_name, service_url, field_value=None, field_display=None, width='300px', select=None, selected=None, cleared=None, options={})">
|
||||
<%def name="autocomplete(field_name, service_url, field_value=None, field_display=None, width='300px', select=None, selected=None, cleared=None, change_clicked=None, options={})">
|
||||
<div id="${field_name}-container" class="autocomplete-container">
|
||||
${h.hidden(field_name, id=field_name, value=field_value)}
|
||||
${h.text(field_name+'-textbox', id=field_name+'-textbox', value=field_display,
|
||||
|
@ -37,6 +37,11 @@
|
|||
% endif
|
||||
});
|
||||
$('#${field_name}-change').click(function() {
|
||||
% if change_clicked:
|
||||
if (! ${change_clicked}()) {
|
||||
return false;
|
||||
}
|
||||
% endif
|
||||
$('#${field_name}').val('');
|
||||
$('#${field_name}-display').hide();
|
||||
with ($('#${field_name}-textbox')) {
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
|
||||
<%def name="title()">${page_title}</%def>
|
||||
|
||||
<%def name="head_tags()">
|
||||
${parent.head_tags()}
|
||||
${h.stylesheet_link(request.static_url('tailbone:static/css/timesheet.css'))}
|
||||
<%def name="extra_javascript()">
|
||||
${parent.extra_javascript()}
|
||||
<script type="text/javascript">
|
||||
|
||||
var data_modified = false;
|
||||
|
@ -82,6 +81,55 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
${h.stylesheet_link(request.static_url('tailbone:static/css/timesheet.css'))}
|
||||
</%def>
|
||||
|
||||
<%def name="edit_timetable_javascript()">
|
||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.edit-shifts.js'))}
|
||||
<script type="text/javascript">
|
||||
|
||||
var weekdays = [
|
||||
% for i, day in enumerate(weekdays, 1):
|
||||
'${day.strftime('%a %d %b %Y')}'${',' if i < len(weekdays) else ''}
|
||||
% endfor
|
||||
];
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="edit_timetable_styles()">
|
||||
<style type="text/css">
|
||||
.timesheet .day {
|
||||
cursor: pointer;
|
||||
height: 5em;
|
||||
}
|
||||
.timesheet tr .day.modified {
|
||||
background-color: #fcc;
|
||||
}
|
||||
.timesheet tr:nth-child(odd) .day.modified {
|
||||
background-color: #ebb;
|
||||
}
|
||||
.timesheet .day .shift.deleted {
|
||||
display: none;
|
||||
}
|
||||
#day-editor .shift {
|
||||
margin-bottom: 1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#day-editor .shift input {
|
||||
width: 6em;
|
||||
}
|
||||
#day-editor .shift button {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
#snippets {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
<%def name="context_menu()"></%def>
|
||||
|
||||
<%def name="render_day(day)">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
<%namespace file="/autocomplete.mako" import="autocomplete" />
|
||||
|
||||
<%def name="timesheet_wrapper(edit_form=None, edit_tools=None, context_menu=None, render_day=None)">
|
||||
<%def name="timesheet_wrapper(edit_form=None, edit_tools=None, context_menu=None, render_day=None, change_employee=None)">
|
||||
<div class="timesheet-wrapper">
|
||||
|
||||
${form.begin(id='filter-form')}
|
||||
|
@ -21,7 +21,8 @@
|
|||
${autocomplete('employee', url('employees.autocomplete'),
|
||||
field_value=employee.uuid if employee else None,
|
||||
field_display=unicode(employee or ''),
|
||||
selected='employee_selected')}
|
||||
selected='employee_selected',
|
||||
change_clicked=change_employee)}
|
||||
% else:
|
||||
${form.hidden('employee', value=employee.uuid)}
|
||||
${employee}
|
||||
|
@ -111,7 +112,10 @@
|
|||
<tbody>
|
||||
% for emp in sorted(employees, key=unicode):
|
||||
<tr data-employee-uuid="${emp.uuid}">
|
||||
<td class="employee">${emp}</td>
|
||||
<td class="employee">
|
||||
## TODO: add link to single employee schedule / timesheet here...
|
||||
${emp}
|
||||
</td>
|
||||
% for day in emp.weekdays:
|
||||
<td class="day">
|
||||
% if render_day:
|
||||
|
|
|
@ -2,195 +2,13 @@
|
|||
<%inherit file="/shifts/base.mako" />
|
||||
<%namespace file="/shifts/lib.mako" import="timesheet_wrapper" />
|
||||
|
||||
<%def name="head_tags()">
|
||||
${parent.head_tags()}
|
||||
<%def name="extra_javascript()">
|
||||
${parent.extra_javascript()}
|
||||
${self.edit_timetable_javascript()}
|
||||
<script type="text/javascript">
|
||||
|
||||
var weekdays = [
|
||||
% for i, day in enumerate(weekdays, 1):
|
||||
'${day.strftime('%a %d %b %Y')}'${',' if i < len(weekdays) else ''}
|
||||
% endfor
|
||||
];
|
||||
|
||||
var editing_day = null;
|
||||
var new_shift_id = 1;
|
||||
|
||||
function add_shift(focus, uuid, start_time, end_time) {
|
||||
var shift = $('#snippets .shift').clone();
|
||||
if (! uuid) {
|
||||
uuid = 'new-' + (new_shift_id++).toString();
|
||||
}
|
||||
shift.attr('data-uuid', uuid);
|
||||
shift.children('input').each(function() {
|
||||
var name = $(this).attr('name') + '-' + uuid;
|
||||
$(this).attr('name', name);
|
||||
$(this).attr('id', name);
|
||||
});
|
||||
shift.children('input[name|="edit_start_time"]').val(start_time || '');
|
||||
shift.children('input[name|="edit_end_time"]').val(end_time || '');
|
||||
$('#day-editor .shifts').append(shift);
|
||||
shift.children('input').timepicker({showPeriod: true});
|
||||
if (focus) {
|
||||
shift.children('input:first').focus();
|
||||
}
|
||||
}
|
||||
|
||||
function calc_minutes(start_time, end_time) {
|
||||
var start = parseTime(start_time);
|
||||
start = new Date(2000, 0, 1, start.hh, start.mm);
|
||||
var end = parseTime(end_time);
|
||||
end = new Date(2000, 0, 1, end.hh, end.mm);
|
||||
return Math.floor((end - start) / 1000 / 60);
|
||||
}
|
||||
|
||||
function format_minutes(minutes) {
|
||||
var hours = Math.floor(minutes / 60);
|
||||
if (hours) {
|
||||
minutes -= hours * 60;
|
||||
}
|
||||
return hours.toString() + ':' + (minutes < 10 ? '0' : '') + minutes.toString();
|
||||
}
|
||||
|
||||
// stolen from http://stackoverflow.com/a/1788084
|
||||
function parseTime(s) {
|
||||
var part = s.match(/(\d+):(\d+)(?: )?(am|pm)?/i);
|
||||
var hh = parseInt(part[1], 10);
|
||||
var mm = parseInt(part[2], 10);
|
||||
var ap = part[3] ? part[3].toUpperCase() : null;
|
||||
if (ap == 'AM') {
|
||||
if (hh == 12) {
|
||||
hh = 0;
|
||||
}
|
||||
} else if (ap == 'PM') {
|
||||
if (hh != 12) {
|
||||
hh += 12;
|
||||
}
|
||||
}
|
||||
return { hh: hh, mm: mm };
|
||||
}
|
||||
|
||||
function time_input(shift, type) {
|
||||
var input = shift.children('input[name|="' + type + '_time"]');
|
||||
if (! input.length) {
|
||||
input = $('<input type="hidden" name="' + type + '_time-' + shift.data('uuid') + '" />');
|
||||
shift.append(input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
function update_row_hours(row) {
|
||||
var minutes = 0;
|
||||
row.find('.day .shift:not(.deleted)').each(function() {
|
||||
var time_range = $.trim($(this).children('span').text()).split(' - ');
|
||||
minutes += calc_minutes(time_range[0], time_range[1]);
|
||||
});
|
||||
row.children('.total').text(minutes ? format_minutes(minutes) : '0');
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
||||
$('.timesheet').on('click', '.day', function() {
|
||||
editing_day = $(this);
|
||||
var editor = $('#day-editor');
|
||||
var employee = editing_day.siblings('.employee').text();
|
||||
var date = weekdays[editing_day.get(0).cellIndex - 1];
|
||||
var shifts = editor.children('.shifts');
|
||||
shifts.empty();
|
||||
editing_day.children('.shift:not(.deleted)').each(function() {
|
||||
var uuid = $(this).data('uuid');
|
||||
var time_range = $.trim($(this).children('span').text()).split(' - ');
|
||||
add_shift(false, uuid, time_range[0], time_range[1]);
|
||||
});
|
||||
if (! shifts.children('.shift').length) {
|
||||
add_shift();
|
||||
}
|
||||
editor.dialog({
|
||||
modal: true,
|
||||
title: employee + ' - ' + date,
|
||||
position: {my: 'center', at: 'center', of: editing_day},
|
||||
width: 'auto',
|
||||
autoResize: true,
|
||||
buttons: [
|
||||
{
|
||||
text: "Update",
|
||||
click: function() {
|
||||
|
||||
// TODO: need to validate times here...
|
||||
|
||||
// create / update shifts in schedule table, as needed
|
||||
editor.find('.shifts .shift').each(function() {
|
||||
var uuid = $(this).data('uuid');
|
||||
var start_time = $(this).children('input[name|="edit_start_time"]').val();
|
||||
var end_time = $(this).children('input[name|="edit_end_time"]').val();
|
||||
var shift = editing_day.children('.shift[data-uuid="' + uuid + '"]');
|
||||
if (! shift.length) {
|
||||
shift = $('<p class="shift" data-uuid="' + uuid + '"><span></span></p>');
|
||||
shift.append($('<input type="hidden" name="employee_uuid-' + uuid + '" value="'
|
||||
+ editing_day.parents('tr:first').data('employee-uuid') + '" />'));
|
||||
## TODO: how to handle editing schedule w/ no store selected..?
|
||||
% if store:
|
||||
shift.append($('<input type="hidden" name="store_uuid-' + uuid + '" value="${store.uuid}" />'));
|
||||
% endif
|
||||
editing_day.append(shift);
|
||||
}
|
||||
shift.children('span').text(start_time + ' - ' + end_time);
|
||||
time_input(shift, 'start').val(date + ' ' + start_time);
|
||||
time_input(shift, 'end').val(date + ' ' + end_time);
|
||||
});
|
||||
|
||||
// remove shifts from schedule table, as needed
|
||||
editing_day.children('.shift').each(function() {
|
||||
var uuid = $(this).data('uuid');
|
||||
if (! editor.find('.shifts .shift[data-uuid="' + uuid + '"]').length) {
|
||||
if (uuid.match(/^new-/)) {
|
||||
$(this).remove();
|
||||
} else {
|
||||
$(this).addClass('deleted');
|
||||
$(this).append($('<input type="hidden" name="delete-' + uuid + '" value="delete" />'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// mark day as modified, close dialog
|
||||
editing_day.addClass('modified');
|
||||
$('.save-changes').button('enable');
|
||||
$('.undo-changes').button('enable');
|
||||
update_row_hours(editing_day.parents('tr:first'));
|
||||
editor.dialog('close');
|
||||
data_modified = true;
|
||||
okay_to_leave = false;
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
click: function() {
|
||||
editor.dialog('close');
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
$('#day-editor #add-shift').click(function() {
|
||||
add_shift(true);
|
||||
});
|
||||
|
||||
$('#day-editor').on('click', '.shifts button', function() {
|
||||
$(this).parents('.shift:first').remove();
|
||||
});
|
||||
|
||||
$('.save-changes').click(function() {
|
||||
$(this).button('disable').button('option', 'label', "Saving Changes...");
|
||||
okay_to_leave = true;
|
||||
$('#schedule-form').submit();
|
||||
});
|
||||
|
||||
$('.undo-changes').click(function() {
|
||||
$(this).button('disable').button('option', 'label', "Refreshing...");
|
||||
okay_to_leave = true;
|
||||
location.href = '${url('schedule.edit')}';
|
||||
});
|
||||
|
||||
$('.clear-schedule').click(function() {
|
||||
if (confirm("This will remove all shifts from the schedule you're " +
|
||||
"currently viewing.\n\nAre you sure you wish to do this?")) {
|
||||
|
@ -234,34 +52,11 @@
|
|||
|
||||
});
|
||||
</script>
|
||||
<style type="text/css">
|
||||
.timesheet .day {
|
||||
cursor: pointer;
|
||||
height: 5em;
|
||||
}
|
||||
.timesheet tr .day.modified {
|
||||
background-color: #fcc;
|
||||
}
|
||||
.timesheet tr:nth-child(odd) .day.modified {
|
||||
background-color: #ebb;
|
||||
}
|
||||
.timesheet .day .shift.deleted {
|
||||
display: none;
|
||||
}
|
||||
#day-editor .shift {
|
||||
margin-bottom: 1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#day-editor .shift input {
|
||||
width: 6em;
|
||||
}
|
||||
#day-editor .shift button {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
#snippets {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
${self.edit_timetable_styles()}
|
||||
</%def>
|
||||
|
||||
<%def name="context_menu()">
|
||||
|
@ -282,7 +77,7 @@
|
|||
</%def>
|
||||
|
||||
<%def name="edit_form()">
|
||||
${h.form(url('schedule.edit'), id='schedule-form')}
|
||||
${h.form(url('schedule.edit'), id='timetable-form')}
|
||||
${h.csrf_token(request)}
|
||||
</%def>
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
<%namespace file="/shifts/lib.mako" import="timesheet_wrapper" />
|
||||
|
||||
<%def name="context_menu()">
|
||||
% if employee is not Undefined and request.has_perm('timesheet.edit'):
|
||||
<li>${h.link_to("Edit this Time Sheet", url('timesheet.employee.edit'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('schedule.view'):
|
||||
<li>${h.link_to("View this Schedule", url('timesheet.goto.schedule'), class_='goto')}</li>
|
||||
% endif
|
||||
|
|
58
tailbone/templates/shifts/timesheet_edit.mako
Normal file
58
tailbone/templates/shifts/timesheet_edit.mako
Normal file
|
@ -0,0 +1,58 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
<%inherit file="/shifts/base.mako" />
|
||||
<%namespace file="/shifts/lib.mako" import="timesheet_wrapper" />
|
||||
|
||||
<%def name="extra_javascript()">
|
||||
${parent.extra_javascript()}
|
||||
${self.edit_timetable_javascript()}
|
||||
</%def>
|
||||
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
${self.edit_timetable_styles()}
|
||||
</%def>
|
||||
|
||||
<%def name="context_menu()">
|
||||
% if request.has_perm('timesheet.view'):
|
||||
<li>${h.link_to("View this Time Sheet", url('timesheet.employee'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('schedule.view'):
|
||||
<li>${h.link_to("View this Schedule", url('schedule.employee'))}</li>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="render_day(day)">
|
||||
% for shift in day['shifts']:
|
||||
<p class="shift" data-uuid="${shift.uuid}">
|
||||
${render_shift(shift)}
|
||||
</p>
|
||||
% endfor
|
||||
</%def>
|
||||
|
||||
<%def name="edit_form()">
|
||||
${h.form(url('timesheet.employee.edit'), id='timetable-form')}
|
||||
${h.csrf_token(request)}
|
||||
</%def>
|
||||
|
||||
<%def name="edit_tools()">
|
||||
<div class="buttons">
|
||||
<button type="button" class="save-changes" disabled="disabled">Save Changes</button>
|
||||
<button type="button" class="undo-changes" disabled="disabled">Undo Changes</button>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
${timesheet_wrapper(edit_form=edit_form, edit_tools=edit_tools, context_menu=context_menu, render_day=render_day, change_employee='confirm_leave')}
|
||||
|
||||
${edit_tools()}
|
||||
|
||||
<div id="day-editor" style="display: none;">
|
||||
<div class="shifts"></div>
|
||||
<button type="button" id="add-shift">Add Shift</button>
|
||||
</div>
|
||||
|
||||
<div id="snippets">
|
||||
<div class="shift" data-uuid="">
|
||||
${h.text('edit_start_time')} thru ${h.text('edit_end_time')}
|
||||
<button type="button"><span class="ui-icon ui-icon-trash"></span></button>
|
||||
</div>
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue