timesheet source code
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

1617 lines
44KB

  1. (function ($) {
  2. $(function () {
  3. // http://davidwalsh.name/javascript-debounce-function
  4. function debounce(func, wait, immediate) {
  5. var timeout;
  6. return function () {
  7. var context = this, args = arguments;
  8. var later = function () {
  9. timeout = null;
  10. if (!immediate)
  11. func.apply(context, args);
  12. };
  13. var callNow = immediate && !timeout;
  14. clearTimeout(timeout);
  15. timeout = setTimeout(later, wait);
  16. if (callNow)
  17. func.apply(context, args);
  18. };
  19. };
  20. /*____________________________________________________________________________________*/
  21. class People{
  22. constructor(selector, template, data){
  23. this.selector = selector;
  24. this.data = data;
  25. this.template = template;
  26. // this.sample_people = {
  27. // login: '01515b52-6936-46b2-a000-9ad4cd7a5b50',
  28. // firstname: "first",
  29. // lastname: "last",
  30. // phone: '041122221',
  31. // email: 'abc@gmail.com',
  32. // pay: 0,
  33. // hour: 12,
  34. // OT: 3,
  35. // petrol: 50,
  36. // rating: 1,
  37. // };
  38. this.load_data(this.data);
  39. }
  40. load_data(data){
  41. var template = $(this.template).html();
  42. var html = Mustache.render(template, data);
  43. $(this.selector).html(html);
  44. //save it
  45. $(this.selector).data({obj:this, data:data});
  46. //draw rating star
  47. this.set_ratings(this.data.rating);
  48. this.set_unconfirmed_job(this.data.unconfirmedjob);
  49. }
  50. set_ratings(num){
  51. for (var i=1; i<= 5; i++){
  52. if (i <=num){
  53. $(this.selector + " div[name='rating'] span:nth-child(" +i+ ")").addClass('checked');
  54. }else{
  55. $(this.selector + " div[name='rating'] span:nth-child(" +i+ ")").removeClass('checked');
  56. }
  57. }
  58. this.data.rating = num;
  59. }
  60. set_unconfirmed_job(num){
  61. if( num == 0 )
  62. $(this.selector + " span[name='badge']").hide();
  63. else
  64. $(this.selector + " span[name='badge']").show();
  65. this.data.unconfirmedjob = num;
  66. }
  67. reset_summary() {
  68. this.summary = {
  69. wages : 0,
  70. normal_hour : 0,
  71. ot_hour : 0,
  72. petrol : 0,
  73. };
  74. this.update_summary_in_gui();
  75. }
  76. add_payment_summary(ps)
  77. {
  78. //{ot: false, hour: "2.67", money: "76.90"}
  79. this.summary.wages += ps.money;
  80. if (! ps.ot )
  81. this.summary.normal_hour += ps.hour;
  82. else
  83. this.summary.ot_hour += ps.hour;
  84. this.update_summary_in_gui();
  85. }
  86. update_summary_in_gui()
  87. {
  88. var msg = '$' + this.summary.wages.toFixed(2);
  89. $(this.selector).find('div[name="wages"]').html(msg);
  90. msg = this.summary.normal_hour.toFixed(2) + '+' +this.summary.ot_hour.toFixed(2) + 'hr';
  91. $(this.selector).find('div[name="hours"]').html(msg);
  92. msg = 'petrol:' + this.summary.petrol.toFixed(2) + 'km';
  93. $(this.selector).find('div[name="petrol"]').html(msg);
  94. }
  95. }//end of class People
  96. function bts_staff_html(data){
  97. var template = $('#staff_item').html();
  98. var head = '<div class="peopleitem" id="p'+ data.login +'">';
  99. r = head + '</div>' ;
  100. return r;
  101. }
  102. function bts_client_html(data){
  103. var template = $('#client_item').html();
  104. var head = '<div class="peopleitem" id="p'+ data.login +'">';
  105. r = head + '</div>' ;
  106. return r;
  107. }
  108. function sample_staff(){
  109. for (var i=1; i<100; i++){
  110. var sample_people = {
  111. login: '01515b52-6936-46b2-a000-9ad4cd7a5b50' +i,
  112. firstname: "first"+i,
  113. lastname: "last",
  114. mobile: '041122221' +i,
  115. email: 'abc@gmail.com' + i,
  116. wages: 0,
  117. hour: i,
  118. OT: 3,
  119. petrol: 50 +i,
  120. rating: Math.floor(Math.random() * Math.floor(5)),
  121. unconfirmedjob: Math.floor(Math.random() * Math.floor(30)),
  122. };
  123. var html = bts_staff_html(sample_people);
  124. jQuery('div.stafflist').append(html);
  125. new People("#p" + sample_people.login, sample_people);
  126. }
  127. }
  128. function list_staff() {
  129. show_loading_staff();
  130. $('div.stafflist div.peopleitem').remove();
  131. $.post(bts().ajax_url, { // POST request
  132. _ajax_nonce: bts().nonce, // nonce
  133. action: "list_staff", // action
  134. }).done(function(response, status, xhr){
  135. if (response.status =='success'){
  136. bts().staff = response.users;
  137. response.users.forEach(function(u){
  138. var html = bts_staff_html(u);
  139. jQuery('div.stafflist').append(html);
  140. new People("#p" + u.login,'#staff_item', u);
  141. });
  142. hide_loading_staff();
  143. }else{
  144. alert('error getting staff list');
  145. }
  146. });
  147. }
  148. function list_clients() {
  149. show_loading_client();
  150. $('div.clientlist div.peopleitem').remove(); //clear it
  151. $.post(bts().ajax_url, { // POST request
  152. _ajax_nonce: bts().nonce, // nonce
  153. action: "list_client", // action
  154. }, function(response, status, xhr){
  155. if (response.status =='success'){
  156. bts().client = response.users;
  157. response.users.forEach(function(u){
  158. hide_loading_client();
  159. var html = bts_client_html(u);
  160. jQuery('div.clientlist').append(html);
  161. new People("#p" + u.login, '#client_item' ,u);
  162. });
  163. }else{
  164. alert('error getting Client list');
  165. }
  166. });
  167. }
  168. function show_loading_staff(){
  169. jQuery('div.stafflist img').attr('src', bts().load_user_img).show();
  170. }
  171. function show_loading_client(){
  172. jQuery('div.clientlist img').attr('src', bts().load_user_img).show();
  173. }
  174. function hide_loading_staff(){
  175. jQuery('div.stafflist img').hide();
  176. }
  177. function hide_loading_client(){
  178. jQuery('div.clientlist img').hide();
  179. }
  180. function show_loading_jobs(){
  181. jQuery('div.workspace img').attr('src', bts().load_job_img).show();
  182. }
  183. function hide_loading_jobs(){
  184. jQuery('div.workspace img').hide();
  185. }
  186. function xero(t){
  187. if (t)
  188. $('div.xero i').show();
  189. else
  190. $('div.xero i').hide();
  191. }
  192. function wifi(t){
  193. if (t)
  194. $('div.wifi i').show();
  195. else
  196. $('div.wifi i').hide();
  197. }
  198. function csv(t){
  199. if (t)
  200. $('div.csv i').show();
  201. else
  202. $('div.csv i').hide();
  203. }
  204. function init_user_search(){
  205. $('div.b_search input').keyup(debounce(function(e){
  206. filter_user(e.target);
  207. }, 500));
  208. }
  209. function filter_user(input){
  210. var value = $(input).attr('value');
  211. value = value.toLowerCase();
  212. var selector = get_selector_for_filter_people(input);
  213. $.each( $(selector).find('div.peopleitem'), function(index, e){
  214. //uncheck everyone
  215. $(e).find('input[type="checkbox"]').prop('checked', false);
  216. var html = $(e).find('div[name="title"] a').html();
  217. html = html.toLowerCase();
  218. if (-1 != html.indexOf(value)){//we find it;
  219. $(e).show();
  220. }else{
  221. $(e).hide();
  222. }
  223. });
  224. }
  225. function get_selector_for_filter_people(input){
  226. var selector='';
  227. var role = $(input).attr('placeholder');
  228. if (role == 'staff') //we filter staff
  229. selector = 'div.stafflist';
  230. else if (role = 'client')
  231. selector = 'div.clientlist';
  232. return selector;
  233. }
  234. $(document).on('click', 'div.divTableHead.bdelete', function(){
  235. add_new_empty_job();
  236. });
  237. function get_workspace_start_date(){
  238. return new Date($('span[name="w1d1"]').data().date) ;
  239. }
  240. function get_payroll_start()
  241. {
  242. return new Date(bts().pay_calendar.start + " 00:00:00");
  243. }
  244. // function allow_add_new(){
  245. // var w1_begin = get_workspace_start_date();
  246. // var pay_begin = get_payroll_start();
  247. // return w1_begin > pay_begin;
  248. // }
  249. function add_new_empty_job(){
  250. var o = new Job({empty:true});
  251. $('div.workspace').append(o.el);
  252. o.el.get(0).scrollIntoView();
  253. dtp_init();
  254. }
  255. $(document).on('click', 'div[name="copyschedule"]', function(e){
  256. e.stopPropagation();
  257. add_new_empty_job();
  258. });
  259. $(document).on('click', 'div.divTableCell.bdelete', function(){
  260. var job = $(this).closest('.divTable').data().job;
  261. var el = $(this).closest('div.divTable');
  262. if (job.data.disabled)
  263. return;
  264. if ( job.get_job_id() == '')
  265. el.remove();
  266. else{
  267. if (confirm('delete this job?')){
  268. $.post(bts().ajax_url, { // POST request
  269. _ajax_nonce: bts().nonce, // nonce
  270. action: "delete_job", // action
  271. jobid: job.data.id,
  272. }, function(response, status, xhr){
  273. if (response.status=='success'){
  274. el.addClass('blink_me');
  275. el.fadeOut(900);
  276. setTimeout(function(){
  277. el.remove();
  278. }, 900);
  279. }else{
  280. alert( 'error saving data, please check your network');
  281. }
  282. });
  283. }
  284. }
  285. });
  286. $(document).on('mouseenter', 'div.divTableCell', function(){
  287. $(this).closest('div.divTable').addClass('highlight');
  288. });
  289. $(document).on('mouseleave', 'div.divTableCell', function(){
  290. $(this).closest('div.divTable').removeClass('highlight');
  291. });
  292. $(document).on('click', 'div.workspace span.ticon.ticon-save', function(){
  293. var table = $(this).closest('div.divTable');
  294. table.data().job.do_save_record();
  295. });
  296. $(document).on('click', '.divTableHeading span.ticon.ticon-save', function(){
  297. //save all
  298. $('div.workspace span.ticon.ticon-save').each(function (i,e){
  299. if ($(this).is(":visible"))
  300. $(this).trigger('click');
  301. });
  302. });
  303. $(document).on('click', 'span.ticon.ticon-copy', function(){
  304. if (!confirm("make a copy of this job?"))
  305. return;
  306. var table = $(this).closest('div.divTable');
  307. var job = table.data().job;
  308. if (job.data.disabled){
  309. alert("Job disabled, cannot copy");
  310. return;
  311. }
  312. var newj = clone_data_create_new_job(job.get_record_from_ui());
  313. $('div.workspace').append(newj.el);
  314. newj.el.get(0).scrollIntoView();//make sure it's visible;
  315. newj.mark_highlight_me(1000);//for 1 second;
  316. dtp_init();
  317. });
  318. class Job{ //save data for the record, and display it as GUI
  319. constructor(data){
  320. var html = jQuery("#job_item").html();
  321. this.el = $(html);
  322. //jQuery('div.workspace').append(this.el);
  323. this.load_data(data);
  324. this.init_start_rating();
  325. }
  326. init_start_rating(){
  327. var self = this;
  328. this.el.find("div.brating span").click(function(){
  329. if (self.data.disabled)
  330. return;
  331. var r = $(this).attr('data-rating');
  332. self.mark_dirty();
  333. self.set_rating(r);
  334. })
  335. this.el.find("div.brating").mouseenter(function(){
  336. if (self.data.disabled)
  337. return;
  338. //change to all hollow star
  339. $(this).find('span').html('☆');
  340. });
  341. this.el.find("div.brating").mouseleave(function(){
  342. if (self.data.disabled)
  343. return;
  344. self.set_rating(self.data.rating);
  345. });
  346. }
  347. load_data(data)
  348. {
  349. //check is disabled?
  350. this.data = data;
  351. this.is_disabled();
  352. //save to html element
  353. this.el.data({job:this, data:data});
  354. //draw GUI
  355. this.clear_err_msg();
  356. this.set_job_id(data.id);
  357. this.set_tos(data.tos);
  358. this.set_start(data.start);
  359. this.set_finish(data.finish);
  360. this.set_rate(data.rate);
  361. this.set_staff(data.staff);
  362. this.set_client(data.client);
  363. this.set_ack(data.ack);
  364. this.set_rating(data.rating);
  365. //draw GUI by other
  366. this.mark_disabled();
  367. this.mark_dirty_on_new_record(data);
  368. this.mark_week_color();
  369. this.validate(); //also triggers mark errors
  370. }
  371. is_disabled()
  372. {
  373. var s = new Date(this.data.start);
  374. var pay_roll_start = get_payroll_start();
  375. this.data.disabled = s < pay_roll_start;
  376. }
  377. get_job_id(){
  378. return this.el.find('input[name="id"]').attr('value');
  379. }
  380. set_job_id(val){
  381. return this.el.find('input[name="id"]').attr('value', val);
  382. }
  383. get_tos()
  384. {
  385. return this.el.find('div.btos select').children("option:selected").val();
  386. }
  387. set_tos(val)
  388. {
  389. if (this.data.disabled)
  390. this.el.find('div.btos select').prop('disabled', 'disabled');
  391. if (typeof(val) =="undefined")
  392. return;
  393. this.el.find('div.btos select option[value="'+val+'"]').prop('selected',true);
  394. }
  395. get_start(){
  396. return this.el.find('div.bstart input').attr('value');
  397. }
  398. set_start(val)
  399. {
  400. if (this.data.disabled)
  401. this.el.find('div.bstart input').prop('disabled', 'disabled');
  402. if (typeof(val) =="undefined")
  403. return;
  404. this.el.find('div.bstart input').attr('value', val);
  405. }
  406. get_finish()
  407. {
  408. return this.el.find('div.bfinish input').attr('value');
  409. }
  410. set_finish(val)
  411. {
  412. if (this.data.disabled)
  413. this.el.find('div.bfinish input').prop('disabled', 'disabled');
  414. if (typeof(val) == "undefined")
  415. return;
  416. this.el.find('div.bfinish input').attr('value', val);
  417. }
  418. get_rate()
  419. {
  420. return this.el.find('div.brate select').children("option:selected").val();
  421. }
  422. set_rate(val)
  423. {
  424. if (this.data.disabled)
  425. this.el.find('div.brate select').prop('disabled', 'disabled');
  426. if (typeof(val) =="undefined")
  427. return;
  428. this.el.find('div.brate select option[value="'+val+'"]').prop('selected',true);
  429. }
  430. get_staff()
  431. {
  432. return this.el.find('div.bstaff select').children("option:selected").val();
  433. }
  434. set_staff(val)
  435. {
  436. if (this.data.disabled)
  437. this.el.find('div.bstaff select').prop('disabled', 'disabled');
  438. if (typeof(val) =="undefined")
  439. return;
  440. this.el.find('div.bstaff select option[value="'+val+'"]').prop('selected',true);
  441. }
  442. get_client()
  443. {
  444. return this.el.find('div.bclient select').children("option:selected").val();
  445. }
  446. set_client(val)
  447. {
  448. if (this.data.disabled)
  449. this.el.find('div.bclient select').prop('disabled', 'disabled');
  450. if (typeof(val) =="undefined")
  451. return;
  452. this.el.find('div.bclient select option[value="'+val+'"]').prop('selected',true);
  453. }
  454. get_ack()
  455. {
  456. return this.el.find('div.bconfirmed input:checked').length > 0;
  457. }
  458. set_ack(val)
  459. {
  460. if (this.data.disabled)
  461. this.el.find('div.bconfirmed input').prop('disabled', 'disabled');
  462. if (typeof(val) =="undefined")
  463. return;
  464. return this.el.find('div.bconfirmed input').prop('checked', val!=0);
  465. }
  466. get_rating(){
  467. var count =0;
  468. this.el.find('div.brating span').each(function(i,e){
  469. if ($(e).html()=='★')
  470. count +=1;
  471. });
  472. return count;
  473. }
  474. set_rating(num){
  475. if (!(1 <= num && num <=5))
  476. return;
  477. this.el.find('div.brating span').each(function(i,e){
  478. var rating = $(e).attr('data-rating');
  479. var rating = parseInt(rating);
  480. if (rating <= num)
  481. $(e).html('★');
  482. else
  483. $(e).html('☆');
  484. });
  485. }
  486. get_record_from_ui(){
  487. var record = {};
  488. record.id = this.get_job_id();
  489. record.tos = this.get_tos();
  490. record.start = this.get_start();
  491. record.finish = this.get_finish();
  492. record.rate = this.get_rate();
  493. record.staff = this.get_staff();
  494. record.client = this.get_client();
  495. record.ack = this.get_ack();
  496. record.rating = this.get_rating();
  497. return record;
  498. }
  499. do_save_record(){
  500. var self = this;
  501. $.post(bts().ajax_url, { // POST request
  502. _ajax_nonce: bts().nonce, // nonce
  503. action: "save_job", // action
  504. record: this.get_record_from_ui(),
  505. }, function(response, status, xhr){
  506. if (response.status=='success'){
  507. self.load_data(response.newdata);
  508. self.mark_saved();
  509. self.mark_old();
  510. }else{
  511. alert( 'error saving data, please check your network');
  512. }
  513. });
  514. }
  515. mark_dirty_on_new_record(data){
  516. if (typeof(data.id) === 'undefined' || data.id == ''){
  517. this.mark_dirty();
  518. this.mark_new();
  519. }
  520. else{
  521. this.mark_saved();
  522. }
  523. }
  524. mark_dirty() //need save
  525. {
  526. var d = this.el.find('.bsave');
  527. d.removeClass('saved');
  528. d.addClass('blink_me');
  529. setTimeout(function(){
  530. d.removeClass('blink_me');
  531. d.removeClass('saved');
  532. },1000);
  533. }
  534. mark_saved()
  535. {
  536. var d = this.el.find('.bsave');
  537. d.addClass('blink_me');
  538. setTimeout(function(){
  539. d.removeClass('blink_me');
  540. d.addClass('saved');
  541. },1000);
  542. }
  543. mark_disabled()
  544. {
  545. if (this.data.disabled)
  546. this.el.addClass('disabled');
  547. else
  548. this.el.removeClass('disabled');
  549. }
  550. //newly created empty record
  551. mark_new()
  552. {
  553. this.el.addClass('emptyrecord');
  554. }
  555. mark_old()
  556. {
  557. this.el.removeClass('emptyrecord');
  558. }
  559. mark_highlight_me(ms){
  560. this.el.addClass('blink_me');
  561. this.el.addClass('highlight');
  562. this.el.addClass('newcopy');
  563. var self = this;
  564. setTimeout(function(){
  565. self.el.removeClass('blink_me');
  566. self.el.removeClass('highlight');
  567. self.el.removeClass('newcopy');
  568. },ms);
  569. }
  570. is_start_valid(){
  571. var s = this.get_start();
  572. return is_valid_date_str(s);
  573. }
  574. is_finish_valid(){
  575. var f = this.get_finish();
  576. if (!is_valid_date_str(f))
  577. return false;
  578. }
  579. is_finish_resonable(){
  580. var f = this.get_finish();
  581. if (!is_valid_date_str(f))
  582. return false;
  583. var s = this.get_start();
  584. s = new Date(s);
  585. f = new Date(f);
  586. return (s < f);
  587. }
  588. validate()
  589. {
  590. var ok_time = this.validate_start() &&
  591. this.validate_finish(); //finish might not be executed, if start is wrong
  592. var ok_tos = this.validate_tos(); //make sure this validate is executed;
  593. var ok_rate = this.validate_rate() ; //make sure this validate is executed
  594. var ok = ok_time && ok_tos && ok_rate;
  595. if (ok){
  596. this.el.removeClass('invalidjob');
  597. }else{
  598. this.el.addClass('invalidjob');
  599. }
  600. return ok;
  601. }
  602. validate_start(){
  603. var str = this.get_start();
  604. if ( is_valid_date_str(str) ){
  605. this.mark_start_valid();
  606. this.set_err_msg_start('');
  607. return true;
  608. }else{
  609. this.mark_start_invalid();
  610. this.set_err_msg_start('wrong date');
  611. return false;
  612. }
  613. }
  614. validate_finish()
  615. {
  616. var str = this.get_finish();
  617. if (! is_valid_date_str(str)){
  618. this.set_err_msg_finish('wrong date');
  619. this.mark_finish_invalid();
  620. return false;
  621. }
  622. if (!this.is_finish_resonable()){
  623. this.set_err_msg_finish("older than start")
  624. this.mark_finish_invalid();
  625. return false;
  626. }
  627. this.mark_finish_valid();
  628. this.set_err_msg_finish('');
  629. return true;
  630. }
  631. validate_rate()
  632. {
  633. var rate_info = this.get_rate_info_by_id(this.get_rate());
  634. if ( rate_info.RatePerUnit <= 0){
  635. this.set_err_msg_rate('bad rate');
  636. this.mark_rate_invalid();
  637. return false;
  638. }
  639. // if (this.get_rate() != this.data.rate){
  640. // this.set_err_msg_rate('rate@XeroOauth1 inactive ' + this.data.rate);
  641. // this.mark_rate_invalid();
  642. // this.mark_dirty();
  643. // return false;
  644. // }
  645. this.set_err_msg_rate('');
  646. this.mark_rate_valid();
  647. return true;
  648. }
  649. validate_tos(){
  650. // if (this.get_tos() != this.data.tos){
  651. // this.set_err_msg_tos('require NDIS ' + this.data.tos);
  652. // this.mark_tos_invalid();
  653. // this.mark_dirty();
  654. // console.log('tos mark dirty');
  655. // return false;
  656. // }
  657. this.set_err_msg_tos('');
  658. this.mark_tos_valid();
  659. return true;
  660. }
  661. clear_err_msg(){
  662. this.el.find('.divTableRow.errmsg > div').html('');
  663. }
  664. set_err_msg_start(str)
  665. {
  666. this.el.find('div.bstart_err').html(str);
  667. }
  668. set_err_msg_finish(str)
  669. {
  670. this.el.find('div.bfinish_err').html(str);
  671. }
  672. set_err_msg_rate(str)
  673. {
  674. this.el.find('div.brate_err').html(str);
  675. }
  676. set_err_msg_save(str)
  677. {
  678. this.el.find('div.bsave_err').html(str);
  679. }
  680. set_err_msg_tos(str)
  681. {
  682. this.el.find('div.btos_err').html(str);
  683. }
  684. mark_tos_valid(){
  685. this.el.find('div.btos select').removeClass('invalid');
  686. }
  687. mark_tos_invalid(){
  688. this.el.find('div.btos select').addClass('invalid');
  689. }
  690. mark_start_valid(){
  691. this.el.find('div.bstart input').removeClass('invalid');
  692. }
  693. mark_start_invalid(){
  694. this.el.find('div.bstart input').addClass('invalid');
  695. }
  696. mark_finish_valid(){
  697. this.el.find('div.bfinish input').removeClass('invalid');
  698. }
  699. mark_finish_invalid(){
  700. this.el.find('div.bfinish input').addClass('invalid');
  701. }
  702. mark_rate_valid(){
  703. this.el.find('div.brate select').removeClass('invalid');
  704. }
  705. mark_rate_invalid(){
  706. this.el.find('div.brate select').addClass('invalid');
  707. }
  708. mark_week_color(){
  709. this.el.find('div.brating').removeClass('week1color');
  710. this.el.find('div.brating').removeClass('week2color');
  711. if (this.is_week1()){
  712. this.el.find('div.brating').addClass('week1color');
  713. }else if (this.is_week2()){
  714. this.el.find('div.brating').addClass('week2color');
  715. }
  716. }
  717. is_week1()
  718. {
  719. var w1_begin = new Date($('span[name="w1d1"]').data().date) ;
  720. var w1_end = new Date($('span[name="w1d7"]').data().date);
  721. w1_begin.setHours(0,0,0,0);
  722. w1_end.setHours(23,59,59);
  723. //console.log("week1 begin %o, end %o", w1_begin, w1_end);
  724. //w1_end = new Date (w1_end.setDate(w1_end.getDate()+1)); //from 00:00 to 23:59;
  725. var me = new Date(this.data.start);
  726. return (w1_begin <= me && me <= w1_end );
  727. }
  728. is_week2()
  729. {
  730. var w2_begin = new Date($('span[name="w2d1"]').data().date);
  731. var w2_end = new Date($('span[name="w2d7"]').data().date);
  732. w2_begin.setHours(0,0,0,0);
  733. w2_end.setHours(23,59,59);
  734. var me = new Date(this.data.start);
  735. return (w2_begin <= me && me <= w2_end );
  736. }
  737. get_payment_summary(){
  738. var result ={};
  739. result.ot = this.get_is_high_pay();
  740. result.hour = this.get_working_duration();
  741. result.money = this.get_wages();
  742. return result;
  743. }
  744. get_is_high_pay()
  745. {
  746. var rate_info = this.get_rate_info_by_id(this.get_rate());
  747. return this.is_high_pay_hour(rate_info);
  748. }
  749. get_working_duration()
  750. {
  751. //finish - start
  752. var f = new Date(this.get_finish());
  753. var s = new Date(this.get_start());
  754. var diff = f.getTime() - s.getTime();
  755. var hours = Math.floor(diff / 1000 / 60 / 60);
  756. diff -= hours * 1000 * 60 * 60;
  757. var minutes = Math.floor(diff / 1000 / 60);
  758. var minute_to_hour = minutes/60;
  759. return (hours + minute_to_hour);
  760. }
  761. get_wages(){
  762. var hour = this.get_working_duration();
  763. var rate_info = this.get_rate_info_by_id(this.get_rate());
  764. return hour * rate_info.RatePerUnit;
  765. }
  766. get_rate_info_by_id(id){
  767. return bts().earnings_rate[id];
  768. // var rate_info = {};
  769. // var rates = bts().earnings_rate;
  770. // for(var i =0; i< rates.length; i++){
  771. // var r = rates[i];
  772. // if(r.EarningsRateID == id){
  773. // rate_info = $.extend(true,{}, r);//make a copy
  774. // break;
  775. // }
  776. // }
  777. // return rate_info;
  778. }
  779. is_high_pay_hour(rate_info){
  780. var keywords =bts().high_pay_keywords;
  781. var found = false;
  782. keywords.forEach(function(e){
  783. if (-1 != rate_info.Name.toLowerCase().indexOf(e.toLowerCase()) )
  784. found = true;
  785. });
  786. return found;
  787. }
  788. }//end of class Job
  789. //global GUI summary
  790. function get_wages()
  791. {
  792. var txt = $('div.wages div').html();
  793. return parseInt(txt);
  794. }
  795. function set_wages(num){
  796. $('div.wages div').html(num);
  797. }
  798. function set_working_hours(num){
  799. $('input#woh').attr('value', num);
  800. }
  801. function get_working_hours(){
  802. var txt = $('input#woh').attr('value');
  803. return parseFloat(txt);
  804. }
  805. //modal box
  806. function set_modal_title(selector, title){
  807. var s = 'div.bts_'+ selector +' .ult_modal-title';
  808. $(s).html(title);
  809. }
  810. function set_modal_content(selector, content){
  811. var s = 'div.bts_'+ selector +' div.ult_modal-body.ult-html';
  812. $(s).html(content);
  813. }
  814. function open_modal (selector){
  815. var s='div.bts_'+selector+'_button';
  816. $(s).trigger('click');
  817. }
  818. // setTimeout(function(){
  819. // set_modal_title('warning', 'suck title');
  820. // set_modal_content('warning', 'fucking details');
  821. // //open_modal('warning');
  822. // }, 1000);
  823. //
  824. // setTimeout(function(){
  825. // set_modal_title('error', 'error title');
  826. // set_modal_content('error', 'error details');
  827. // //open_modal('error');
  828. // }, 5000);
  829. $(document).on('mouseenter', 'div.week1 div', function(){
  830. $(this).addClass('blink_me');
  831. get_week2_partner(this).addClass('blink_me');
  832. blink_same_date_by_div(this);
  833. });
  834. $(document).on('mouseleave', 'div.week1 div', function(){
  835. $(this).removeClass('blink_me');
  836. get_week2_partner(this).removeClass('blink_me');
  837. unblink_all_date();
  838. });
  839. function get_week2_partner(div){
  840. var index = $(div).index()+1;
  841. return $('div.week2 div:nth-child('+index+')');
  842. }
  843. function init_weekdays(){
  844. var curr = new Date; // get current date
  845. init_weekdays_by_anchor(curr, true);
  846. return;
  847. }
  848. function init_weekdays_by_anchor(anchor, is_week1){
  849. var curr = new Date(anchor); // get the date;
  850. if (!is_week1){ //it is week2, shift for 7 days;
  851. curr.setDate(curr.getDate() -7); //curr will be changed;
  852. }
  853. var first = curr.getDate() - curr.getDay() + 1; //+1 we want Mon as first
  854. var last = first + 6; // last day is the first day + 6
  855. if (curr.getDay() == 0 ){// it's Sunday;
  856. last = curr.getDate();
  857. first = last - 6;
  858. }
  859. var pos = 1; //first lot
  860. for (var i=first; i<=last; i++)
  861. {
  862. var now = new Date(curr);
  863. var d1 = new Date(now.setDate(i));
  864. d1.setHours(0,0,0,0); //0 hour, 0 minute, 0 second, 0 milliseconds,
  865. now = new Date(curr);
  866. var d2 = new Date(now.setDate(i+7));
  867. d2.setHours(0,0,0,0);
  868. set_day_number(1,pos, d1); //week 1
  869. set_day_number(2,pos, d2); //week 2
  870. pos +=1;
  871. }
  872. }
  873. function set_day_number(week, index, date){
  874. var selector = 'span[name="w'+week+'d'+index+'"]';
  875. $(selector).html(date.getDate());
  876. $(selector).data({date:date});
  877. //console.log('set w%d-d%d %o', week,index,date);
  878. }
  879. function set_today(){
  880. var selector = 'div.sheettitle span[name="today"]';
  881. var curr = new Date;
  882. $(selector).html(format_date(curr));
  883. }
  884. Date.prototype.get_week_number = function(){
  885. var d = new Date(Date.UTC(this.getFullYear(), this.getMonth(), this.getDate()));
  886. var dayNum = d.getUTCDay() || 7;
  887. d.setUTCDate(d.getUTCDate() + 4 - dayNum);
  888. var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1));
  889. return Math.ceil((((d - yearStart) / 86400000) + 1)/7)
  890. };
  891. function set_week_number(){
  892. var date = $('span[name="w1d1"]').data().date;
  893. //console.log("date %o", date);
  894. var num = date.get_week_number();
  895. $('div.weekly span[name="week1"]').html(num);
  896. $('div.weekly span[name="week2"]').html(num+1);
  897. set_week_boundry();
  898. }
  899. function set_week_boundry()
  900. {
  901. var date = $('span[name="w1d1"]').data().date;
  902. $('#week1b').attr('value', format_date(date));
  903. var date = $('span[name="w2d7"]').data().date;
  904. $('#week2b').attr('value', format_date(date));
  905. }
  906. function number_of_unsaved_job(){
  907. var count =0;
  908. var total_job = $('div.bsave').length -1;//remove table header
  909. var total_saved = $('div.bsave.saved').length;
  910. var empty = $('div.emptyrecord').length;
  911. count = total_job - total_saved - empty;
  912. return count;
  913. }
  914. $('div.prevweek.left').click(function(){
  915. if (number_of_unsaved_job() > 0){
  916. if(!confirm("you have unsaved jobs, proceed will lost them"))
  917. return;
  918. }
  919. $('div.weekdays span.weekday').each(function(i, e){
  920. var date = $(e).data().date;
  921. var newdate = new Date(date.setDate(date.getDate() -7 ));
  922. $(e).html(newdate.getDate());
  923. $(e).data({data:newdate});
  924. });
  925. set_week_number();
  926. debounced_load_timesheet();
  927. });
  928. $('div.nextweek.right').click(function(){
  929. if (number_of_unsaved_job() > 0){
  930. if(!confirm("you have unsaved jobs,proceed will lost them"))
  931. return;
  932. }
  933. $('div.weekdays span.weekday').each(function(i, e){
  934. var date = $(e).data().date;
  935. var newdate = new Date(date.setDate(date.getDate() +7 ));
  936. $(e).html(newdate.getDate());
  937. $(e).data({data:newdate});
  938. });
  939. set_week_number();
  940. debounced_load_timesheet();
  941. });
  942. $('div.weekly div.weekname.prev >input ').click(function(e){
  943. e.stopPropagation();
  944. });
  945. $('div.weekly div.weekname.prev >input ').change(function(e){
  946. var date = $('#week1b').attr('value');
  947. init_weekdays_by_anchor(date, true);
  948. set_week_number();
  949. debounced_load_timesheet();
  950. });
  951. $('div.weekly div.weekname.prev').click(function(){
  952. if (!confirm ('copy entire week to next week? '))
  953. return;
  954. var jobs = [];
  955. var job_els =[];
  956. $('div.week1 >div').each(function(i,e){
  957. var date = new Date($(e).find('span.weekday').data().date);
  958. var strDate = format_date(date); //yyyy-mm-dd
  959. $('div.bstart input').each(function(i,e){
  960. var value = $(e).attr('value');
  961. if( -1 != value.indexOf(strDate) ) //found
  962. {
  963. var el = $(e).closest('div.divTable');
  964. if (el.is(":visible")){
  965. var j = el.data().job;
  966. if (! j.data.disabled ){
  967. var newj = clone_data_create_new_job(j.get_record_from_ui(),7);//add 7 days
  968. job_els.push(newj.el);
  969. }
  970. }
  971. }
  972. });
  973. });
  974. show_jobs(job_els);
  975. debounced_calculate();
  976. alert("Copied .. (" + job_els.length + ") jobs");
  977. });
  978. $('div.weekly div.weekname.next > input').click(function(e){
  979. e.stopPropagation();
  980. });
  981. $('div.weekly div.weekname.next >input ').change(function(e){
  982. e.stopPropagation();
  983. var date = $('#week2b').attr('value');
  984. init_weekdays_by_anchor(date, false);
  985. set_week_number();
  986. debounced_load_timesheet();
  987. });
  988. $('div.weekly div.weekname.next').click(function(e){
  989. $(e).find('input').trigger('click');
  990. });
  991. $('div.week1 > div').click(function(e){
  992. e.stopPropagation();
  993. if ($('div.bstart input.blink_me').length == 0){
  994. alert("nothing to copy");
  995. return;
  996. }
  997. if (!confirm ('copy to next week?'))
  998. return;
  999. var jobs_el = [];
  1000. $('div.bstart input.blink_me').each(function(i,e){
  1001. var r = copy_single_day_to_next_week(e);
  1002. if (r != false)
  1003. jobs_el.push(r.el);
  1004. });
  1005. show_jobs(jobs_el);
  1006. unblink_all_date();
  1007. alert('Copied (' +jobs_el.length + ") jobs");
  1008. });
  1009. $('div.week1,div.week2').click(function(e){
  1010. e.stopPropagation();
  1011. $(this).toggleClass('filtered');
  1012. do_filter_workspace();
  1013. });
  1014. function copy_single_day_to_next_week(el){
  1015. var tb = $(el).closest('div.divTable');
  1016. if (tb.is(':visible')){
  1017. var j = $(tb).data().job;
  1018. if (j.data.disabled)
  1019. return false;
  1020. var newj = clone_data_create_new_job(j.get_record_from_ui() , 7); // +7 days
  1021. return newj;
  1022. }
  1023. return false;
  1024. }
  1025. function clone_data_create_new_job(val, num_of_shifted_days){
  1026. var data = $.extend(true, {}, val);//make a copy
  1027. num_of_shifted_days = typeof num_of_shifted_days !=='undefined'? num_of_shifted_days: 0;// 0 days
  1028. //reset
  1029. data.id='';
  1030. data.ack = 0;
  1031. data.rating = 0;
  1032. if (is_valid_date_str(data.start)){
  1033. var s = new Date(data.start);
  1034. var s1 = s.getDate() + num_of_shifted_days;
  1035. s = new Date(s.setDate(s1));
  1036. data.start = format_date_time(s);
  1037. }
  1038. if (is_valid_date_str(data.finish)){
  1039. var f = new Date(data.finish);
  1040. var f1 = f.getDate() + num_of_shifted_days;
  1041. f = new Date(f.setDate(f1));
  1042. data.finish = format_date_time(f);
  1043. }
  1044. var newj = new Job(data);
  1045. return newj;
  1046. }
  1047. function is_valid_date_str(val){
  1048. var d = new Date(val);
  1049. if (d.toString()== 'Invalid Date')
  1050. return false;
  1051. return true;
  1052. }
  1053. function blink_same_date_by_div(div){
  1054. var date = new Date($(div).find('span.weekday').data().date);
  1055. blink_same_date(date);
  1056. }
  1057. function blink_same_date(date){
  1058. var strDate = format_date(date); //yyyy-mm-dd
  1059. var els=[];
  1060. unblink_all_date();
  1061. $('div.bstart input:visible').each(function(i,e){
  1062. var value = $(e).attr('value');
  1063. if( -1 != value.indexOf(strDate) ) //found
  1064. {
  1065. els.push(e);
  1066. $(e).addClass('blink_me');
  1067. }
  1068. });
  1069. }
  1070. function unblink_all_date(){
  1071. $('div.bstart input').removeClass('blink_me');
  1072. }
  1073. $('div.sheettitle h1 span').click(function(){
  1074. reset_title_to_today();
  1075. load_timesheet();
  1076. });
  1077. function reset_title_to_today(){
  1078. set_today();
  1079. init_weekdays();
  1080. set_week_number();
  1081. }
  1082. var debounced_load_timesheet = debounce(load_timesheet,1000);
  1083. function load_timesheet()
  1084. {
  1085. clear_workspace();
  1086. if(typeof bts().staff == 'undefined' || typeof bts().client == 'undefined' || bts().earnings_rate == 'undefined' ){
  1087. setTimeout(do_load_time_sheet,500);
  1088. }else{
  1089. do_load_time_sheet();
  1090. }
  1091. function do_load_time_sheet(){
  1092. var first = $('span[name="w1d1"]').data().date;
  1093. var last = $('span[name="w2d7"]').data().date;
  1094. $.post(bts().ajax_url, { // POST request
  1095. _ajax_nonce: bts().nonce, // nonce
  1096. action: "list_job", // action
  1097. start: format_date(first),
  1098. finish: format_date(last),
  1099. }, function(response, status, xhr){
  1100. if (response.status =='success'){
  1101. var job_els = [];
  1102. response.jobs.forEach(function(job){
  1103. //console.log('loading job... %o', job);
  1104. var o = new Job(job);
  1105. job_els.push(o.el);
  1106. });
  1107. show_jobs(job_els, 'in-ajax=true');
  1108. //filter it if reqired
  1109. debounced_filter_workspace();
  1110. }else{
  1111. alert('error loading job');
  1112. }
  1113. hide_loading_jobs();
  1114. });
  1115. }
  1116. }
  1117. function show_jobs(job_els, in_ajax){
  1118. if (job_els.length >0){
  1119. $('div.workspace').append(job_els);
  1120. job_els[0].get(0).scrollIntoView();
  1121. console.log('loading ... %d jobs', job_els.length);
  1122. }
  1123. if (typeof in_ajax =='undefined')
  1124. dtp_init();
  1125. }
  1126. function format_date(date){
  1127. var dd = date.getDate();
  1128. var mm = date.getMonth() + 1; //January is 0!
  1129. var yyyy = date.getFullYear();
  1130. if (dd < 10) {
  1131. dd = '0' + dd;
  1132. }
  1133. if (mm < 10) {
  1134. mm = '0' + mm;
  1135. }
  1136. return yyyy + '-' + mm + '-' +dd ;
  1137. }
  1138. function format_date_time(date){
  1139. var strdate = format_date(date);
  1140. var hh = date.getHours();
  1141. if (hh<10){
  1142. hh= '0' + hh;
  1143. }
  1144. var mm = date.getMinutes();
  1145. if (mm<10){
  1146. mm='0' + mm;
  1147. }
  1148. return strdate + ' ' + hh + ":" + mm;
  1149. }
  1150. function clear_workspace()//clear all timesheet jobs
  1151. {
  1152. $('div.workspace > div.divTable').remove();
  1153. //clear datetime picker
  1154. $('div.xdsoft_datetimepicker').remove();
  1155. //
  1156. show_loading_jobs();
  1157. }
  1158. $('div.workinghours').click(function(){
  1159. $('div.bts_message_button').trigger('click');
  1160. });
  1161. $('button[name="confirmschedule"]').click(function(){
  1162. if (!confirm('sending email to each staff for their job arrangement?'))
  1163. return;
  1164. $('div.bts_message .ult-overlay-close-inside').hide();
  1165. $('div.bts_message_button').trigger('click');
  1166. setTimeout(do_email_jobs, 2000);//2 seconds for dialog to popup
  1167. });
  1168. $('button[name="confirmschedule"]').mouseenter(function(){
  1169. $('div.week2').addClass('blink_me');
  1170. })
  1171. $('button[name="confirmschedule"]').mouseleave(function(){
  1172. $('div.week2').removeClass('blink_me');
  1173. })
  1174. var debounced_filter_workspace = debounce(do_filter_workspace, 1000);
  1175. $(document).on('click','div.userlist', debounced_filter_workspace);
  1176. function do_filter_workspace(){
  1177. var staffs =[];
  1178. $('div.stafflist div.peopleitem :checked').each(function(i, e){
  1179. if ($(e).parent().is(':visible')){
  1180. var id = $(e).parent().attr('data-id');
  1181. //console.log("%o, id=%s", e, id);
  1182. staffs.push(id.substring(1));
  1183. }
  1184. });
  1185. var clients =[];
  1186. $('div.clientlist div.peopleitem :checked').each(function(i, e){
  1187. if ($(e).parent().is(':visible')){
  1188. var id = $(e).parent().attr('data-id');
  1189. //console.log("%o, id=%s", e, id);
  1190. clients.push(id.substring(1));
  1191. }
  1192. });
  1193. filter_workspace(staffs, clients);
  1194. filter_workspace_by_weeks();
  1195. debounced_calculate();
  1196. }
  1197. function filter_workspace(staffs, clients){
  1198. //if both array is empty
  1199. if( (staffs === undefined || staffs.length ==0) &&
  1200. (clients===undefined || clients.length ==0)){
  1201. //show all
  1202. $('div.workspace div.divTable').show();
  1203. return;
  1204. }
  1205. //if staffs is empty, we only filter by client
  1206. if (staffs === undefined || staffs.length ==0){
  1207. filter_workspace_by_client(clients);
  1208. return;
  1209. }
  1210. //if clients is empty, we only filter by staff
  1211. if (clients===undefined || clients.length ==0){
  1212. filter_workspace_by_staff(staffs);
  1213. return;
  1214. }
  1215. //filter by both
  1216. filter_workspace_by_both(staffs, clients);
  1217. }
  1218. function filter_workspace_by_staff(staffs)
  1219. {
  1220. //filter some of them;
  1221. $('div.workspace div.divTable').each(function(i,e){
  1222. var job = $(e).data().job;
  1223. var s = job.get_staff();
  1224. if (staffs.indexOf(s) ==-1)
  1225. $(this).fadeOut();
  1226. else
  1227. $(this).fadeIn();
  1228. });
  1229. }
  1230. function filter_workspace_by_client(clients)
  1231. {
  1232. //filter some of them;
  1233. $('div.workspace div.divTable').each(function(i,e){
  1234. var job = $(e).data().job;
  1235. var c = job.get_client();
  1236. if (clients.indexOf(c) ==-1)
  1237. $(this).fadeOut();
  1238. else
  1239. $(this).fadeIn();
  1240. });
  1241. }
  1242. function filter_workspace_by_both(staffs, clients)
  1243. {
  1244. //filter some of them;
  1245. $('div.workspace div.divTable').each(function(i,e){
  1246. var job = $(e).data().job;
  1247. var s = job.get_staff();
  1248. var c = job.get_client();
  1249. if (staffs.indexOf(s) ==-1 || clients.indexOf(c) ==-1)
  1250. $(this).fadeOut();
  1251. else
  1252. $(this).fadeIn();
  1253. });
  1254. }
  1255. function filter_workspace_by_weeks(){
  1256. var hide_week1 = $('div.week1').hasClass('filtered');
  1257. var hide_week2 = $('div.week2').hasClass('filtered');
  1258. if (hide_week1 && hide_week2 ){
  1259. alert("You are hiding both weeks");
  1260. }
  1261. $('div.workspace div.divTable').each(function(i,e){
  1262. var job = $(e).data().job;
  1263. if ((hide_week1 && job.is_week1()) ||
  1264. (hide_week2 && job.is_week2()) ){
  1265. $(e).fadeOut();
  1266. }
  1267. });
  1268. }
  1269. var debounced_calculate = debounce(calculate_total_hour_and_money, 2000);
  1270. function calculate_total_hour_and_money()
  1271. {
  1272. //init pays for all staff;
  1273. var pays={
  1274. total: 0,
  1275. hours: 0,
  1276. };
  1277. $('.stafflist > div.peopleitem').each(function(i,e){
  1278. var people = $(this).data().obj;
  1279. people.reset_summary();
  1280. });
  1281. $('div.workspace > .divTable:visible').each(function(i,e){
  1282. var job = $(e).data().job; //class Job
  1283. if (typeof job === 'undefined')
  1284. return;
  1285. var ps = job.get_payment_summary();
  1286. pays.total += ps.money;
  1287. pays.hours += ps.hour;
  1288. var staff = job.get_staff();
  1289. var people = find_staff(staff); //class People
  1290. if (people !=false)
  1291. people.add_payment_summary(ps);
  1292. });
  1293. set_wages(pays.total.toFixed(2));
  1294. set_working_hours(pays.hours.toFixed(2));
  1295. }
  1296. function find_staff(login)
  1297. {
  1298. var d = $('#p'+login).data();
  1299. if (typeof d === 'undefined')
  1300. return false;
  1301. return $('#p'+login).data().obj;
  1302. }
  1303. $(document).on('change', '.divTableRow select, .divTableRow input', function() {
  1304. var job = $(this).closest('.divTable').data().job;
  1305. job.validate();
  1306. job.mark_dirty();
  1307. debounced_calculate();
  1308. });
  1309. function init_ts(){
  1310. show_loading_jobs();
  1311. list_staff();
  1312. list_clients();
  1313. ajax_earning_rate();
  1314. xero(false);
  1315. wifi(false);
  1316. csv(false);
  1317. init_user_search();
  1318. reset_title_to_today();
  1319. load_timesheet();
  1320. }
  1321. function do_email_jobs()
  1322. {
  1323. var selector = 'div.bts_message div.ult_modal-body';
  1324. $(selector).html('Analysis staff jobs ... ok');
  1325. var staff = bts().staff.slice(0);//copy this array;
  1326. var s = staff.pop();
  1327. //week2 start
  1328. var w2_begin = new Date($('span[name="w2d1"]').data().date);
  1329. var w2_end = new Date($('span[name="w2d7"]').data().date);
  1330. var start = format_date(w2_begin);
  1331. var finish = format_date(w2_end);
  1332. function do_staff(){
  1333. var el = $('<p> Checking ' + s.firstname + "....</p>");
  1334. $(selector).append(el);
  1335. el[0].scrollIntoView();
  1336. $.post(bts().ajax_url, { // POST request
  1337. _ajax_nonce: bts().nonce, // nonce
  1338. action: "email_job", // action
  1339. staff : s.login,
  1340. start : start,
  1341. finish: finish,
  1342. }, function(response, status, xhr){
  1343. if (response.status == 'success'){
  1344. if (response.sent){
  1345. el.append('<span class="sent">' + response.emailstatus + '</span>');
  1346. }else{
  1347. el.append('<span class="nojob">' + response.emailstatus + '</span>');
  1348. }
  1349. }else{
  1350. el.append('<span class="error"> Error[' + response.error + ' ...]</span>');
  1351. }
  1352. }).fail(function(){
  1353. el.append('<span class="error">' + 'Network Error occured' + '</span>');
  1354. //clear staff pending list, stop further processing
  1355. s = [];
  1356. }).always(function(){//next staff
  1357. if (staff.length >0){
  1358. s = staff.pop();
  1359. setTimeout(do_staff, 100); //a short delay makes it looks nice
  1360. }else{
  1361. $('div.bts_message .ult-overlay-close-inside').show();
  1362. $('div.bts_message .ult-overlay-close-inside').addClass('blink_me');
  1363. $('div.week2').removeClass('blink_me');
  1364. $(selector).append('<span class="sent">All staff confirmed! </span>');
  1365. }
  1366. });
  1367. }
  1368. //execute
  1369. do_staff();
  1370. }
  1371. function ajax_earning_rate(){
  1372. $.post(bts().ajax_url, { // POST request
  1373. _ajax_nonce: bts().nonce, // nonce
  1374. action: "earnings_rate", // action
  1375. }, function(response, status, xhr){
  1376. bts().earnings_rate = response;
  1377. response.options.forEach(function(e){
  1378. bts().earnings_rate[e.EarningsRateID]=e;
  1379. });
  1380. });
  1381. }
  1382. $( ".boundary_datepicker" ).datepicker();
  1383. $( ".boundary_datepicker" ).datepicker("option", "dateFormat", "yy-mm-dd");
  1384. $(document).on('click', 'div.clientlist div[name="title"] a', function(e){
  1385. e.preventDefault();
  1386. e.stopPropagation();
  1387. var id = $(this).closest('label.peopleitem').attr('data-id').substring(1);
  1388. var str = 'https://acaresydncy.com.au/feedback_card/' + id;
  1389. var name = $(this).html();
  1390. if ( confirm ("Email feedback link of : " + name + "\n\n\n" + str + "\n\n\n to helen@acaresydney.com.au?")){
  1391. $.post(bts().ajax_url, { // POST request
  1392. _ajax_nonce: bts().nonce, // nonce
  1393. action: "email_feedback_url", // action
  1394. client : id,
  1395. }, function(response, status, xhr){
  1396. //alert('please check your email on the phone and SMS the link to your client');
  1397. }).fail(function(){
  1398. alert('network error ');
  1399. });
  1400. }
  1401. });
  1402. init_ts();
  1403. /*________________________________________________________________________*/
  1404. });
  1405. })(jQuery);
  1406. /*______________scrolling______________________________________________*/
  1407. jQuery(document).ready(function(){
  1408. var timeoutid =0;
  1409. jQuery('button.peoplelist[name="down"]').mousedown(function(){
  1410. var button = this;
  1411. timeoutid = setInterval(function(){
  1412. //console.log("down scrotop %d ", jQuery(button).parent().find(".userlist").get(0).scrollTop );
  1413. jQuery(button).parent().find(".userlist").get(0).scrollTop +=240;
  1414. }, 100);
  1415. }).on('mouseup mouseleave', function(){
  1416. clearTimeout(timeoutid);
  1417. });
  1418. jQuery('button.peoplelist[name="up"]').mousedown(function(){
  1419. var button = this;
  1420. timeoutid = setInterval(function(){
  1421. //console.log("up scrotop %d ", jQuery(button).parent().find(".userlist").get(0).scrollTop );
  1422. jQuery(button).parent().find(".userlist").get(0).scrollTop -=240;
  1423. }, 100);
  1424. }).on('mouseup mouseleave', function(){
  1425. clearTimeout(timeoutid);
  1426. });
  1427. });