(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({obj:this, 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; } reset_summary() { this.summary = { wages : 0, normal_hour : 0, ot_hour : 0, petrol : 0, }; this.update_summary_in_gui(); } add_payment_summary(ps) { //{ot: false, hour: "2.67", money: "76.90"} this.summary.wages += ps.money; if (! ps.ot ) this.summary.normal_hour += ps.hour; else this.summary.ot_hour += ps.hour; this.update_summary_in_gui(); } update_summary_in_gui() { var msg = '$' + this.summary.wages.toFixed(2); $(this.selector).find('div[name="wages"]').html(msg); msg = this.summary.normal_hour.toFixed(2) + '+' +this.summary.ot_hour.toFixed(2) + 'hr'; $(this.selector).find('div[name="hours"]').html(msg); msg = 'petrol:' + this.summary.petrol.toFixed(2) + 'km'; $(this.selector).find('div[name="petrol"]').html(msg); } }//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 }).done(function(response, status, xhr){ if (response.status =='success'){ bts().staff = response.users; bts().staff_map = {}; bts().staff_people= {}; response.users.forEach(function(u){ bts().staff_map[u.login] = u; var html = bts_staff_html(u); jQuery('div.stafflist').append(html); var staff_obj = new People("#p" + u.login,'#staff_item', u); bts().staff_people[u.login] = staff_obj; }); hide_loading_staff(); calculate_total_hour_and_money(); }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'){ bts().client = response.users; bts().client_map = {}; response.users.forEach(function(u){ bts().client_map[u.login] = 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 list_tos() { wifi(true); $.post(bts().ajax_url, { // POST request _ajax_nonce: bts().nonce, // nonce action: "list_tos", // action }, function(response, status, xhr){ if (response.status =='success'){ bts().tos = response.tos; wifi(false); }else{ alert('error getting Type of Service 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 show_loading_jobs(){ jQuery('div.workspace img').attr('src', bts().load_job_img).show(); } function hide_loading_jobs(){ jQuery('div.workspace 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 csv(t){ if (t) $('div.csv i').show(); else $('div.csv 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){ //uncheck everyone $(e).find('input[type="checkbox"]').prop('checked', false); 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; } $(document).on('click', 'div.jobTable div.brate.error', function(){ var msg = $(this).attr('title'); if (msg != "") alert(msg); }); $(document).on('click', 'div.workspace div.bedit span.ticon-edit', function(){ var el = $(this).closest('div.jobTable'); el.addClass('Editing'); var newjob_id = el.attr('data-newjob_id'); if (typeof newjob_id != 'undefined' && newjob_id != ''){ do_edit_new_job(newjob_id); }else{ var id = el.attr('data-id'); do_edit_job(id); } }); // $(document).on('click', 'div.bts_editor div.ult-overlay-close', function(){ // $('.Editing').addClass('blink_me'); // setTimeout(function(){ // $(".Editing").removeClass('Editing blink_me'); // }, 1000); // // }); $(document).on('click', 'div.bts_editor div.ult-overlay-close', function(){ var el = $('.Editing'); el.addClass("blink_me").removeClass('Editing'); setTimeout(function(){ el.removeClass('blink_me'); }, 600); }); function close_editor(){ $('div.bts_editor div.ult-overlay-close').trigger('click'); } $(document).on('jobEditor:close', 'div.divTable.Editor', function(e,data){ var editor = data.editor; var job = data.job; //console.log('close_editor event %o, %o', editor, job); editor.off_event_handler();//remove all events handler; var templ = $("#jobv1_item").html(); var html = Mustache.render(templ, {jobs:job}); //job id should be available; if ( $('div.jobTable.Editing').length == 0){ $('div.workspace').append(html); $('#job_'+job.id).get(0).scrollIntoView(); }else{ //create new job underneath it var templ = $("#jobv1_item").html(); var html = Mustache.render(templ, {jobs:job}); //job id should be available; $('div.jobTable.Editing').after(html).remove(); } var el = $('#job_' + job.id).addClass("blink_me"); setTimeout(function(){ el.removeClass('blink_me'); }, 1500); debounced_calculate(); }); $(document).on('click', 'div.divTableHead.bdelete span.ticon-trash', function(){ var len = $('div.jobTable.to_be_deleted_duplicate').length; if (len <= 0){ if (confirm("No duplicates to delete, trying to find duplicates?")){ check_duplicate(); } return; }else{ if (!confirm("Delete " + len + " duplicates? ")) return; } var ids = []; $('div.jobTable.to_be_deleted_duplicate').each(function(){ var id = $(this).attr('data-id'); ids.push(id); }); $.post(bts().ajax_url, { // POST request _ajax_nonce: bts().nonce, // nonce action: "delete_jobv1", // action jobs: ids, //delete multiple ids }, function(response, status, xhr){ if (response.status=='success'){ $('div.jobTable.to_be_deleted_duplicate').each(function(){ var id = $(this).attr('data-id'); delete bts().job_map[id]; $(this).get(0).scrollIntoView(); fade_and_delete(this); }); debounced_calculate(); }else{ alert( 'error deleting duplicates:\n\nError:\n\n' + response.error + "\n\n"); } }); }); function fade_and_delete(el) { $(el).fadeOut(300); setTimeout(function(){ $(el).remove(); },300); } function add_new_empty_job(job){ var title = ""; if (typeof job == 'undefined' || ! (job instanceof Job) ){ job = new Job({empty:true}); job.is_new = true; title = "Create New Job "; }else if (job instanceof Job){ title = "Create new Job by Copy Existing one"; } job.editorid = Math.floor(Math.random() * Math.floor(99999)); // a random number; //show editor var html = $('#jobv1_editor').html(); html = Mustache.render(html, job); set_modal_content('editor', html); //update GUI open_modal('editor'); set_modal_title('editor', title); dtp_init(); //init editor var e = new JobEditor('#editor_' + job.editorid, job); console.log("%o is instance of JobEditor %o", e, e instanceof JobEditor); } $(document).on('click', 'div[name="copyschedule"]', function(e){ e.stopPropagation(); add_new_empty_job(); }); $(document).on('click', 'div.divTableCell.bdelete', function(){ var el = $(this).closest('div.jobTable'); var data = el.data(); if (typeof data.id =='undefined' || data.id == '') el.remove(); else{ var id = data.id; if (confirm('delete this job?')){ $.post(bts().ajax_url, { // POST request _ajax_nonce: bts().nonce, // nonce action: "delete_job", // action jobid: id, }, function(response, status, xhr){ if (response.status=='success'){ var id = el.attr('data-id'); delete bts().job_map[id]; //console.log("delete %s , job_map[%s]=%o ", id, id, bts().job_map[id]); el.addClass('blink_me'); el.fadeOut(900); setTimeout(function(){ el.remove(); }, 900); }else{ alert( 'error saving data, please check your network'); } }); } } debounced_calculate(); }); $(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', 'div.workspace span.ticon.ticon-save', function(){ var table = $(this).closest('div.divTable'); table.data().job.do_save_record(); }); $(document).on('click', '.divTableHeading span.ticon.ticon-save', function(){ //save all $('div.workspace span.ticon.ticon-save').each(function (i,e){ if ($(this).is(":visible")) $(this).trigger('click'); }); }); $(document).on('click', 'span.ticon.ticon-copy', function(){ var table = $(this).closest('div.jobTable'); var record = bts().job_map[table.data().id].get_record(); //create new job; var newj = clone_data_create_new_job(record); add_new_empty_job(newj); }); function mark_highlight_me(el, ms){ el.addClass('blink_me'); el.addClass('highlight'); el.addClass('newcopy'); setTimeout(function(){ el.removeClass('blink_me'); el.removeClass('highlight'); el.removeClass('newcopy'); },ms); } class Job{ constructor(record) { this.original_attr = Object.keys(record); $.extend(this, record); this.derive_attr(); } get_record() {//without extended attributes; var self = this; var r = {}; this.original_attr.forEach(function(attr){ r[attr] = self[attr]; }); return r; } derive_attr() { this.tos_name(this); this.staff_name(this); this.client_name(this); this.rate_name(this); this.rating_range(this); this.identify_job_week(this); this.job_acked(this); } tos_name(e){ if (typeof bts().tos[e.tos] != 'undefined'){ e.tos_name = bts().tos[e.tos].tos_full_str; }else{ e.tos_name = "(deleted) "; e.tos_err="Missing: " + e.tos; } } staff_name(e){ if (typeof bts().staff_map[e.staff] != 'undefined'){ e.staff_name = bts().staff_map[e.staff].display_name; }else{ e.staff_name = "(deleted) "; e.staff_err="Missing: " + e.staff; } } client_name(e){ if (typeof bts().client_map[e.client] != 'undefined'){ e.client_name = bts().client_map[e.client].display_name; }else{ e.client_name ="(deleted)"; e.client_err ="Missing: " + e.client; } } rate_name(e){ if (typeof bts().earnings_rate[e.rate] != 'undefined') { e.rate_name = bts().earnings_rate[e.rate].RatePerUnit + "-" + bts().earnings_rate[e.rate].Name; if (! has_txt_hour( bts().earnings_rate[e.rate].TypeOfUnits )){ e.rate_err = `Rate unit must be ⟦ Hours ⟧ Possible solution: 1. Change it in Xero 2. Delete this job`; } }else{ e.rate_name = "(deleted)"; e.rate_err = "Missing: " + e.rate; } } rating_range(e){ if (e.rating <0 || e.rating >5){ e.rating_err = "Rating 0-5 Only"; } } identify_job_week(e){ if (job_is_week1(e.start)){ e.is_week1=true; }else if (job_is_week2(e.start)){ e.is_week2=true; } } job_acked(e){ if (typeof e == 'undefined'){ console.log('break;'); } if (e.ack != 0){ e.is_confirmed = true; } } is_job_valid(){ var error_found = false; var self = this; this.original_attr.forEach(function(attr){ var col = attr + "_err"; if (typeof self[col] == 'undefined') return; if (self[attr+"_err"] != "") error_found = true; }); return !error_found; } is_high_pay() { var job = this; var rate_info = bts().earnings_rate[job.rate]; var keywords =bts().high_pay_keywords; var found = false; keywords.forEach(function(e){ if (-1 != rate_info.Name.toLowerCase().indexOf(e.toLowerCase()) ) found = true; }); return found; } get_payment_summary() { var job = this; var result ={}; result.ot = job.is_high_pay(); result.hour = job.get_working_duration(); result.money = job.get_wages(); return result; } get_working_duration() { var job = this; //finish - start var f = new Date(job.finish); var s = new Date(job.start); var diff = f.getTime() - s.getTime(); var hours = Math.floor(diff / 1000 / 60 / 60); diff -= hours * 1000 * 60 * 60; var minutes = Math.floor(diff / 1000 / 60); var minute_to_hour = minutes/60; return (hours + minute_to_hour); } get_wages() { var job = this; var hour = job.get_working_duration(job); var rate_info = bts().earnings_rate[job.rate]; if ( has_txt_hour( bts().earnings_rate[job.rate].TypeOfUnits ) ) { return hour * rate_info.RatePerUnit; }else{ return 1 * rate_info.RatePerUnit; } } }; class JobEditor{ //save data for the record, and display it as GUI constructor(selector, job){ this.el = $(selector); this.load_data(job); console.assert(job instanceof Job); this.init_event_handler(); } load_data(data) { //save to html element this.data = data; this.el.data({job:this, data:data}); //draw GUI this.clear_err_msg(); 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); //draw GUI by other this.mark_dirty_on_new_record(data); this.mark_week_color(); //this.validate(); //also triggers mark errors } init_event_handler(){ var self = this; this.el.find("div.btos select").change(function(){ if ( self.validate_tos() ) { self.data.tos = self.get_tos(); self.set_err_msg_tos(''); } }); this.el.find("div.bstart input").change(function(){ if (self.validate_start()){ self.data.start = self.get_start(); self.set_err_msg_start(''); self.validate_start_and_finish(); } }); this.el.find("div.bfinish input").change(function(){ if (self.validate_finish()){ self.data.finish = self.get_finish(); self.set_err_msg_finish(''); self.validate_start_and_finish(); } }); this.el.find("div.bstaff select").change(function(){ if (self.validate_staff()){ self.data.staff = self.get_staff(); self.set_err_msg_staff(''); } }); this.el.find("div.bclient select").change(function(){ if (self.validate_client()){ self.data.client = self.get_client(); self.set_err_msg_client(''); } }); this.el.find("div.brate select").change(function(){ if (self.validate_rate()){ self.data.rate = self.get_rate(); self.set_err_msg_rate(''); } }); this.el.find("div.bconfirmed input").change(function(){ if(self.validate_ack()){ self.data.ack = self.get_ack(); } }); this.el.find("div.brating select").change(function(){ if( self.validate_rating()){ self.data.rating =self.get_rating(); } }); this.el.find("div.bsave span.ticon-save").click(function(e){ if ( self.validate() ){ self.do_save_record(); }else{ self.set_err_msg_save('Data Error'); } }); } off_event_handler(){ this.el.off('change',"div.btos select"); this.el.off('change',"div.bstart input"); this.el.off('change',"div.bfinish input"); this.el.off('change',"div.bstaff select"); this.el.off('change',"div.bclient select"); this.el.off('change',"div.brate select"); this.el.off('change',"div.bconfirmed input"); this.el.off('change',"div.brating select"); this.el.off('change',"div.bsave span.ticon-save"); } get_job_id(){ return this.el.attr('data-id'); } set_job_id(val){ if (typeof val == 'undefined' || val == '') {//remove data-id and id this.el.removeAttr('data-id'); this.el.removeAttr('id'); }else{ this.el.attr('data-id', val); this.el.attr('id', 'job_' + 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); if ( this.get_tos() != val ){ this.set_err_msg_tos("Missing:" + this.data.tos) var o = new Option("(deleted)", this.data.tos); this.el.find('div.btos select').prepend(o); this.el.find('div.btos select option[value="'+this.data.tos+'"]').prop('selected',true); } } get_start(){ return this.el.find('div.bstart input').attr('value'); } set_start(val) { if (typeof(val) =="undefined"){ this.set_err_msg_start("need start"); 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"){ this.set_err_msg_finish("need finish"); 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); if ( this.get_rate() != val ){ var o = new Option("(deleted)", this.data.rate); this.el.find('div.brate select').prepend(o); this.set_err_msg_rate('Missing:' + this.data.rate); this.el.find('div.brate select option[value="'+this.data.rate+'"]').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); if ( this.get_staff() != val ){ var o = new Option("(deleted)", this.data.staff); this.el.find('div.bstaff select').prepend(o); this.set_err_msg_staff('Missing:' + this.data.staff); this.el.find('div.bstaff select option[value="'+this.data.staff+'"]').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); if ( this.get_client() != val ){ var o = new Option("(deleted)", this.data.client); this.el.find('div.bclient select').prepend(o); this.set_err_msg_client('Missing:' + this.data.client); this.el.find('div.bclient select option[value="'+this.data.client+'"]').prop('selected',true); } } get_ack() { return this.el.find('div.bconfirmed input:checked').length > 0? 1:0; } set_ack(val) { if (typeof(val) =="undefined") return; return this.el.find('div.bconfirmed input').prop('checked', val!=0); } get_rating(){ return this.el.find('div.brating select').children("option:selected").val(); } set_rating(num){ if (!(0 <= num && num <=5)) return; this.el.find('div.brating select option[value="'+num+'"]').prop('selected',true); } 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: self.get_record_from_ui(), }, function(response, status, xhr){ if (response.status=='success'){ self.load_data(response.newdata); var job = new Job(response.newdata); job.saved = true; job.is_new = response.isNew; bts().job_map[job.id] = job; var data = {editor:self, job:job}; self.el.trigger('jobEditor:close', data); close_editor(); }else{ self.self.set_err_msg_save('Not saved'); alert( 'error saving data, please check your network'); } }); } mark_dirty_on_new_record(data){ if (typeof(data.id) === 'undefined' || data.id == ''){ this.mark_dirty(); this.mark_new(); } else{ this.mark_saved(); } } mark_dirty() //need save { var d = this.el.find('.bsave'); d.removeClass('saved'); d.addClass('blink_me'); setTimeout(function(){ d.removeClass('blink_me'); d.removeClass('saved'); },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); } validate() { var ok_tos = this.validate_tos(); var ok_time = this.validate_start() && this.validate_finish() && this.validate_start_and_finish(); var ok_staff = this.validate_staff(); var ok_client = this.validate_client(); var ok_rate = this.validate_rate() ; //make sure this validate is executed var ok = ok_tos && ok_time && ok_staff && ok_client && ok_rate; if (ok){ this.el.removeClass('invalidjob'); }else{ this.el.addClass('invalidjob'); } return ok; } validate_start(){ var str = this.get_start(); if (str == ""){ this.set_err_msg_start('need start'); return false; } if (!is_valid_date_str(str) ){ this.mark_start_invalid(); this.set_err_msg_start('wrong date'); return false; } return true; } validate_finish() { var str = this.get_finish(); if (str == ""){ this.set_err_msg_finish('need finish'); return false; } if (! is_valid_date_str(str)){ this.set_err_msg_finish('wrong date'); this.mark_finish_invalid(); return false; } return true; } validate_start_and_finish() { if (! this.validate_start() || ! this.validate_finish()) return; if (!this.is_finish_resonable()){ this.set_err_msg_finish("older than start"); this.set_err_msg_start("after finish"); this.mark_start_invalid(); this.mark_finish_invalid(); return false; }else{ this.mark_start_valid(); this.mark_finish_valid(); this.set_err_msg_finish(""); this.set_err_msg_start(""); return true; } } validate_rate() { var rate_info = this.get_rate_info_by_id(this.get_rate()); if ( rate_info.RatePerUnit <= 0){ this.set_err_msg_rate('bad rate'); this.mark_rate_invalid(); return false; } this.set_err_msg_rate(''); this.mark_rate_valid(); return true; } validate_tos(){ if ( typeof bts().tos[this.get_tos()] == 'undefined') { this.set_err_msg_tos('missing ' + this.get_tos()); return false; }else{ this.set_err_msg_tos(''); return true; } } validate_staff(){ if ( typeof bts().staff_map[this.get_staff()] == 'undefined') { this.set_err_msg_staff('missing ' + this.get_staff()); return false; }else{ this.set_err_msg_staff(''); return true; } } validate_client(){ if ( typeof bts().client_map[this.get_client()] == 'undefined') { this.set_err_msg_client('missing ' + this.get_client()); return false; }else{ this.set_err_msg_client(''); return true; } } validate_rating(){ return; } clear_err_msg(){ this.el.find('.divTableRow.errmsg > div').html(''); } set_err_msg_start(str) { this.el.find('div.bstart_err').html(str); } set_err_msg_finish(str) { this.el.find('div.bfinish_err').html(str); } set_err_msg_staff(str) { this.el.find('div.bstaff_err').html(str); } set_err_msg_client(str) { this.el.find('div.bclient_err').html(str); } set_err_msg_rate(str) { this.el.find('div.brate_err').html(str); } set_err_msg_save(str) { this.el.find('div.bsave_err').html(str); } set_err_msg_tos(str) { this.el.find('div.btos_err').html(str); } mark_tos_valid(){ this.el.find('div.btos select').removeClass('invalid'); } mark_tos_invalid(){ this.el.find('div.btos select').addClass('invalid'); } mark_start_valid(){ this.el.find('div.bstart input').removeClass('invalid'); } mark_start_invalid(){ this.el.find('div.bstart input').addClass('invalid'); } mark_finish_valid(){ this.el.find('div.bfinish input').removeClass('invalid'); } mark_finish_invalid(){ this.el.find('div.bfinish input').addClass('invalid'); } mark_rate_valid(){ this.el.find('div.brate select').removeClass('invalid'); } mark_rate_invalid(){ this.el.find('div.brate select').addClass('invalid'); } mark_week_color(){ this.el.find('div.brating').removeClass('week1color'); this.el.find('div.brating').removeClass('week2color'); if (this.is_week1()){ this.el.find('div.brating').addClass('week1color'); }else if (this.is_week2()){ this.el.find('div.brating').addClass('week2color'); } } is_week1() { var w1_begin = new Date($('span[name="w1d1"]').data().date) ; var w1_end = new Date($('span[name="w1d7"]').data().date); w1_begin.setHours(0,0,0,0); w1_end.setHours(23,59,59); //console.log("week1 begin %o, end %o", w1_begin, w1_end); //w1_end = new Date (w1_end.setDate(w1_end.getDate()+1)); //from 00:00 to 23:59; var me = new Date(this.data.start); return (w1_begin <= me && me <= w1_end ); } is_week2() { var w2_begin = new Date($('span[name="w2d1"]').data().date); var w2_end = new Date($('span[name="w2d7"]').data().date); w2_begin.setHours(0,0,0,0); w2_end.setHours(23,59,59); var me = new Date(this.data.start); return (w2_begin <= me && me <= w2_end ); } get_payment_summary(){ var result ={}; result.ot = this.get_is_high_pay(); result.hour = this.get_working_duration(); result.money = this.get_wages(); return result; } get_is_high_pay() { var rate_info = this.get_rate_info_by_id(this.get_rate()); return this.is_high_pay_hour(rate_info); } get_working_duration() { //finish - start var f = new Date(this.get_finish()); var s = new Date(this.get_start()); var diff = f.getTime() - s.getTime(); var hours = Math.floor(diff / 1000 / 60 / 60); diff -= hours * 1000 * 60 * 60; var minutes = Math.floor(diff / 1000 / 60); var minute_to_hour = minutes/60; return (hours + minute_to_hour); } get_wages(){ var hour = this.get_working_duration(); var rate_info = this.get_rate_info_by_id(this.get_rate()); return hour * rate_info.RatePerUnit; } get_rate_info_by_id(id){ var rate_info = {}; var rates = bts().earnings_rate; for(var i =0; i< rates.length; i++){ var r = rates[i]; if(r.EarningsRateID == id){ rate_info = $.extend(true,{}, r);//make a copy break; } } return rate_info; } is_high_pay_hour(rate_info){ var keywords =bts().high_pay_keywords; var found = false; return false; keywords.forEach(function(e){ if (-1 != rate_info.Name.toLowerCase().indexOf(e.toLowerCase()) ) found = true; }); return found; } }//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'); } function set_modal_data(selector, data){ var s = 'div.bts_'+ selector; $(s).data(data); } function get_modal_data(selector, data){ var s = 'div.bts_'+ selector; $(s).data(); } // 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 init_weekdays_by_anchor(curr, true); return; } function init_weekdays_by_anchor(anchor, is_week1){ var curr = new Date(anchor); // get the date; if (!is_week1){ //it is week2, shift for 7 days; curr.setDate(curr.getDate() -7); //curr will be changed; } var first = curr.getDate() - curr.getDay() + 1; //+1 we want Mon as first var last = first + 6; // last day is the first day + 6 if (curr.getDay() == 0 ){// it's Sunday; last = curr.getDate(); first = last - 6; } var pos = 1; //first lot for (var i=first; i<=last; i++) { var now = new Date(curr); var d1 = new Date(now.setDate(i)); now = new Date(curr); var d2 = new Date(now.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}); //console.log('set w%d-d%d %o', week,index,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); set_week_boundry(); } function set_week_boundry() { var date = $('span[name="w1d1"]').data().date; $('#week1b').attr('value', format_date(date)); var date = $('span[name="w2d7"]').data().date; $('#week2b').attr('value', format_date(date)); } function number_of_unsaved_job(){ var count =0; var total_job = $('div.jobTable').length; var total_saved = $('div.jobTable.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){ if(!confirm("you have unsaved jobs, proceed will lost them")) 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(); debounced_load_timesheet(); }); $('div.nextweek.right').click(function(){ if (number_of_unsaved_job() > 0){ if(!confirm("you have unsaved jobs,proceed will lost them")) 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(); debounced_load_timesheet(); }); $('div.weekly div.weekname.prev >input ').click(function(e){ e.stopPropagation(); }); $('div.weekly div.weekname.prev >input ').change(function(e){ var date = $('#week1b').attr('value'); init_weekdays_by_anchor(date, true); set_week_number(); debounced_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:visible').each(function(i,e){ var value = $(e).html(); if( -1 != value.indexOf(strDate) ) //found { var el = $(e).closest('div.jobTable'); if (el.is(":visible") && el.hasClass('saved')){ var id = el.data().id; var j = bts().job_map[id]; var newj = clone_data_create_new_job(j.get_record(),7);//add 7 days add_new_job_to_map(newj); jobs.push(newj); } } }); }); show_jobs(jobs); debounced_calculate(); }); $('div.weekly div.weekname.next > input').click(function(e){ e.stopPropagation(); }); $('div.weekly div.weekname.next >input ').change(function(e){ e.stopPropagation(); var date = $('#week2b').attr('value'); init_weekdays_by_anchor(date, false); set_week_number(); debounced_load_timesheet(); }); $('div.weekly div.weekname.next').click(function(e){ $(e).find('input').trigger('click'); }); $('div.week1 > div').click(function(e){ e.stopPropagation(); if ($('div.bstart.blink_me').length == 0){ alert("nothing to copy"); return; } if (!confirm ('copy to next week')) return; var new_jobs = []; $('div.bstart.blink_me').each(function(i,e){ var r = copy_single_day_to_next_week(e); if (r != false){ add_new_job_to_map(r); new_jobs.push(r); } }); show_jobs(new_jobs); unblink_all_date(); }); $('div.week1,div.week2').click(function(e){ e.stopPropagation(); $(this).toggleClass('filtered'); do_filter_workspace(); }); function copy_single_day_to_next_week(el){ var tb = $(el).closest('div.jobTable'); if (tb.is(':visible') && tb.hasClass('saved')){ var id = tb.data().id; var j = bts().job_map[id]; var newj = clone_data_create_new_job(j.get_record() , 7); // +7 days return newj; } return false; } function clone_data_create_new_job(val, num_of_shifted_days){ var data = $.extend(true, {}, val);//make a copy num_of_shifted_days = typeof num_of_shifted_days !=='undefined'? num_of_shifted_days: 0;// 0 days //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() + num_of_shifted_days; 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() + num_of_shifted_days; f = new Date(f.setDate(f1)); data.finish = format_date_time(f); } var newj = new Job(data); //return; return newj; } function add_new_job_to_map(newj) { //add to job map newj.newjob_id = "new_" + bts_unique_ID(); if (typeof bts().job_map_new == 'undefined'){ bts().job_map_new = []; } bts().job_map_new[newj.newjob_id] = newj; } 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(); var first_into_view = false; //make sure first row in match is visible $('div.bstart').each(function(i,e){ if ( $(e).is(":visible") ){ var value = $(e).html(); if( -1 != value.indexOf(strDate) ) //found { if ( !first_into_view ){ //scroll to top first_into_view = true; $(e).get(0).scrollIntoView(); } els.push(e); $(e).addClass('blink_me'); } } }); } function unblink_all_date(){ $('div.bstart').removeClass('blink_me'); } $('div.sheettitle h1 span').click(function(){ reset_title_to_today(); load_timesheet(); }); function reset_title_to_today(){ set_today(); init_weekdays(); set_week_number(); } var debounced_load_timesheet = debounce(load_timesheet,1000); 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_jobv1", // action start: format_date(first), finish: format_date(last), }, function(response, status, xhr){ if (response.status =='success'){ display_jobs_after_staff_client_tos_info_ready(response); }else{ alert('error loading job'); hide_loading_jobs(); } }); } function display_jobs_after_staff_client_tos_info_ready(response) { var b = bts(); if ((typeof b.staff_map != "undefined" && Object.keys(b.staff_map).length > 0) && (typeof b.client_map != "undefined" && Object.keys(b.client_map).length > 0) && (typeof b.tos != "undefined" && Object.keys(b.tos).length > 0 ) && (typeof b.earnings_rate != "undefined" && Object.keys(b.earnings_rate).length > 0 )) { var job_map={}; var jobs = []; //map data for each jobTable response.jobs.forEach(function(e){ //job_derive_attr(e); var j = new Job(e) j.saved=true; //this is a record from database; job_map[e.id] = j; jobs.push(j); }); bts().job_map = job_map; //we do works, load timesheets var template = $("#jobv1_item").html(); var html = Mustache.render(template, {jobs:jobs}); $('div.workspace').append(html); hide_loading_jobs(); //filter it if reqired debounced_filter_workspace(); return; } //console.log('wating staff/client/tos info to be ready'); setTimeout(function(){ display_jobs_after_staff_client_tos_info_ready(response); }, 500); //try it half seconds later } function has_txt_hour(str){ if (str == null){ console.warn('null'); return; } var s = str.toLowerCase(); return s.indexOf('hour') != -1; } function show_jobs(jobs){ if (jobs.length >0){ var templ = $("#jobv1_item").html(); var html = Mustache.render(templ, {jobs:jobs}); //job id should be available; var el = $(html); $('div.workspace').append(el); el.get(0).scrollIntoView(); } debounced_calculate(); } 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(); // show_loading_jobs(); } $('div.workinghours').click(function(){ $('div.bts_message_button').trigger('click'); }); function check_workspace_error(){ var els = $('div.workspace').find('.error'); if(els.length >0){ els.get(0).scrollIntoView(); return true; } return false; } $('button[name="confirmschedule"]').click(function(){//TODO: check error before confirm if( check_workspace_error() ){ return; } if (!confirm('sending email to each staff for their job arrangement?')) return; $('div.bts_message .ult-overlay-close-inside').hide(); $('div.bts_message_button').trigger('click'); setTimeout(do_email_jobs, 2000);//2 seconds for dialog to popup }); $('button[name="confirmschedule"]').mouseenter(function(){ $('div.week2').addClass('blink_me'); }) $('button[name="confirmschedule"]').mouseleave(function(){ $('div.week2').removeClass('blink_me'); }) var debounced_filter_workspace = debounce(do_filter_workspace, 1000); $(document).on('click','div.userlist', debounced_filter_workspace); function do_filter_workspace(){ var staffs =[]; $('div.stafflist div.peopleitem :checked').each(function(i, e){ if ($(e).parent().is(':visible')){ 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){ if ($(e).parent().is(':visible')){ var id = $(e).parent().attr('data-id'); //console.log("%o, id=%s", e, id); clients.push(id.substring(1)); } }); filter_workspace(staffs, clients); filter_workspace_by_weeks(); debounced_calculate(); } 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; } //if staffs is empty, we only filter by client if (staffs === undefined || staffs.length ==0){ filter_workspace_by_client(clients); return; } //if clients is empty, we only filter by staff if (clients===undefined || clients.length ==0){ filter_workspace_by_staff(staffs); return; } //filter by both filter_workspace_by_both(staffs, clients); } function filter_workspace_by_staff(staffs) { var class_name='to_be_shown'; //filter some of them; staffs.forEach(function(e){ $('div.workspace div.jobTable[data-staff="' + e + '"]').addClass(class_name); }); $('div.workspace div.jobTable.' + class_name).fadeIn(); $('div.workspace div.jobTable:not(.'+ class_name +')').hide(); $('.' + class_name).removeClass(class_name); } function filter_workspace_by_client(clients) { var class_name='to_be_shown'; //filter some of them; clients.forEach(function(e){ $('div.workspace div.jobTable[data-client="' + e + '"]').addClass(class_name); }); $('div.workspace div.jobTable.' + class_name).fadeIn(); $('div.workspace div.jobTable:not(.'+ class_name +')').hide(); $('.' + class_name).removeClass(class_name); } function filter_workspace_by_both(staffs, clients) { var class_name='hide'; //filter some of them; clients.forEach(function(e){ $('div.workspace div.jobTable:not([data-client="' + e + '"])').addClass(class_name); }); staffs.forEach(function(e){ $('div.workspace div.jobTable:not([data-staff="' + e + '"])').addClass(class_name); }); $('div.workspace div.jobTable.' + class_name).hide(); $('div.workspace div.jobTable:not(.'+ class_name +')').show(); $('.' + class_name).removeClass(class_name); } function filter_workspace_by_weeks(){ var hide_week1 = $('div.week1').hasClass('filtered'); var hide_week2 = $('div.week2').hasClass('filtered'); if (hide_week1 && hide_week2 ){ alert("You are hiding both weeks"); $('div.jobTable').show();//show non-week1 and none-week2 $('div.jobTable.week1job,div.jobTable.week2job').hide(); //hide week1 or week2; }else if (hide_week1){ $('div.jobTable:not(.week1job)').show();//show non-week1 $('div.jobTable.week1job').hide(); //hide week1; }else if (hide_week2){ $('div.jobTable.week2job').hide(); //show non-week2 $('div.jobTable:not(.week2job)').show(); //hide week2 } } var debounced_calculate = debounce(calculate_total_hour_and_money, 2000); function calculate_total_hour_and_money() { //init pays for all staff; var pays={ total: 0, hours: 0, }; $('.stafflist > div.peopleitem').each(function(i,e){ var people = $(this).data().obj; people.reset_summary(); }); $('div.workspace > .divTable.jobTable:visible').each(function(i,e){ var id = $(e).attr('data-id'); var job = bts().job_map[id]; if (id == ''){ //is this a new Job without id? var newjob_id = $(e).attr('data-newjob_id'); if ( typeof newjob_id != "undefined"){ id = newjob_id; job = bts().job_map_new[newjob_id]; } } if (typeof job === 'undefined' || !job.is_job_valid() ) return; var ps = job.get_payment_summary(); pays.total += ps.money; pays.hours += ps.hour; var staff = job.staff; var people = bts().staff_people[staff]; //class People if (people !=false) people.add_payment_summary(ps); }); set_wages(pays.total.toFixed(2)); set_working_hours(pays.hours.toFixed(2)); } function find_staff(login) { var d = $('#p'+login).data(); if (typeof d === 'undefined') return false; return $('#p'+login).data().obj; } $(document).on('change', '.divTableRow input[name="ack"]', function(e) { var el = $(this).closest('.jobTable'); var data = el.data(); data.ack = e.checked? 1: 0; job_mark_dirty(el); }); function job_mark_dirty(el) { el.removeClass('saved'); el.addClass('dirty'); } function job_mark_clean(el) { el.addClass('saved'); el.removeClass('dirty'); } function job_is_week1(t) { var w1_begin = new Date($('span[name="w1d1"]').data().date) ; var w1_end = new Date($('span[name="w1d7"]').data().date); w1_begin.setHours(0,0,0,0); w1_end.setHours(23,59,59); //console.log("week1 begin %o, end %o", w1_begin, w1_end); //w1_end = new Date (w1_end.setDate(w1_end.getDate()+1)); //from 00:00 to 23:59; var me = new Date(t); return (w1_begin <= me && me <= w1_end ); } function job_is_week2(t) { var w2_begin = new Date($('span[name="w2d1"]').data().date); var w2_end = new Date($('span[name="w2d7"]').data().date); w2_begin.setHours(0,0,0,0); w2_end.setHours(23,59,59); var me = new Date(t); return (w2_begin <= me && me <= w2_end ); } function init_ts(){ xero(false); wifi(false); csv(false); show_loading_jobs(); list_staff(); list_clients(); list_tos(); ajax_earning_rate(); //setTimeout(list_staff, 5000); // for testing delayed loading of jobs only //setTimeout(list_clients, 8000); // for testing delayed loading of jobs only //setTimeout(list_tos, 10000); // for testing delayed loading of jobs only init_user_search(); reset_title_to_today(); load_timesheet(); } function do_email_jobs() { var selector = 'div.bts_message div.ult_modal-body'; $(selector).html('Analysis staff jobs ... ok'); var staff = bts().staff.slice(0);//copy this array; var s = staff.pop(); //week2 start var w2_begin = new Date($('span[name="w2d1"]').data().date); var w2_end = new Date($('span[name="w2d7"]').data().date); var start = format_date(w2_begin); var finish = format_date(w2_end); function do_staff(){ var el = $('

Checking ' + s.firstname + "....

"); $(selector).append(el); el[0].scrollIntoView(); $.post(bts().ajax_url, { // POST request _ajax_nonce: bts().nonce, // nonce action: "email_job", // action staff : s.login, start : start, finish: finish, }, function(response, status, xhr){ if (response.status == 'success'){ if (response.sent){ el.append('' + response.emailstatus + ''); }else{ el.append('' + response.emailstatus + ''); } }else{ el.append(' Error[' + response.error + ' ...]'); } }).fail(function(){ el.append('' + 'Network Error occured' + ''); //clear staff pending list, stop further processing s = []; }).always(function(){//next staff if (staff.length >0){ s = staff.pop(); setTimeout(do_staff, 100); //a short delay makes it looks nice }else{ $('div.bts_message .ult-overlay-close-inside').show(); $('div.bts_message .ult-overlay-close-inside').addClass('blink_me'); $('div.week2').removeClass('blink_me'); $(selector).append('All staff confirmed! '); } }); } //execute do_staff(); } 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.options.forEach(function(e){ bts().earnings_rate[e.EarningsRateID]=e; }); //console.log("%o", bts().earnings_rate); }); } function check_duplicate() { var to_be_deleted=[]; //loop through jobs for(var id1 in bts().job_map){ var job1 = bts().job_map[id1]; if (typeof job1.parent != 'undefined') continue; //bypass it it has already found parents job1.compared_as_master = true;//mark it job1.duplicates={}; //console.log('investigating %s' , job1.id); //match job2 for(var id2 in bts().job_map){ var job2 = bts().job_map[id2]; if (typeof job2.compared_as_master != 'undefined') continue; if (typeof job2.parent != "undefined") continue; //it has already parent; //console.log('comareing %s vs %s', job1.id, job2.id); if (is_same_job(job1,job2)){ job2.parent = job1.id; job1.duplicates[id2] = job2; //console.warn("found: %s = %s", job1.id, job2.id); to_be_deleted.push(id2); } } } if (to_be_deleted.length == 0){ alert("No duplicate found!"); return; } bts().to_be_deleted_duplicate = to_be_deleted; //console.log('all-done, found %d duplicates: %o', to_be_deleted.length, to_be_deleted); mark_duplicate(to_be_deleted); } function is_same_job(job1, job2) { if ( (job1.tos == job2.tos) && (job1.staff == job2.staff) && (job1.client == job2.client) && (job1.start == job2.start) && (job1.finish == job2.finish) ) { return true; } return false; } function mark_duplicate(ids) { ids.forEach(function(id){ var selector = '#job_' +id; $(selector).get(0).scrollIntoView(); $(selector).addClass('to_be_deleted_duplicate'); }); } function do_edit_new_job(id) { open_modal('editor'); set_modal_title('editor', "Editing New Job "); var new_job = bts().job_map_new[id]; new_job.editorid = id; //set_modal_data('editor', {jobid: id, job_copy:job_copy}); var html = $('#jobv1_editor').html(); html = Mustache.render(html, new_job); set_modal_content('editor', html); //update GUI dtp_init(); //init editor var e = new JobEditor('#editor_' + id, new_job); //console.log("e is instance of JobEditor %o", e instanceof JobEditor); } function do_edit_job(id) { open_modal('editor'); set_modal_title('editor', "Editing Job: " + id); //make a copy of the job var child = bts().job_map[id]; var job_copy = Object.assign(Object.create(Object.getPrototypeOf(child)), child); //a shallow copy only; job_copy.editorid = bts_random_number(); //set_modal_data('editor', {jobid: id, job_copy:job_copy}); var html = $('#jobv1_editor').html(); html = Mustache.render(html, job_copy); set_modal_content('editor', html); //update GUI dtp_init(); //init editor var e = new JobEditor('#editor_' + job_copy.editorid, job_copy); //console.log("e is instance of JobEditor %o", e instanceof JobEditor); } $( ".boundary_datepicker" ).datepicker(); $( ".boundary_datepicker" ).datepicker("option", "dateFormat", "yy-mm-dd"); $(document).on('click', 'div.clientlist div[name="title"] a', function(e){ e.preventDefault(); e.stopPropagation(); var id = $(this).closest('label.peopleitem').attr('data-id').substring(1); var str = 'https://acaresydncy.com.au/feedback_card/' + id; var name = $(this).html(); if ( confirm ("Email feedback link of : " + name + "\n\n\n" + str + "\n\n\n to helen@acaresydney.com.au?")){ $.post(bts().ajax_url, { // POST request _ajax_nonce: bts().nonce, // nonce action: "email_feedback_url", // action client : id, }, function(response, status, xhr){ //alert('please check your email on the phone and SMS the link to your client'); }).fail(function(){ alert('network error '); }); } }); init_ts(); $('div.divTableHeading div.bsave span.ticon-search').click(do_test); function do_test(){ //TODO: remove this search function open_modal('editor'); set_modal_title('editor', "title"); set_modal_content('editor', $('div.workspace').html()); } /*________________________________________________________________________*/ }); })(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); }); });