(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); }); });