timesheet source code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1602 lines
45KB

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