(function ($) {
$(function () {
// http://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this, args = arguments;
var later = function () {
timeout = null;
if (!immediate)
func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow)
func.apply(context, args);
};
};
/*____________________________________________________________________________________*/
class People{
constructor(selector, template, data){
this.selector = selector;
this.data = data;
this.template = template;
// this.sample_people = {
// login: '01515b52-6936-46b2-a000-9ad4cd7a5b50',
// firstname: "first",
// lastname: "last",
// phone: '041122221',
// email: 'abc@gmail.com',
// pay: 0,
// hour: 12,
// OT: 3,
// petrol: 50,
// rating: 1,
// };
this.load_data(this.data);
}
load_data(data){
var template = $(this.template).html();
var html = Mustache.render(template, data);
$(this.selector).html(html);
//save it
$(this.selector).data(data);
//draw rating star
this.set_ratings(this.data.rating);
this.set_unconfirmed_job(this.data.unconfirmedjob);
}
set_ratings(num){
for (var i=1; i<= 5; i++){
if (i <=num){
$(this.selector + " div[name='rating'] span:nth-child(" +i+ ")").addClass('checked');
}else{
$(this.selector + " div[name='rating'] span:nth-child(" +i+ ")").removeClass('checked');
}
}
this.data.rating = num;
}
set_unconfirmed_job(num){
if( num == 0 )
$(this.selector + " span[name='badge']").hide();
else
$(this.selector + " span[name='badge']").show();
this.data.unconfirmedjob = num;
}
}//end of class People
function bts_staff_html(data){
var template = $('#staff_item').html();
var head = '
';
r = head + '
' ;
return r;
}
function bts_client_html(data){
var template = $('#client_item').html();
var head = '';
r = head + '
' ;
return r;
}
function sample_staff(){
for (var i=1; i<100; i++){
var sample_people = {
login: '01515b52-6936-46b2-a000-9ad4cd7a5b50' +i,
firstname: "first"+i,
lastname: "last",
mobile: '041122221' +i,
email: 'abc@gmail.com' + i,
wages: 0,
hour: i,
OT: 3,
petrol: 50 +i,
rating: Math.floor(Math.random() * Math.floor(5)),
unconfirmedjob: Math.floor(Math.random() * Math.floor(30)),
};
var html = bts_staff_html(sample_people);
jQuery('div.stafflist').append(html);
new People("#p" + sample_people.login, sample_people);
}
}
function list_staff() {
show_loading_staff();
$('div.stafflist div.peopleitem').remove();
$.post(bts().ajax_url, { // POST request
_ajax_nonce: bts().nonce, // nonce
action: "list_staff", // action
}, function(response, status, xhr){
if (response.status =='success'){
hide_loading_staff();
response.users.forEach(function(u){
var html = bts_staff_html(u);
jQuery('div.stafflist').append(html);
new People("#p" + u.login,'#staff_item', u);
});
}else{
alert('error getting staff list');
}
});
}
function list_clients() {
show_loading_client();
$('div.clientlist div.peopleitem').remove(); //clear it
$.post(bts().ajax_url, { // POST request
_ajax_nonce: bts().nonce, // nonce
action: "list_client", // action
}, function(response, status, xhr){
if (response.status =='success'){
response.users.forEach(function(u){
hide_loading_client();
var html = bts_client_html(u);
jQuery('div.clientlist').append(html);
new People("#p" + u.login, '#client_item' ,u);
});
}else{
alert('error getting Client list');
}
});
}
function show_loading_staff(){
jQuery('div.stafflist img').attr('src', bts().load_user_img).show();
}
function show_loading_client(){
jQuery('div.clientlist img').attr('src', bts().load_user_img).show();
}
function hide_loading_staff(){
jQuery('div.stafflist img').hide();;
}
function hide_loading_client(){
jQuery('div.clientlist img').hide();
}
function xero(t){
if (t)
$('div.xero i').show();
else
$('div.xero i').hide();
}
function wifi(t){
if (t)
$('div.wifi i').show();
else
$('div.wifi i').hide();
}
function init_user_search(){
$('div.b_search input').keyup(debounce(function(e){
filter_user(e.target);
}, 500));
}
function filter_user(input){
var value = $(input).attr('value');
value = value.toLowerCase();
var selector = get_selector_for_filter_people(input);
$.each( $(selector).find('div.peopleitem'), function(index, e){
var html = $(e).find('div[name="title"] a').html();
html = html.toLowerCase();
if (-1 != html.indexOf(value)){//we find it;
$(e).show();
}else{
$(e).hide();
}
});
}
function get_selector_for_filter_people(input){
var selector='';
var role = $(input).attr('placeholder');
if (role == 'staff') //we filter staff
selector = 'div.stafflist';
else if (role = 'client')
selector = 'div.clientlist';
return selector;
}
function init_ts(){
list_staff();
list_clients();
xero(false);
wifi(false);
init_user_search();
ajax_earning_rate();
}
function ajax_earning_rate(){
$.post(bts().ajax_url, { // POST request
_ajax_nonce: bts().nonce, // nonce
action: "earnings_rate", // action
}, function(response, status, xhr){
bts().earnings_rate = response;
console.log("%o", bts().earnings_rate);
});
}
init_ts();
$(document).on('click', 'div.divTableHead.bdelete', function(){
var o = new Job({empty:true});
});
$(document).on('click', 'div.copyprogress', function(){
for (var i=1; i<10; i++){
var o = new Job({empty:true});
}
});
$(document).on('click', 'div.divTableCell.bdelete', function(){
var job = $(this).closest('.divTable').data().job;
var el = $(this).closest('div.divTable');
if ( job.get_job_id() == '')
el.remove();
else{
if (confirm('delete this job?')){
$.post(bts().ajax_url, { // POST request
_ajax_nonce: bts().nonce, // nonce
action: "delete_job", // action
jobid: job.data.id,
}, function(response, status, xhr){
if (response.status=='success'){
el.addClass('blink_me');
el.fadeOut(900);
setTimeout(function(){
el.remove();
}, 900);
}else{
alert( 'error saving data, please check your network');
}
});
}
}
});
$(document).on('mouseenter', 'div.divTableCell', function(){
$(this).closest('div.divTable').addClass('highlight');
});
$(document).on('mouseleave', 'div.divTableCell', function(){
$(this).closest('div.divTable').removeClass('highlight');
});
$(document).on('click', 'span.ticon.ticon-save', function(){
var table = $(this).closest('div.divTable')
table.data().job.do_save_record();
});
class Job{ //save data for the record, and display it as GUI
constructor(data){
var html = jQuery("#job_item").html();
this.el = $(html);
jQuery('div.workspace').append(this.el);
this.load_data(data);
dtp_init();
this.init_start_rating()
}
init_start_rating(){
var self = this;
this.el.find("div.brating span").click(function(){
var r = $(this).attr('data-rating');
self.mark_dirty();
self.set_rating(r);
})
this.el.find("div.brating").mouseenter(function(){
//change to all hollow star
$(this).find('span').html('☆');
});
this.el.find("div.brating").mouseleave(function(){
self.set_rating(self.data.rating);
});
}
load_data(data)
{
this.set_job_id(data.id);
this.set_tos(data.tos);
this.set_start(data.start);
this.set_finish(data.finish);
this.set_rate(data.rate);
this.set_staff(data.staff);
this.set_client(data.client);
this.set_ack(data.ack);
this.set_rating(data.rating);
//save to html element
this.data = data;
this.el.data({job:this, data:data});
if (typeof(data.id) === 'undefined' || data.id == ''){
this.mark_dirty();
this.mark_new();
}
else{
this.mark_saved();
}
}
get_job_id(){
return this.el.find('input[name="id"]').attr('value');
}
set_job_id(val){
return this.el.find('input[name="id"]').attr('value', val);
}
get_tos()
{
return this.el.find('div.btos select').children("option:selected").val();
}
set_tos(val)
{
if (typeof(val) =="undefined")
return;
this.el.find('div.btos select option[value="'+val+'"]').prop('selected',true);
}
get_start(){
return this.el.find('div.bstart input').attr('value');
}
set_start(val)
{
if (typeof(val) =="undefined")
return;
this.el.find('div.bstart input').attr('value', val);
}
get_finish()
{
return this.el.find('div.bfinish input').attr('value');
}
set_finish(val)
{
if (typeof(val) == "undefined")
return;
this.el.find('div.bfinish input').attr('value', val);
}
get_rate()
{
return this.el.find('div.brate select').children("option:selected").val();
}
set_rate(val)
{
if (typeof(val) =="undefined")
return;
this.el.find('div.brate select option[value="'+val+'"]').prop('selected',true);
}
get_staff()
{
return this.el.find('div.bstaff select').children("option:selected").val();
}
set_staff(val)
{
if (typeof(val) =="undefined")
return;
this.el.find('div.bstaff select option[value="'+val+'"]').prop('selected',true);
}
get_client()
{
return this.el.find('div.bclient select').children("option:selected").val();
}
set_client(val)
{
if (typeof(val) =="undefined")
return;
this.el.find('div.bclient select option[value="'+val+'"]').prop('selected',true);
}
get_ack()
{
return this.el.find('div.bconfirmed input:checked').length > 0;
}
set_ack(val)
{
if (typeof(val) =="undefined")
return;
return this.el.find('div.bconfirmed input').prop('checked', val!=0);
}
get_rating(){
var count =0;
this.el.find('div.brating span').each(function(i,e){
if ($(e).html()=='★')
count +=1;
});
return count;
}
set_rating(num){
if (!(1 <= num && num <=5))
return;
this.el.find('div.brating span').each(function(i,e){
var rating = $(e).attr('data-rating');
var rating = parseInt(rating);
if (rating <= num)
$(e).html('★');
else
$(e).html('☆');
});
}
get_record_from_ui(){
var record = {};
record.id = this.get_job_id();
record.tos = this.get_tos();
record.start = this.get_start();
record.finish = this.get_finish();
record.rate = this.get_rate();
record.staff = this.get_staff();
record.client = this.get_client();
record.ack = this.get_ack();
record.rating = this.get_rating();
return record;
}
do_save_record(){
var self = this;
$.post(bts().ajax_url, { // POST request
_ajax_nonce: bts().nonce, // nonce
action: "save_job", // action
record: this.get_record_from_ui(),
}, function(response, status, xhr){
if (response.status=='success'){
self.load_data(response.newdata);
self.mark_saved();
self.mark_old();
}else{
alert( 'error saving data, please check your network');
}
});
}
mark_dirty() //need save
{
var d = this.el.find('.bsave');
d.removeClass('saved');
d.addClass('blink_me');
setTimeout(function(){
d.removeClass('blink_me');
},1000);
}
mark_saved()
{
var d = this.el.find('.bsave');
d.addClass('blink_me');
setTimeout(function(){
d.removeClass('blink_me');
d.addClass('saved');
},1000);
}
//newly created empty record
mark_new()
{
this.el.addClass('emptyrecord');
}
mark_old()
{
this.el.removeClass('emptyrecord');
}
is_start_valid(){
var s = this.get_start();
return is_valid_date_str(s);
}
is_finish_valid(){
var f = this.get_finish();
if (!is_valid_date_str(f))
return false;
}
is_finish_resonable(){
var f = this.get_finish();
if (!is_valid_date_str(f))
return false;
var s = this.get_start();
s = new Date(s);
f = new Date(f);
return (s < f);
}
}//end of class Job
//global GUI summary
function get_wages()
{
var txt = $('div.wages div').html();
return parseInt(txt);
}
function set_wages(num){
$('div.wages div').html(num);
}
function set_working_hours(num){
$('input#woh').attr('value', num);
}
function get_working_hours(){
var txt = $('input#woh').attr('value');
return parseFloat(txt);
}
//modal box
function set_modal_title(selector, title){
var s = 'div.bts_'+ selector +' .ult_modal-title';
$(s).html(title);
}
function set_modal_content(selector, content){
var s = 'div.bts_'+ selector +' div.ult_modal-body.ult-html';
$(s).html(content);
}
function open_modal (selector){
var s='div.bts_'+selector+'_button';
$(s).trigger('click');
}
// setTimeout(function(){
// set_modal_title('warning', 'suck title');
// set_modal_content('warning', 'fucking details');
// //open_modal('warning');
// }, 1000);
//
// setTimeout(function(){
// set_modal_title('error', 'error title');
// set_modal_content('error', 'error details');
// //open_modal('error');
// }, 5000);
$(document).on('mouseenter', 'div.week1 div', function(){
$(this).addClass('blink_me');
get_week2_partner(this).addClass('blink_me');
blink_same_date_by_div(this);
});
$(document).on('mouseleave', 'div.week1 div', function(){
$(this).removeClass('blink_me');
get_week2_partner(this).removeClass('blink_me');
unblink_all_date();
});
function get_week2_partner(div){
var index = $(div).index()+1;
return $('div.week2 div:nth-child('+index+')');
}
function init_weekdays(){
var curr = new Date; // get current date
// First day is the day of the month - the day of the week
var first = curr.getDate() - curr.getDay() + 1; //+1 we want Mon as first
var last = first + 6; // last day is the first day + 6
//var firstday = new Date(curr.setDate(first)); //Mon
//var lastday = new Date(curr.setDate(last)); //Sun
var pos = 1; //first lot
for (var i=first; i<=last; i++)
{
var d1 = new Date(curr.setDate(i));
var d2 = new Date(curr.setDate(i+7));
set_day_number(1,pos, d1); //week 1
set_day_number(2,pos, d2); //week 2
pos +=1;
}
}
function set_day_number(week, index, date){
var selector = 'span[name="w'+week+'d'+index+'"]';
$(selector).html(date.getDate());
$(selector).data({date:date});
}
function set_today(){
var selector = 'div.sheettitle span[name="today"]';
var curr = new Date;
$(selector).html(format_date(curr));
}
Date.prototype.get_week_number = function(){
var d = new Date(Date.UTC(this.getFullYear(), this.getMonth(), this.getDate()));
var dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1));
return Math.ceil((((d - yearStart) / 86400000) + 1)/7)
};
function set_week_number(){
var date = $('span[name="w1d1"]').data().date;
//console.log("date %o", date);
var num = date.get_week_number();
$('div.weekly span[name="week1"]').html(num);
$('div.weekly span[name="week2"]').html(num+1);
}
function number_of_unsaved_job(){
var count =0;
var total_job = $('div.bsave').length -1;//remove table header
var total_saved = $('div.bsave.saved').length;
var empty = $('div.emptyrecord').length;
count = total_job - total_saved - empty;
return count;
}
$('div.prevweek.left').click(function(){
if (number_of_unsaved_job() > 0){
alert ("you have unsaved jobs,please save it before proceed");
return;
}
$('div.weekdays span.weekday').each(function(i, e){
var date = $(e).data().date;
var newdate = new Date(date.setDate(date.getDate() -7 ));
$(e).html(newdate.getDate());
$(e).data({data:newdate});
});
set_week_number();
load_timesheet();
});
$('div.nextweek.right').click(function(){
if (number_of_unsaved_job() > 0){
alert ("you have unsaved jobs,please save it before proceed");
return;
}
$('div.weekdays span.weekday').each(function(i, e){
var date = $(e).data().date;
var newdate = new Date(date.setDate(date.getDate() +7 ));
$(e).html(newdate.getDate());
$(e).data({data:newdate});
});
set_week_number();
load_timesheet();
});
$('div.weekly div.weekname.prev').click(function(){
if (!confirm ('copy entire week to next week? '))
return;
var jobs = [];
$('div.week1 >div').each(function(i,e){
var date = new Date($(e).find('span.weekday').data().date);
var strDate = format_date(date); //yyyy-mm-dd
$('div.bstart input').each(function(i,e){
var value = $(e).attr('value');
if( -1 != value.indexOf(strDate) ) //found
{
var j = $(e).closest('div.divTable').data().job;
jobs.push(j);
}
});
});
jobs.forEach(function(e){
clone_data_create_new_job(e.data);
});
});
$('div.weekly div.weekname.next').click(function(){
alert('you can only copy from past to future (left to right)');
});
$('div.week1 > div').click(function(){
if ($('div.bstart input.blink_me').length == 0){
alert("nothing to copy");
return;
}
if (!confirm ('copy to next week'))
return;
$('div.bstart input.blink_me').each(function(i,e){
copy_single_day_to_next_week(e);
});
unblink_all_date();
});
function copy_single_day_to_next_week(el){
var j = $(el).closest('div.divTable').data().job;
clone_data_create_new_job(j.data);
}
function clone_data_create_new_job(val){
var data = $.extend(true, {}, val);//make a copy
//reset
data.id='';
data.ack = 0;
data.rating = 0;
if (is_valid_date_str(data.start)){
var s = new Date(data.start);
var s1 = s.getDate() + 7;
s = new Date(s.setDate(s1));
data.start = format_date_time(s);
}
if (is_valid_date_str(data.finish)){
var f = new Date(data.finish);
var f1 = f.getDate() + 7;
f = new Date(f.setDate(f1));
data.finish = format_date_time(f);
}
new Job(data);
}
function is_valid_date_str(val){
var d = new Date(val);
if (d.toString()== 'Invalid Date')
return false;
return true;
}
function blink_same_date_by_div(div){
var date = new Date($(div).find('span.weekday').data().date);
blink_same_date(date);
}
function blink_same_date(date){
var strDate = format_date(date); //yyyy-mm-dd
var els=[];
unblink_all_date();
$('div.bstart input').each(function(i,e){
var value = $(e).attr('value');
if( -1 != value.indexOf(strDate) ) //found
{
els.push(e);
$(e).addClass('blink_me');
}
});
}
function unblink_all_date(){
$('div.bstart input').removeClass('blink_me');
}
$('div.sheettitle h1').click(function(){
reset_title_to_today();
})
function reset_title_to_today(){
set_today();
init_weekdays();
set_week_number();
load_timesheet();
}
function load_timesheet()
{
clear_workspace();
var first = $('span[name="w1d1"]').data().date;
var last = $('span[name="w2d7"]').data().date;
$.post(bts().ajax_url, { // POST request
_ajax_nonce: bts().nonce, // nonce
action: "list_job", // action
start: format_date(first),
finish: format_date(last),
}, function(response, status, xhr){
if (response.status =='success'){
response.jobs.forEach(function(job){
new Job(job);
});
//filter it if reqired
do_filter_workspace();
}else{
alert('error loading job');
}
});
}
function format_date(date){
var dd = date.getDate();
var mm = date.getMonth() + 1; //January is 0!
var yyyy = date.getFullYear();
if (dd < 10) {
dd = '0' + dd;
}
if (mm < 10) {
mm = '0' + mm;
}
return yyyy + '-' + mm + '-' +dd ;
}
function format_date_time(date){
var strdate = format_date(date);
var hh = date.getHours();
if (hh<10){
hh= '0' + hh;
}
var mm = date.getMinutes();
if (mm<10){
mm='0' + mm;
}
return strdate + ' ' + hh + ":" + mm;
}
function clear_workspace()//clear all timesheet jobs
{
$('div.workspace > div.divTable').remove();
//clear datetime picker
$('div.xdsoft_datetimepicker').remove();
}
$('button[name="confirmschedule"]').click(function(){
$('span.ticon.ticon-save').trigger('click');
});
$(document).on('click','div.userlist', debounce(do_filter_workspace));
function do_filter_workspace(){
var staffs =[];
$('div.stafflist div.peopleitem :checked').each(function(i, e){
var id = $(e).parent().attr('data-id');
//console.log("%o, id=%s", e, id);
staffs.push(id.substring(1));
});
var clients =[];
$('div.clientlist div.peopleitem :checked').each(function(i, e){
var id = $(e).parent().attr('data-id');
//console.log("%o, id=%s", e, id);
clients.push(id.substring(1));
});
console.log('staffs %o' , staffs);
console.log('clients %o' , clients);
filter_workspace(staffs, clients);
}
function filter_workspace(staffs, clients){
//if both array is empty
if( (staffs === undefined || staffs.length ==0) &&
(clients===undefined || clients.length ==0)){
//show all
$('div.workspace div.divTable').show();
return;
}
//filter some of them;
$('div.workspace div.divTable').each(function(i,e){
var job = $(e).data().job;
var s = job.get_staff();
var c = job.get_client();
if (staffs.indexOf(s) ==-1 && clients.indexOf(c) ==-1)
$(this).fadeOut();
else
$(this).fadeIn();
});
}
function calculate_total_working_hour()
{
}
function calculate_total_money()
{
}
//visually hint whether start is correct;
$(document).on('change','div.bstart input', function(){
var str = $(this).attr('value');
if ( ! is_valid_date_str(str) )
$(this).css('background-color', 'orange');
else
$(this).css('background-color', 'white');
});
//visually hint whether finish date is correct;
$(document).on('change','div.bfinish input', function(){
var job = $(this).closest('div.divTable').data().job;
var str = $(this).attr('value');
if ( ! is_valid_date_str(str) ){
$(this).css('background-color', 'orange');
return;
}else
$(this).css('background-color', 'white');
//must be later than start
if (! job.is_finish_resonable()){
alert('finish date should be bigger than start date');
$(this).css('background-color', 'orange');
}else{
$(this).css('background-color', 'white');
}
});
$(document).on('change', '.divTableRow select, .divTableRow input', function() {
var job = $(this).closest('.divTable').data().job;
job.mark_dirty();
});
reset_title_to_today();
/*________________________________________________________________________*/
});
})(jQuery);
/*______________scrolling______________________________________________*/
jQuery(document).ready(function(){
var timeoutid =0;
jQuery('button.peoplelist[name="down"]').mousedown(function(){
var button = this;
timeoutid = setInterval(function(){
//console.log("down scrotop %d ", jQuery(button).parent().find(".userlist").get(0).scrollTop );
jQuery(button).parent().find(".userlist").get(0).scrollTop +=240;
}, 100);
}).on('mouseup mouseleave', function(){
clearTimeout(timeoutid);
});
jQuery('button.peoplelist[name="up"]').mousedown(function(){
var button = this;
timeoutid = setInterval(function(){
//console.log("up scrotop %d ", jQuery(button).parent().find(".userlist").get(0).scrollTop );
jQuery(button).parent().find(".userlist").get(0).scrollTop -=240;
}, 100);
}).on('mouseup mouseleave', function(){
clearTimeout(timeoutid);
});
});