From 10aedeb77c942c4bad78af4c4476ab04532c6b6e Mon Sep 17 00:00:00 2001 From: patrick Date: Sun, 1 Sep 2019 23:55:42 +1000 Subject: [PATCH] split new version of timesheet for v1 --- Apiv1.php | 69 ++ css/bts_office.css | 946 +++++++++++++++++++++++++++ html/jobv1.html | 38 ++ js/bts_office.js | 1528 ++++++++++++++++++++++++++++++++++++++++++++ ts.php | 48 +- 5 files changed, 2624 insertions(+), 5 deletions(-) create mode 100644 Apiv1.php create mode 100644 css/bts_office.css create mode 100644 html/jobv1.html create mode 100644 js/bts_office.js diff --git a/Apiv1.php b/Apiv1.php new file mode 100644 index 0000000..b18eed7 --- /dev/null +++ b/Apiv1.php @@ -0,0 +1,69 @@ +office = $office; + $this->job_table = $job_table; + add_shortcode( 'bts_jobv1_item', array($this, 'bts_jobv1_item')); + add_action('wp_ajax_list_jobv1', array($this,'list_job' )); + } + + + public function bts_jobv1_item($attr){ + $html =$this->template('jobv1_item', 'jobv1.html'); + //$html = str_replace('[bts-tos-options]', $this->bts_tos_options([]), $html); + $html = do_shortcode($html); + return $html; + } + + //generate template based on html file + private function template($id, $file) + { + $text = ''; + return $text; + } + + + //ajax browse job with different filters + function list_job(){ + global $wpdb; + check_ajax_referer('acaresydney'); + $start = $_POST['start'] . " 00:00:00"; + $finish = $_POST['finish']. " 23:59:59"; + + $response = array( + 'status'=>'success', + 'jobs' => [], + ); + + //$sql = "SELECT * FROM $this->table_name WHERE start>='%s' and start <='%s' order by start ASC ,staff ASC"; + $sql = "SELECT * FROM $this->job_table order by start ASC ,staff ASC"; + $query = $wpdb->prepare ($sql, array($start, $finish)); + $response['sql'] = $query; + $jobs = $wpdb->get_results($query); + + if (! empty($jobs)){ + $response['status'] = 'success'; + foreach( $jobs as $s){ + $response['jobs'][] = array( + 'id' => $s->id, + 'tos' => $s->tos, + 'start'=> $s->start, + 'finish'=> $s->finish, + 'rate'=> $s->rate, + 'staff'=> $s->staff, + 'client'=> $s->client, + 'ack' => $s->ack, + 'rating' =>$s->rating, + ); + } + } + wp_send_json($response); + wp_die(); + } +} \ No newline at end of file diff --git a/css/bts_office.css b/css/bts_office.css new file mode 100644 index 0000000..b7fd08b --- /dev/null +++ b/css/bts_office.css @@ -0,0 +1,946 @@ +html, +body { + margin: 0px !important; + overflow: hidden; +} + +.bts_hidden { + z-index: -1; +} + +.blink_me { + animation: blinker 0.3s linear infinite; +} + +@keyframes blinker { + 50% { + opacity: 0; + } +} + +.titlebar_gradient { + /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#f6e6b4+0,ed9017+100 */ + background: rgb(246,230,180); /* Old browsers */ + background: -moz-linear-gradient(top, rgba(246,230,180,1) 0%, rgba(237,144,23,1) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(top, rgba(246,230,180,1) 0%,rgba(237,144,23,1) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to bottom, rgba(246,230,180,1) 0%,rgba(237,144,23,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f6e6b4', endColorstr='#ed9017',GradientType=0 ); /* IE6-9 */ +} + +.sheettitle h1:hover { + text-decoration: underline; + font-weight: bolder; + cursor: pointer; + animation: blinker 1s linear infinite; +} + +.timesheets { + width: calc(100vw - 300px); + height: 100vh; + margin: 0px; + padding: 0px; + left: 150px; + position: fixed; + background-color: red; + overflow: hidden; +} + +.peoplebar { + position: fixed; + top: 0px; + width: 150px; + height: 100vh; + background: lightgrey; + overflow: hidden; +} + +.peoplebar.left { + border-radius: 5px 0px 0px 0px; + left: 0px; +} + +.peoplebar.right { + right: 0px; + border-radius: 0px 5px 0px 0px; +} + +.timesheets .sheettitle { + padding-top: 8px; + text-align: center; + background-color: lightgrey; + height: 40px; + text-shadow: 1px 1px 1px #fcfcfc; +} + +.timesheets .workspace { + height: calc(100vh - 190px); + background-color: white; + overflow-x: hidden; + overflow-y: scroll; +} + +.timesheets .workspace img { + display: block; + margin-top: 140px; + margin-left: auto; + margin-right: auto; +} + +.timesheets .statusbar { + position: relative; + height: 40px; + background-color: ivory; + box-shadow: 1px 1px 10px black; +} + +/* staff search bar*/ +.b_search { + position: relative; + height: 40px; +} + +.b_search input { + margin-top: 5px; + padding-left: 20px; + padding-right: 20px; + border-radius: 20px; + box-shadow: 1px 1px 2px grey inset; + height: 30px; +} + +.b_search .ticon-search { + position: absolute; + top: 15px; + left: 5px; +} + +.b_search .ticon-times-circle-o { + position: absolute; + top: 15px; + right: 5px; +} + +.peoplebar button { + width: 100%; + padding: 0px; + height: 20px; +} + +.peoplebar .stafflist, +.peoplebar .clientlist { + height: calc(100vh - 80px); + background-color: dimgrey; + overflow: hidden; + text-align: center; + + /* scroll-behavior: smooth; */ +} + +.peopleitem { + position: relative; + margin-top: 10px; + margin-bottom: 10px; + height: 100px; + background: transparent; +} + +/* hover box */ +div.peopleitem > label { + -webkit-perspective: 1000px; + perspective: 1000px; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; + display: block; + width: 100%; + height: 100px; + position: absolute; + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + cursor: pointer; +} + +div.peopleitem .card { + position: relative; + height: 100%; + width: 95%; + margin: auto; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transition: all 600ms; + transition: all 600ms; + z-index: 20; + border: 1px solid black; + box-shadow: 1px 1px 10px black; +} + +div.peopleitem .card > div { + position: absolute; + height: 100%; + width: 100%; + background: #FFF; + text-align: center; + + /* line-height: 200px; */ + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + border-radius: 2px; +} + +div.peopleitem .card .back { + background: #222; + color: #FFF; + -webkit-transform: rotateX(180deg); + transform: rotateX(180deg); +} + +div.peopleitem label:hover .card { + -webkit-transform: rotateX(20deg); + transform: rotateX(20deg); + box-shadow: 0 20px 20px rgba(50,50,50,.2); +} + +div.peopleitem input { + display: none; +} + +div.peopleitem :checked + .card { + transform: rotateX(180deg); + -webkit-transform: rotateX(180deg); +} + +div.peopleitem label:hover :checked + .card { + transform: rotateX(160deg); + -webkit-transform: rotateX(160deg); + box-shadow: 0 20px 20px rgba(255,255,255,.2); +} + +/* end of hover box */ +.satusbar { + position: relative; +} + +.statusbar * { + vertical-align: top; + text-shadow: 1px 1px 1px #fcfcfc; +} + +.statusbar div { + position: relative; + display: inline; +} + +.statusbar .xero { + width: 40px; + height: 100%; + margin-left: 5px; + margin-right: 5px; + display: inline-block; + background-color: white; + box-shadow: inset 0px 0px 2px dotted white; + border-radius: 100px; + background-image: url(../img/xero.png); + background-size: 40px; + box-shadow: 0px 0px 10px white; +} + +.statusbar .wifi i, +.statusbar .xero i, +.statusbar .csv i { + font-size: 20px; + color: red; + position: absolute; + right: 0px; + top: -5px; +} + +.statusbar .wifi { + width: 40px; + height: 100%; + margin-left: 0px; + margin-right: 5px; + display: inline-block; + background-color: white; + box-shadow: inset 0px 0px 2px black; + border-radius: 100px; + background-image: url(../img/wnet.png); + background-size: 40px; + box-shadow: 0px 0px 10px white; +} + +.statusbar .csv { + width: 40px; + height: 100%; + margin-left: 0px; + margin-right: 5px; + display: inline-block; + background-color: white; + box-shadow: inset 0px 0px 2px black; + border-radius: 100px; + background-image: url(../img/csv.png); + background-size: 40px; + box-shadow: 0px 0px 10px white; +} + +.statusbar .xero:hover, +.statusbar .wifi:hover, +.statusbar .csv:hover { + box-shadow: 0px 0px 3px black; +} + +.statusbar .wages { + text-align: center; + width: 150px; + height: 100%; + display: inline-block; + background-color: transparent; + font-size: 24px; + font-weight: bold; + color: dimgrey; + border: 1px dotted white; + position: absolute; + right: 400px; +} + +.statusbar .workinghours { + background-color: transparent; + width: 175px; + height: 100%; + display: inline-block; + position: absolute; + right: 220px; +} + +.statusbar #woh:hover { + cursor: pointer; + animation: blinker 0.3s linear infinite; +} + +.workinghours label { + transform: unset; + left: 0; + top: 0; + width: 50px; + height: 100%; + padding: 0.5em; + text-align: left; + display: inline-block; + color: #A974D; + font-weight: bolder; + line-height: 15px; + background-color: transparent; + margin-right: 0px; +} + +.statusbar input { + margin: 0px; + width: 115px; + height: 100%; + display: inline-block; + font-size: 24px; + background: transparent; + text-align: center; +} + +.statusbar button[name='confirmschedule'] { + margin-top: 5px; + margin-bottom: 5px ; + text-shadow: 0px 0px 2px black; + width: 200px ; + height: calc( 100% - 10px ); + font-weight: bolder; + position: absolute; + right: 10px; +} + +div.sheetsheader { + height: 110px; + background: darkorange; + position: relative; +} + +div.prevweek { + display: inline-block; + height: 70px; + width: 40px; + font-size: 36px; + padding-top: 5px; + padding-left: 2px; + float: left; + background: darkorange; +} + +div.prevweek span:hover, +div.nextweek span:hover { + box-shadow: 1px 1px 10px black; + border-radius: 20px; + color: black; +} + +div.nextweek { + display: inline-block; + height: 70px; + width: 40px; + font-size: 36px; + padding-top: 5px; + padding-left: 4px; + float: right; +} + +div.weekly { + width: calc( 100% - 80px ) ; + display: inline-block; + background-color: black; + height: 70px; +} + +div.weekly div { + margin: 0px; +} + +div.weekly div.weekname { + position: relative; + width: 170px; + top: -5px; + border: 2px solid grey; + border-radius: 10px; + text-align: center; + color: white; + + /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#b5bdc8+0,828c95+36,28343b+100;Grey+Black+3D */ + background: rgb(181,189,200); /* Old browsers */ + background: -moz-linear-gradient(top, rgba(181,189,200,1) 0%, rgba(130,140,149,1) 36%, rgba(40,52,59,1) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(top, rgba(181,189,200,1) 0%,rgba(130,140,149,1) 36%,rgba(40,52,59,1) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to bottom, rgba(181,189,200,1) 0%,rgba(130,140,149,1) 36%,rgba(40,52,59,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b5bdc8', endColorstr='#28343b',GradientType=0 ); /* IE6-9 */ +} + +div.weekly div.weekname input{ + width: 80px; + height: 20px; + border:0px; + background-color:transparent; + color: white; + padding-left:4px; + padding-right:4px; +} + +div.weekly div.weekname input.hasDatepicker{ + background-image:none +} + +div.weekly div.weekname.prev { + position: absolute; + left: 100px; + z-index: 1; +} + +div.weekly div.weekname.next { + position: absolute; + right: 100px; + z-index: 1; +} + +div.weekly >div.weekname:hover, +div.weekly div[name='copyschedule']:hover { + cursor: pointer; + box-shadow: 0px 0px 10px #2c87f0; +} + +div.weekly div.copyprogress { + position: absolute; + display: inline-block; + width: calc(100% - 80px); + height: 15px; + top: 0px; + border-radius: 2px; + box-shadow: inset 1px 1px 2px black; + text-align: center; + + /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#fefcea+0,f1da36+100;Gold+3D */ + background: rgb(254,252,234); /* Old browsers */ + background: -moz-linear-gradient(top, rgba(254,252,234,1) 0%, rgba(241,218,54,1) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(top, rgba(254,252,234,1) 0%,rgba(241,218,54,1) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to bottom, rgba(254,252,234,1) 0%,rgba(241,218,54,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fefcea', endColorstr='#f1da36',GradientType=0 ); /* IE6-9 */ +} + +div.copyprogress div[name='copyschedule'] { + position: relative; + top: -5px; + width: 150px; + height: 25px; + padding: 0px; + border-radius: 10px; + margin: auto; + z-index: 1; + color: white; + + /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#b5bdc8+0,828c95+36,28343b+100;Grey+Black+3D */ + background: rgb(181,189,200); /* Old browsers */ + background: -moz-linear-gradient(top, rgba(181,189,200,1) 0%, rgba(130,140,149,1) 36%, rgba(40,52,59,1) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(top, rgba(181,189,200,1) 0%,rgba(130,140,149,1) 36%,rgba(40,52,59,1) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to bottom, rgba(181,189,200,1) 0%,rgba(130,140,149,1) 36%,rgba(40,52,59,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b5bdc8', endColorstr='#28343b',GradientType=0 ); /* IE6-9 */ +} + +#gotoweek { + width: 50%; + background-color: transparent; + border: 0px; + color: white; + font-weight: 900; +} + +div.weekdays { + position: absolute; + top: 15px; + width: calc(100% - 80px); + height: 55px; + background-color: darkorange; + text-align: center; +} + +div.week1, +div.week2 { + display: inline-block; + width: 45%; + height: 55px; + background-color: darkorange; + text-align: justify; + border: 1px dashed lightgrey; + padding-left: 10px; + padding-right: 10px; +} + +div.week1:hover, +div.week2:Hover { + border-radius: 20px; + box-shadow: 0px 0px 2px #2c87f0 inset; + cursor: pointer; + + /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#ebf1f6+0,abd3ee+50,89c3eb+51,d5ebfb+100;Blue+Gloss+%234 */ + background: rgb(235,241,246); /* Old browsers */ + background: -moz-linear-gradient(top, rgba(235,241,246,1) 0%, rgba(171,211,238,1) 50%, rgba(137,195,235,1) 51%, rgba(213,235,251,1) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(top, rgba(235,241,246,1) 0%,rgba(171,211,238,1) 50%,rgba(137,195,235,1) 51%,rgba(213,235,251,1) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to bottom, rgba(235,241,246,1) 0%,rgba(171,211,238,1) 50%,rgba(137,195,235,1) 51%,rgba(213,235,251,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ebf1f6', endColorstr='#d5ebfb',GradientType=0 ); /* IE6-9 */ +} + +div.week1.filtered, +div.week2.filtered { + border-radius: 20px; + + /* background:white; */ + /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#ebf1f6+0,abd3ee+50,89c3eb+51,d5ebfb+100;Blue+Gloss+%234 */ + background: rgb(235,241,246); /* Old browsers */ + background: -moz-linear-gradient(top, rgba(235,241,246,1) 0%, rgba(171,211,238,1) 50%, rgba(137,195,235,1) 51%, rgba(213,235,251,1) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(top, rgba(235,241,246,1) 0%,rgba(171,211,238,1) 50%,rgba(137,195,235,1) 51%,rgba(213,235,251,1) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to bottom, rgba(235,241,246,1) 0%,rgba(171,211,238,1) 50%,rgba(137,195,235,1) 51%,rgba(213,235,251,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ebf1f6', endColorstr='#d5ebfb',GradientType=0 ); /* IE6-9 */ +} + +div.week1:after, +div.week2:after { + content: ''; + display: inline-block; + width: 100%; +} + +div.week1 { + position: relative; + left: -10px; +} + +div.week2 { + position: relative; + left: 10px; +} + +div.week1 > div, +div.week2 > div { + display: inline-block; + text-align: center; + width: 10%; + height: 40px; + margin-top: 5px; + font-weight: bolder; + border-radius: 10px; +} + +div.week1 div:hover, +div.week2 div:hover { + cursor: pointer; + box-shadow: 0px 0px 10px red; +} + +div.week1 > div { + box-shadow: 0px 0px 10px white inset; +} + +.week1color { + color: black; + + /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#ebe9f9+0,d8d0ef+50,cec7ec+51,c1bfea+100;Purple+3D+%231 */ + background: #ebe9f9; /* Old browsers */ + background: -moz-linear-gradient(top, #ebe9f9 0%, #d8d0ef 50%, #cec7ec 51%, #c1bfea 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(top, #ebe9f9 0%,#d8d0ef 50%,#cec7ec 51%,#c1bfea 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to bottom, #ebe9f9 0%,#d8d0ef 50%,#cec7ec 51%,#c1bfea 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ebe9f9', endColorstr='#c1bfea',GradientType=0 ); /* IE6-9 */ +} + +div.week2 > div { + box-shadow: 0px 0px 10px white inset; +} + +.week2color { + color: white; + + /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#627d4d+0,1f3b08+100;Olive+3D */ + background: #627d4d; /* Old browsers */ + background: -moz-linear-gradient(top, #627d4d 0%, #1f3b08 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(top, #627d4d 0%,#1f3b08 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to bottom, #627d4d 0%,#1f3b08 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#627d4d', endColorstr='#1f3b08',GradientType=0 ); /* IE6-9 */ +} + +div.assignment > div { + color: navy; + background-color: white; + font-size: 20px; + font-weight: lighter; + border: 1px dotted lightgrey; +} + +/* peple item card*/ +.peopleitem div span.checked { + color: orange; +} + +.peopleitem div.card div.front > div[name="title"] { + height: auto; + text-align: center; + background-color: lightgrey; +} + +div.front > span[name='icon'] { + width: 100%; + height: 15px; + background-color: dimgrey; + color: white; +} + +div.back > span.icon { + width: 100%; + background-color: lightgrey; + color: dimgrey; +} + +div.front > span.badge, +div.back > span.badge { + border-radius: 50%; + display: inline-block; + padding-left: 8px; + padding-right: 8px; + text-align: center; + position: absolute; + right: -5px; + top: -5px; +} + +span.badge.blue { + color: #fff; + box-shadow: 1px 1px 10px black; + background-color: black; +} + +span.badge.pink { + color: black; + box-shadow: 1px 1px 10px white; + background-color: white; +} + +/* div table */ +div.blueTable { + border: 1px solid #1C6EA4; + background-color: white; + width: 100%; + text-align: left; + border-collapse: collapse; + border: 0px; +} + +div.blueTable.emptyrecord { + background-color: #eeeeee; +} + +.divTable.blueTable .divTableCell, +.divTable.blueTable .divTableHead, + { + border: 1px solid #AAAAAA; + padding: 3px 2px; +} + +.divTable.blueTable .divTableBody .divTableCell { + font-size: 15px; +} + +.divTable.blueTable .divTableRow:nth-child(even) { + background: #D0E4F5; +} + +.divTable.blueTable .divTableHeading { + background: #1C6EA4; + background: -moz-linear-gradient(top, #5592bb 0%, #327cad 66%, #1C6EA4 100%); + background: -webkit-linear-gradient(top, #5592bb 0%, #327cad 66%, #1C6EA4 100%); + background: linear-gradient(to bottom, #5592bb 0%, #327cad 66%, #1C6EA4 100%); + border-bottom: 2px solid #444444; +} + +.divTable.blueTable .divTableHeading .divTableHead { + font-size: 15px; + font-weight: bold; + color: #FFFFFF; + border-left: 2px solid #D0E4F5; +} + +.divTable.blueTable .divTableHeading .divTableHead:first-child { + border-left: none; +} + +.blueTable .tableFootStyle { + font-size: 14px; + font-weight: bold; + color: #FFFFFF; + background: #D0E4F5; + background: -moz-linear-gradient(top, #dcebf7 0%, #d4e6f6 66%, #D0E4F5 100%); + background: -webkit-linear-gradient(top, #dcebf7 0%, #d4e6f6 66%, #D0E4F5 100%); + background: linear-gradient(to bottom, #dcebf7 0%, #d4e6f6 66%, #D0E4F5 100%); + border-top: 2px solid #444444; +} + +.blueTable .tableFootStyle { + font-size: 14px; +} + +.blueTable .tableFootStyle .links { + text-align: right; +} + +.blueTable .tableFootStyle .links a { + display: inline-block; + background: #1C6EA4; + color: #FFFFFF; + padding: 2px 8px; + border-radius: 5px; +} + +.blueTable.outerTableFooter { + border-top: none; +} + +.blueTable.outerTableFooter .tableFootStyle { + padding: 3px 5px; +} + +/* DivTable.com */ +.divTable { + display: table; + margin: 3px 0px 3px 0px; +} + +.divTable.highlight { + box-shadow: 1px 1px 10px green; +} + +.divTable.invalidjob.highlight { + box-shadow: 1px 1px 10px #f50202; +} + +.divTable.highlight.newcopy { + box-shadow: 1px 1px 10px orange; + border: 5px orange solid; +} + +.divTableRow { + display: table-row; +} + +.divTableRow.errmsg { + background-color: white; + font-weight: bolder; + color: red; +} + +.divTableHeading { + display: table-header-group; +} + +.divTableCell, +.divTableHead { + display: table-cell; +} + +.divTableHeading { + display: table-header-group; +} + +.divTableFoot { + display: table-footer-group; +} + +.divTableBody { + display: table-row-group; +} + +/* customized part of div table */ +div.sheettableheader { + margin-top: 0px; + height: 36px; + overflow-x: hidden; + overflow-y: scroll; +} + +div.btos, +div.bstart, +div.bfinish, +div.brate, +div.bstaff, +div.bclient, +div.bconfirmed, +div.bsave, +div.brating, +div.bdelete { + width: 10%; + height: 30px; +} + +div.btos { + width: 20%; +} + +div.bstart, +div.bfinish { + width: 11%; +} + +div.brate { + width: 10%; + max-width: 20px; +} + +.divTable div > select { + width: 100%; + height: 100%; +} + +div.bdelete, +div.bsave, +div.bconfirmed { + text-align: center; + width: 5%; +} + +div.brating { + text-align: center; + cursor: pointer; +} + +.brating { + unicode-bidi: bidi-override; + direction: rtl; +} + +div.workspace .brating > span:hover:before, +div.workspace .brating > span:hover ~ span:before { + content: "\2605"; + position: absolute; +} + +div.bsave span.ticon-copy { + display: none; +} + +div.bsave span.ticon-save { + display: inline-block; + border: 3px solid lightgrey; + padding: 5px; + border-radius: 5px; + color: black; + cursor: pointer; +} + +div.bsave.saved span.ticon-save { + display: none; +} + +div.bsave.saved span.ticon-copy { + display: inline-block; + border: 3px solid orange; + padding: 5px; + border-radius: 20px; + color: orange; +} + +div.bdelete span.ticon-trash { + display: inline-block; + border: 3px solid lightgrey; + padding: 5px; + border-radius: 5px; + color: red; + cursor: pointer; +} + +div.workspace div > span:hover { + box-shadow: 0px 0px 10px black; +} + +div.divTableHeading div > span:hover { + cursor: pointer; + animation: blinker 0.3s linear infinite; +} + +/* short code for the table */ +div.btos select, +div.bstart input, +div.bfinish input { + width: 100%; + height: 98%; +} + +div.divTable.validjob { + border: 0px solid white; +} + +div.divTable.invalidjob { + border-left: 2px solid red; + border-right: 2px solid red; +} + +div.divTable .invalid { + background-color: yellow; +} + +/* end of div table */ +/* pop up message box */ +.bts_message .ult_modal-body { + height: 50vh; + overflow: scroll; +} + +.bts_message .ult_modal-body .sent { + color: green; +} + +.bts_message .ult_modal-body .nojob { + color: dimgrey; +} + +.bts_message .ult_modal-body .error { + color: red; +} + +.bts_message .ult_modal-body .span { + font-weight: 900; +} \ No newline at end of file diff --git a/html/jobv1.html b/html/jobv1.html new file mode 100644 index 0000000..48b671c --- /dev/null +++ b/html/jobv1.html @@ -0,0 +1,38 @@ +
+
+
+ +
+ {{tos_name}} +
+
{{start}}
+
{{finish}}
+
{{rate_name}}
+
{{staff_name}}
+
{{client_name}}
+
+
{{rating}}
+
+ +
+
+ + +
+
+
+
e tos +
+
es
+
ef
+
er
+
estaf
+
ecli
+
econfirm
+
erat
+
edel
+
eeave
+
+ +
+
\ No newline at end of file diff --git a/js/bts_office.js b/js/bts_office.js new file mode 100644 index 0000000..6d97123 --- /dev/null +++ b/js/bts_office.js @@ -0,0 +1,1528 @@ +(function ($) { + $(function () { + + // http://davidwalsh.name/javascript-debounce-function + function debounce(func, wait, immediate) { + var timeout; + return function () { + var context = this, args = arguments; + var later = function () { + timeout = null; + if (!immediate) + func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) + func.apply(context, args); + }; + }; +/*____________________________________________________________________________________*/ + class People{ + constructor(selector, template, data){ + this.selector = selector; + this.data = data; + this.template = template; +// this.sample_people = { +// login: '01515b52-6936-46b2-a000-9ad4cd7a5b50', +// firstname: "first", +// lastname: "last", +// phone: '041122221', +// email: 'abc@gmail.com', +// pay: 0, +// hour: 12, +// OT: 3, +// petrol: 50, +// rating: 1, +// }; + this.load_data(this.data); + } + + load_data(data){ + var template = $(this.template).html(); + var html = Mustache.render(template, data); + $(this.selector).html(html); + //save it + $(this.selector).data({obj:this, data:data}); + //draw rating star + this.set_ratings(this.data.rating); + this.set_unconfirmed_job(this.data.unconfirmedjob); + } + + set_ratings(num){ + for (var i=1; i<= 5; i++){ + if (i <=num){ + $(this.selector + " div[name='rating'] span:nth-child(" +i+ ")").addClass('checked'); + }else{ + $(this.selector + " div[name='rating'] span:nth-child(" +i+ ")").removeClass('checked'); + } + } + this.data.rating = num; + } + + set_unconfirmed_job(num){ + if( num == 0 ) + $(this.selector + " span[name='badge']").hide(); + else + $(this.selector + " span[name='badge']").show(); + this.data.unconfirmedjob = num; + } + + reset_summary() { + this.summary = { + wages : 0, + normal_hour : 0, + ot_hour : 0, + petrol : 0, + }; + this.update_summary_in_gui(); + } + + add_payment_summary(ps) + { + //{ot: false, hour: "2.67", money: "76.90"} + this.summary.wages += ps.money; + if (! ps.ot ) + this.summary.normal_hour += ps.hour; + else + this.summary.ot_hour += ps.hour; + this.update_summary_in_gui(); + } + + update_summary_in_gui() + { + var msg = '$' + this.summary.wages.toFixed(2); + $(this.selector).find('div[name="wages"]').html(msg); + + msg = this.summary.normal_hour.toFixed(2) + '+' +this.summary.ot_hour.toFixed(2) + 'hr'; + $(this.selector).find('div[name="hours"]').html(msg); + + msg = 'petrol:' + this.summary.petrol.toFixed(2) + 'km'; + $(this.selector).find('div[name="petrol"]').html(msg); + } + }//end of class People + + function bts_staff_html(data){ + var template = $('#staff_item').html(); + var head = '
'; + r = head + '
' ; + return r; + } + + function bts_client_html(data){ + var template = $('#client_item').html(); + var head = '
'; + r = head + '
' ; + return r; + } + + function sample_staff(){ + for (var i=1; i<100; i++){ + var sample_people = { + login: '01515b52-6936-46b2-a000-9ad4cd7a5b50' +i, + firstname: "first"+i, + lastname: "last", + mobile: '041122221' +i, + email: 'abc@gmail.com' + i, + wages: 0, + hour: i, + OT: 3, + petrol: 50 +i, + rating: Math.floor(Math.random() * Math.floor(5)), + unconfirmedjob: Math.floor(Math.random() * Math.floor(30)), + }; + var html = bts_staff_html(sample_people); + jQuery('div.stafflist').append(html); + new People("#p" + sample_people.login, sample_people); + } + } + function list_staff() { + show_loading_staff(); + $('div.stafflist div.peopleitem').remove(); + $.post(bts().ajax_url, { // POST request + _ajax_nonce: bts().nonce, // nonce + action: "list_staff", // action + }).done(function(response, status, xhr){ + if (response.status =='success'){ + bts().staff = response.users; + response.users.forEach(function(u){ + var html = bts_staff_html(u); + jQuery('div.stafflist').append(html); + new People("#p" + u.login,'#staff_item', u); + }); + hide_loading_staff(); + calculate_total_hour_and_money(); + }else{ + alert('error getting staff list'); + } + }); + } + + function list_clients() { + show_loading_client(); + $('div.clientlist div.peopleitem').remove(); //clear it + $.post(bts().ajax_url, { // POST request + _ajax_nonce: bts().nonce, // nonce + action: "list_client", // action + }, function(response, status, xhr){ + if (response.status =='success'){ + bts().client = response.users; + response.users.forEach(function(u){ + hide_loading_client(); + var html = bts_client_html(u); + jQuery('div.clientlist').append(html); + new People("#p" + u.login, '#client_item' ,u); + }); + }else{ + alert('error getting Client list'); + } + }); + } + + function show_loading_staff(){ + jQuery('div.stafflist img').attr('src', bts().load_user_img).show(); + } + function show_loading_client(){ + jQuery('div.clientlist img').attr('src', bts().load_user_img).show(); + } + function hide_loading_staff(){ + jQuery('div.stafflist img').hide(); + } + function hide_loading_client(){ + jQuery('div.clientlist img').hide(); + } + + function show_loading_jobs(){ + jQuery('div.workspace img').attr('src', bts().load_job_img).show(); + } + function hide_loading_jobs(){ + jQuery('div.workspace img').hide(); + } + + function xero(t){ + if (t) + $('div.xero i').show(); + else + $('div.xero i').hide(); + } + + function wifi(t){ + if (t) + $('div.wifi i').show(); + else + $('div.wifi i').hide(); + } + function csv(t){ + if (t) + $('div.csv i').show(); + else + $('div.csv i').hide(); + } + + + function init_user_search(){ + $('div.b_search input').keyup(debounce(function(e){ + filter_user(e.target); + }, 500)); + } + + function filter_user(input){ + var value = $(input).attr('value'); + value = value.toLowerCase(); + var selector = get_selector_for_filter_people(input); + $.each( $(selector).find('div.peopleitem'), function(index, e){ + //uncheck everyone + $(e).find('input[type="checkbox"]').prop('checked', false); + + var html = $(e).find('div[name="title"] a').html(); + html = html.toLowerCase(); + if (-1 != html.indexOf(value)){//we find it; + $(e).show(); + }else{ + $(e).hide(); + } + }); + } + + function get_selector_for_filter_people(input){ + var selector=''; + var role = $(input).attr('placeholder'); + if (role == 'staff') //we filter staff + selector = 'div.stafflist'; + else if (role = 'client') + selector = 'div.clientlist'; + return selector; + } + + + $(document).on('click', 'div.divTableHead.bdelete', function(){ + add_new_empty_job(); + }); + + function add_new_empty_job(){ + var o = new Job({empty:true}); + $('div.workspace').append(o.el); + o.el.get(0).scrollIntoView(); + dtp_init(); + } + $(document).on('click', 'div[name="copyschedule"]', function(e){ + e.stopPropagation(); + add_new_empty_job(); + }); + + $(document).on('click', 'div.divTableCell.bdelete', function(){ + var job = $(this).closest('.divTable').data().job; + var el = $(this).closest('div.divTable'); + if ( job.get_job_id() == '') + el.remove(); + else{ + if (confirm('delete this job?')){ + $.post(bts().ajax_url, { // POST request + _ajax_nonce: bts().nonce, // nonce + action: "delete_job", // action + jobid: job.data.id, + }, function(response, status, xhr){ + if (response.status=='success'){ + el.addClass('blink_me'); + el.fadeOut(900); + setTimeout(function(){ + el.remove(); + }, 900); + + }else{ + alert( 'error saving data, please check your network'); + } + }); + } + } + }); + + $(document).on('mouseenter', 'div.divTableCell', function(){ + $(this).closest('div.divTable').addClass('highlight'); + }); + $(document).on('mouseleave', 'div.divTableCell', function(){ + $(this).closest('div.divTable').removeClass('highlight'); + }); + + $(document).on('click', 'div.workspace span.ticon.ticon-save', function(){ + var table = $(this).closest('div.divTable'); + table.data().job.do_save_record(); + }); + $(document).on('click', '.divTableHeading span.ticon.ticon-save', function(){ + //save all + $('div.workspace span.ticon.ticon-save').each(function (i,e){ + if ($(this).is(":visible")) + $(this).trigger('click'); + }); + }); + + + $(document).on('click', 'span.ticon.ticon-copy', function(){ + if (!confirm("make a copy of this job?")) + return; + var table = $(this).closest('div.divTable'); + var job = table.data().job; + var newj = clone_data_create_new_job(job.get_record_from_ui()); + $('div.workspace').append(newj.el); + newj.el.get(0).scrollIntoView();//make sure it's visible; + newj.mark_highlight_me(1000);//for 1 second; + dtp_init(); + }); + + class Job{ //save data for the record, and display it as GUI + constructor(data){ + var html = jQuery("#jobv1_item").html(); + this.el = $(html); + //jQuery('div.workspace').append(this.el); + this.load_data(data); + this.init_start_rating(); + } + + init_start_rating(){ + var self = this; + this.el.find("div.brating span").click(function(){ + var r = $(this).attr('data-rating'); + self.mark_dirty(); + self.set_rating(r); + }) + + this.el.find("div.brating").mouseenter(function(){ + //change to all hollow star + $(this).find('span').html('☆'); + }); + + this.el.find("div.brating").mouseleave(function(){ + self.set_rating(self.data.rating); + }); + + } + + load_data(data) + { + //save to html element + this.data = data; + this.el.data({job:this, data:data}); + + //draw GUI + this.clear_err_msg(); + this.set_job_id(data.id); + this.set_tos(data.tos); + this.set_start(data.start); + this.set_finish(data.finish); + this.set_rate(data.rate); + this.set_staff(data.staff); + this.set_client(data.client); + this.set_ack(data.ack); + this.set_rating(data.rating); + + //draw GUI by other + this.mark_dirty_on_new_record(data); + this.mark_week_color(); + this.validate(); //also triggers mark errors + } + + get_job_id(){ + return this.el.find('input[name="id"]').attr('value'); + } + set_job_id(val){ + return this.el.find('input[name="id"]').attr('value', val); + } + get_tos() + { + return this.el.find('div.btos select').children("option:selected").val(); + } + set_tos(val) + { + if (typeof(val) =="undefined") + return; + this.el.find('div.btos select option[value="'+val+'"]').prop('selected',true); + } + get_start(){ + return this.el.find('div.bstart input').attr('value'); + } + set_start(val) + { + if (typeof(val) =="undefined") + return; + this.el.find('div.bstart input').attr('value', val); + } + get_finish() + { + return this.el.find('div.bfinish input').attr('value'); + } + set_finish(val) + { + if (typeof(val) == "undefined") + return; + this.el.find('div.bfinish input').attr('value', val); + } + get_rate() + { + return this.el.find('div.brate select').children("option:selected").val(); + } + set_rate(val) + { + if (typeof(val) =="undefined") + return; + this.el.find('div.brate select option[value="'+val+'"]').prop('selected',true); + } + get_staff() + { + return this.el.find('div.bstaff select').children("option:selected").val(); + } + set_staff(val) + { + if (typeof(val) =="undefined") + return; + this.el.find('div.bstaff select option[value="'+val+'"]').prop('selected',true); + } + get_client() + { + return this.el.find('div.bclient select').children("option:selected").val(); + } + set_client(val) + { + if (typeof(val) =="undefined") + return; + this.el.find('div.bclient select option[value="'+val+'"]').prop('selected',true); + } + get_ack() + { + return this.el.find('div.bconfirmed input:checked').length > 0; + } + set_ack(val) + { + if (typeof(val) =="undefined") + return; + return this.el.find('div.bconfirmed input').prop('checked', val!=0); + } + get_rating(){ + var count =0; + this.el.find('div.brating span').each(function(i,e){ + if ($(e).html()=='★') + count +=1; + }); + return count; + } + set_rating(num){ + if (!(1 <= num && num <=5)) + return; + this.el.find('div.brating span').each(function(i,e){ + var rating = $(e).attr('data-rating'); + var rating = parseInt(rating); + if (rating <= num) + $(e).html('★'); + else + $(e).html('☆'); + }); + } + + get_record_from_ui(){ + var record = {}; + record.id = this.get_job_id(); + record.tos = this.get_tos(); + record.start = this.get_start(); + record.finish = this.get_finish(); + record.rate = this.get_rate(); + record.staff = this.get_staff(); + record.client = this.get_client(); + record.ack = this.get_ack(); + record.rating = this.get_rating(); + return record; + } + + do_save_record(){ + var self = this; + $.post(bts().ajax_url, { // POST request + _ajax_nonce: bts().nonce, // nonce + action: "save_job", // action + record: this.get_record_from_ui(), + }, function(response, status, xhr){ + if (response.status=='success'){ + self.load_data(response.newdata); + self.mark_saved(); + self.mark_old(); + }else{ + alert( 'error saving data, please check your network'); + } + }); + } + + mark_dirty_on_new_record(data){ + if (typeof(data.id) === 'undefined' || data.id == ''){ + this.mark_dirty(); + this.mark_new(); + } + else{ + this.mark_saved(); + } + } + + mark_dirty() //need save + { + var d = this.el.find('.bsave'); + d.removeClass('saved'); + d.addClass('blink_me'); + setTimeout(function(){ + d.removeClass('blink_me'); + d.removeClass('saved'); + },1000); + } + + mark_saved() + { + var d = this.el.find('.bsave'); + + d.addClass('blink_me'); + setTimeout(function(){ + d.removeClass('blink_me'); + d.addClass('saved'); + },1000); + } + + //newly created empty record + mark_new() + { + this.el.addClass('emptyrecord'); + } + mark_old() + { + this.el.removeClass('emptyrecord'); + } + + mark_highlight_me(ms){ + this.el.addClass('blink_me'); + this.el.addClass('highlight'); + this.el.addClass('newcopy'); + var self = this; + setTimeout(function(){ + self.el.removeClass('blink_me'); + self.el.removeClass('highlight'); + self.el.removeClass('newcopy'); + },ms); + } + + is_start_valid(){ + var s = this.get_start(); + return is_valid_date_str(s); + + } + is_finish_valid(){ + var f = this.get_finish(); + if (!is_valid_date_str(f)) + return false; + } + + is_finish_resonable(){ + var f = this.get_finish(); + if (!is_valid_date_str(f)) + return false; + var s = this.get_start(); + + s = new Date(s); + f = new Date(f); + + return (s < f); + } + + validate() + { + var ok_time = this.validate_start() && + this.validate_finish(); //finish might not be executed, if start is wrong + var ok_tos = this.validate_tos(); //make sure this validate is executed; + var ok_rate = this.validate_rate() ; //make sure this validate is executed + var ok = ok_time && ok_tos && ok_rate; + if (ok){ + this.el.removeClass('invalidjob'); + }else{ + this.el.addClass('invalidjob'); + } + return ok; + } + + validate_start(){ + var str = this.get_start(); + if ( is_valid_date_str(str) ){ + this.mark_start_valid(); + this.set_err_msg_start(''); + return true; + }else{ + this.mark_start_invalid(); + this.set_err_msg_start('wrong date'); + return false; + } + } + validate_finish() + { + var str = this.get_finish(); + if (! is_valid_date_str(str)){ + this.set_err_msg_finish('wrong date'); + this.mark_finish_invalid(); + return false; + } + + if (!this.is_finish_resonable()){ + this.set_err_msg_finish("older than start") + this.mark_finish_invalid(); + return false; + } + this.mark_finish_valid(); + this.set_err_msg_finish(''); + return true; + } + validate_rate() + { + var rate_info = this.get_rate_info_by_id(this.get_rate()); + if ( rate_info.RatePerUnit <= 0){ + this.set_err_msg_rate('bad rate'); + this.mark_rate_invalid(); + return false; + } +// if (this.get_rate() != this.data.rate){ +// this.set_err_msg_rate('rate@Xero inactive ' + this.data.rate); +// this.mark_rate_invalid(); +// this.mark_dirty(); +// return false; +// } + this.set_err_msg_rate(''); + this.mark_rate_valid(); + return true; + } + + validate_tos(){ +// if (this.get_tos() != this.data.tos){ +// this.set_err_msg_tos('require NDIS ' + this.data.tos); +// this.mark_tos_invalid(); +// this.mark_dirty(); +// console.log('tos mark dirty'); +// return false; +// } + this.set_err_msg_tos(''); + this.mark_tos_valid(); + return true; + } + + clear_err_msg(){ + this.el.find('.divTableRow.errmsg > div').html(''); + } + set_err_msg_start(str) + { + this.el.find('div.bstart_err').html(str); + } + set_err_msg_finish(str) + { + this.el.find('div.bfinish_err').html(str); + } + set_err_msg_rate(str) + { + this.el.find('div.brate_err').html(str); + } + set_err_msg_save(str) + { + this.el.find('div.bsave_err').html(str); + } + set_err_msg_tos(str) + { + this.el.find('div.btos_err').html(str); + } + + mark_tos_valid(){ + this.el.find('div.btos select').removeClass('invalid'); + } + mark_tos_invalid(){ + this.el.find('div.btos select').addClass('invalid'); + } + mark_start_valid(){ + this.el.find('div.bstart input').removeClass('invalid'); + } + mark_start_invalid(){ + this.el.find('div.bstart input').addClass('invalid'); + } + + mark_finish_valid(){ + this.el.find('div.bfinish input').removeClass('invalid'); + } + mark_finish_invalid(){ + this.el.find('div.bfinish input').addClass('invalid'); + } + mark_rate_valid(){ + this.el.find('div.brate select').removeClass('invalid'); + } + mark_rate_invalid(){ + this.el.find('div.brate select').addClass('invalid'); + } + + mark_week_color(){ + this.el.find('div.brating').removeClass('week1color'); + this.el.find('div.brating').removeClass('week2color'); + + if (this.is_week1()){ + this.el.find('div.brating').addClass('week1color'); + }else if (this.is_week2()){ + this.el.find('div.brating').addClass('week2color'); + } + } + + is_week1() + { + var w1_begin = new Date($('span[name="w1d1"]').data().date) ; + var w1_end = new Date($('span[name="w1d7"]').data().date); + w1_begin.setHours(0,0,0,0); + w1_end.setHours(23,59,59); + //console.log("week1 begin %o, end %o", w1_begin, w1_end); + //w1_end = new Date (w1_end.setDate(w1_end.getDate()+1)); //from 00:00 to 23:59; + var me = new Date(this.data.start); + return (w1_begin <= me && me <= w1_end ); + } + + is_week2() + { + var w2_begin = new Date($('span[name="w2d1"]').data().date); + var w2_end = new Date($('span[name="w2d7"]').data().date); + w2_begin.setHours(0,0,0,0); + w2_end.setHours(23,59,59); + var me = new Date(this.data.start); + return (w2_begin <= me && me <= w2_end ); + } + get_payment_summary(){ + var result ={}; + result.ot = this.get_is_high_pay(); + result.hour = this.get_working_duration(); + result.money = this.get_wages(); + return result; + } + get_is_high_pay() + { + var rate_info = this.get_rate_info_by_id(this.get_rate()); + return this.is_high_pay_hour(rate_info); + } + get_working_duration() + { + //finish - start + var f = new Date(this.get_finish()); + var s = new Date(this.get_start()); + var diff = f.getTime() - s.getTime(); + var hours = Math.floor(diff / 1000 / 60 / 60); + diff -= hours * 1000 * 60 * 60; + var minutes = Math.floor(diff / 1000 / 60); + var minute_to_hour = minutes/60; + return (hours + minute_to_hour); + } + get_wages(){ + var hour = this.get_working_duration(); + var rate_info = this.get_rate_info_by_id(this.get_rate()); + return hour * rate_info.RatePerUnit; + } + + get_rate_info_by_id(id){ + var rate_info = {}; + var rates = bts().earnings_rate; + for(var i =0; i< rates.length; i++){ + var r = rates[i]; + if(r.EarningsRateID == id){ + rate_info = $.extend(true,{}, r);//make a copy + break; + } + } + return rate_info; + } + + is_high_pay_hour(rate_info){ + var keywords =bts().high_pay_keywords; + var found = false; + return false; + keywords.forEach(function(e){ + if (-1 != rate_info.Name.toLowerCase().indexOf(e.toLowerCase()) ) + found = true; + }); + return found; + } + }//end of class Job + + //global GUI summary + function get_wages() + { + var txt = $('div.wages div').html(); + return parseInt(txt); + } + function set_wages(num){ + $('div.wages div').html(num); + } + + function set_working_hours(num){ + $('input#woh').attr('value', num); + } + + function get_working_hours(){ + var txt = $('input#woh').attr('value'); + return parseFloat(txt); + } + + + //modal box + function set_modal_title(selector, title){ + var s = 'div.bts_'+ selector +' .ult_modal-title'; + $(s).html(title); + } + + function set_modal_content(selector, content){ + var s = 'div.bts_'+ selector +' div.ult_modal-body.ult-html'; + $(s).html(content); + } + + function open_modal (selector){ + var s='div.bts_'+selector+'_button'; + $(s).trigger('click'); + } + +// setTimeout(function(){ +// set_modal_title('warning', 'suck title'); +// set_modal_content('warning', 'fucking details'); +// //open_modal('warning'); +// }, 1000); +// +// setTimeout(function(){ +// set_modal_title('error', 'error title'); +// set_modal_content('error', 'error details'); +// //open_modal('error'); +// }, 5000); + + $(document).on('mouseenter', 'div.week1 div', function(){ + $(this).addClass('blink_me'); + get_week2_partner(this).addClass('blink_me'); + blink_same_date_by_div(this); + }); + $(document).on('mouseleave', 'div.week1 div', function(){ + $(this).removeClass('blink_me'); + get_week2_partner(this).removeClass('blink_me'); + unblink_all_date(); + }); + + function get_week2_partner(div){ + var index = $(div).index()+1; + return $('div.week2 div:nth-child('+index+')'); + } + + function init_weekdays(){ + var curr = new Date; // get current date + init_weekdays_by_anchor(curr, true); + return; + } + + function init_weekdays_by_anchor(anchor, is_week1){ + var curr = new Date(anchor); // get the date; + + if (!is_week1){ //it is week2, shift for 7 days; + curr.setDate(curr.getDate() -7); //curr will be changed; + } + + var first = curr.getDate() - curr.getDay() + 1; //+1 we want Mon as first + var last = first + 6; // last day is the first day + 6 + + if (curr.getDay() == 0 ){// it's Sunday; + last = curr.getDate(); + first = last - 6; + } + + + var pos = 1; //first lot + for (var i=first; i<=last; i++) + { + var now = new Date(curr); + var d1 = new Date(now.setDate(i)); + now = new Date(curr); + var d2 = new Date(now.setDate(i+7)); + set_day_number(1,pos, d1); //week 1 + set_day_number(2,pos, d2); //week 2 + pos +=1; + } + } + + + function set_day_number(week, index, date){ + var selector = 'span[name="w'+week+'d'+index+'"]'; + $(selector).html(date.getDate()); + $(selector).data({date:date}); + //console.log('set w%d-d%d %o', week,index,date); + } + + function set_today(){ + var selector = 'div.sheettitle span[name="today"]'; + var curr = new Date; + $(selector).html(format_date(curr)); + } + + Date.prototype.get_week_number = function(){ + var d = new Date(Date.UTC(this.getFullYear(), this.getMonth(), this.getDate())); + var dayNum = d.getUTCDay() || 7; + d.setUTCDate(d.getUTCDate() + 4 - dayNum); + var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1)); + return Math.ceil((((d - yearStart) / 86400000) + 1)/7) + }; + + function set_week_number(){ + var date = $('span[name="w1d1"]').data().date; + //console.log("date %o", date); + var num = date.get_week_number(); + $('div.weekly span[name="week1"]').html(num); + $('div.weekly span[name="week2"]').html(num+1); + + set_week_boundry(); + } + + function set_week_boundry() + { + var date = $('span[name="w1d1"]').data().date; + $('#week1b').attr('value', format_date(date)); + var date = $('span[name="w2d7"]').data().date; + $('#week2b').attr('value', format_date(date)); + } + + function number_of_unsaved_job(){ + var count =0; + var total_job = $('div.bsave').length -1;//remove table header + var total_saved = $('div.bsave.saved').length; + var empty = $('div.emptyrecord').length; + count = total_job - total_saved - empty; + return count; + } + + $('div.prevweek.left').click(function(){ + if (number_of_unsaved_job() > 0){ + if(!confirm("you have unsaved jobs, proceed will lost them")) + return; + } + $('div.weekdays span.weekday').each(function(i, e){ + var date = $(e).data().date; + var newdate = new Date(date.setDate(date.getDate() -7 )); + $(e).html(newdate.getDate()); + $(e).data({data:newdate}); + }); + set_week_number(); + debounced_load_timesheet(); + }); + $('div.nextweek.right').click(function(){ + if (number_of_unsaved_job() > 0){ + if(!confirm("you have unsaved jobs,proceed will lost them")) + return; + } + + $('div.weekdays span.weekday').each(function(i, e){ + var date = $(e).data().date; + var newdate = new Date(date.setDate(date.getDate() +7 )); + $(e).html(newdate.getDate()); + $(e).data({data:newdate}); + }); + set_week_number(); + debounced_load_timesheet(); + }); + + $('div.weekly div.weekname.prev >input ').click(function(e){ + e.stopPropagation(); + }); + $('div.weekly div.weekname.prev >input ').change(function(e){ + var date = $('#week1b').attr('value'); + init_weekdays_by_anchor(date, true); + set_week_number(); + debounced_load_timesheet(); + }); + + $('div.weekly div.weekname.prev').click(function(){ + if (!confirm ('copy entire week to next week? ')) + return; + var jobs = []; + var job_els =[]; + $('div.week1 >div').each(function(i,e){ + var date = new Date($(e).find('span.weekday').data().date); + var strDate = format_date(date); //yyyy-mm-dd + $('div.bstart input').each(function(i,e){ + var value = $(e).attr('value'); + if( -1 != value.indexOf(strDate) ) //found + { + var el = $(e).closest('div.divTable'); + if (el.is(":visible")){ + var j = el.data().job; + var newj = clone_data_create_new_job(j.get_record_from_ui(),7);//add 7 days + job_els.push(newj.el); + } + } + }); + }); + show_jobs(job_els); + debounced_calculate(); + }); + + $('div.weekly div.weekname.next > input').click(function(e){ + e.stopPropagation(); + }); + + $('div.weekly div.weekname.next >input ').change(function(e){ + e.stopPropagation(); + var date = $('#week2b').attr('value'); + init_weekdays_by_anchor(date, false); + set_week_number(); + debounced_load_timesheet(); + }); + + + $('div.weekly div.weekname.next').click(function(e){ + $(e).find('input').trigger('click'); + }); + + + $('div.week1 > div').click(function(e){ + e.stopPropagation(); + if ($('div.bstart input.blink_me').length == 0){ + alert("nothing to copy"); + return; + } + if (!confirm ('copy to next week')) + return; + var jobs_el = []; + $('div.bstart input.blink_me').each(function(i,e){ + var r = copy_single_day_to_next_week(e); + if (r != false) + jobs_el.push(r.el); + }); + show_jobs(jobs_el); + unblink_all_date(); + }); + + $('div.week1,div.week2').click(function(e){ + e.stopPropagation(); + $(this).toggleClass('filtered'); + do_filter_workspace(); + }); + + function copy_single_day_to_next_week(el){ + var tb = $(el).closest('div.divTable'); + if (tb.is(':visible')){ + var j = $(tb).data().job; + var newj = clone_data_create_new_job(j.get_record_from_ui() , 7); // +7 days + return newj; + } + return false; + } + + function clone_data_create_new_job(val, num_of_shifted_days){ + var data = $.extend(true, {}, val);//make a copy + num_of_shifted_days = typeof num_of_shifted_days !=='undefined'? num_of_shifted_days: 0;// 0 days + //reset + data.id=''; + data.ack = 0; + data.rating = 0; + + if (is_valid_date_str(data.start)){ + var s = new Date(data.start); + var s1 = s.getDate() + num_of_shifted_days; + s = new Date(s.setDate(s1)); + data.start = format_date_time(s); + } + if (is_valid_date_str(data.finish)){ + var f = new Date(data.finish); + var f1 = f.getDate() + num_of_shifted_days; + f = new Date(f.setDate(f1)); + data.finish = format_date_time(f); + } + var newj = new Job(data); + return newj; + } + + function is_valid_date_str(val){ + var d = new Date(val); + if (d.toString()== 'Invalid Date') + return false; + return true; + } + + function blink_same_date_by_div(div){ + var date = new Date($(div).find('span.weekday').data().date); + blink_same_date(date); + } + + function blink_same_date(date){ + var strDate = format_date(date); //yyyy-mm-dd + var els=[]; + unblink_all_date(); + $('div.bstart input').each(function(i,e){ + if ( $(e).is(":visible") ){ + var value = $(e).attr('value'); + if( -1 != value.indexOf(strDate) ) //found + { + els.push(e); + $(e).addClass('blink_me'); + } + } + }); + } + + + function unblink_all_date(){ + $('div.bstart input').removeClass('blink_me'); + } + + $('div.sheettitle h1 span').click(function(){ + reset_title_to_today(); + load_timesheet(); + }); + + function reset_title_to_today(){ + set_today(); + init_weekdays(); + set_week_number(); + } + + var debounced_load_timesheet = debounce(load_timesheet,1000); + + function load_timesheet() + { + clear_workspace(); + var first = $('span[name="w1d1"]').data().date; + var last = $('span[name="w2d7"]').data().date; + $.post(bts().ajax_url, { // POST request + _ajax_nonce: bts().nonce, // nonce + action: "list_jobv1", // action + start: format_date(first), + finish: format_date(last), + }, function(response, status, xhr){ + if (response.status =='success'){ + var job_els = []; + response.jobs.forEach(function(job){ + //console.log('loading job... %o', job); + var o = new Job(job); + job_els.push(o.el); + }); + show_jobs(job_els, 'in-ajax=true'); + //filter it if reqired + debounced_filter_workspace(); + }else{ + alert('error loading job'); + } + hide_loading_jobs(); + }); + + } + + function show_jobs(job_els, in_ajax){ + if (job_els.length >0){ + $('div.workspace').append(job_els); + job_els[0].get(0).scrollIntoView(); + console.log('loading ... %d jobs', job_els.length); + } + if (typeof in_ajax =='undefined') + dtp_init(); + } + + function format_date(date){ + var dd = date.getDate(); + var mm = date.getMonth() + 1; //January is 0! + + var yyyy = date.getFullYear(); + if (dd < 10) { + dd = '0' + dd; + } + if (mm < 10) { + mm = '0' + mm; + } + return yyyy + '-' + mm + '-' +dd ; + } + + function format_date_time(date){ + var strdate = format_date(date); + var hh = date.getHours(); + if (hh<10){ + hh= '0' + hh; + } + var mm = date.getMinutes(); + if (mm<10){ + mm='0' + mm; + } + return strdate + ' ' + hh + ":" + mm; + } + + function clear_workspace()//clear all timesheet jobs + { + $('div.workspace > div.divTable').remove(); + //clear datetime picker + $('div.xdsoft_datetimepicker').remove(); + // + show_loading_jobs(); + } + + $('div.workinghours').click(function(){ + $('div.bts_message_button').trigger('click'); + }); + + $('button[name="confirmschedule"]').click(function(){ + if (!confirm('sending email to each staff for their job arrangement?')) + return; + $('div.bts_message .ult-overlay-close-inside').hide(); + $('div.bts_message_button').trigger('click'); + setTimeout(do_email_jobs, 2000);//2 seconds for dialog to popup + }); + + $('button[name="confirmschedule"]').mouseenter(function(){ + $('div.week2').addClass('blink_me'); + }) + $('button[name="confirmschedule"]').mouseleave(function(){ + $('div.week2').removeClass('blink_me'); + }) + var debounced_filter_workspace = debounce(do_filter_workspace, 1000); + $(document).on('click','div.userlist', debounced_filter_workspace); + + function do_filter_workspace(){ + var staffs =[]; + $('div.stafflist div.peopleitem :checked').each(function(i, e){ + if ($(e).parent().is(':visible')){ + var id = $(e).parent().attr('data-id'); + //console.log("%o, id=%s", e, id); + staffs.push(id.substring(1)); + } + }); + + var clients =[]; + $('div.clientlist div.peopleitem :checked').each(function(i, e){ + if ($(e).parent().is(':visible')){ + var id = $(e).parent().attr('data-id'); + //console.log("%o, id=%s", e, id); + clients.push(id.substring(1)); + } + }); + + filter_workspace(staffs, clients); + filter_workspace_by_weeks(); + debounced_calculate(); + } + + function filter_workspace(staffs, clients){ + + //if both array is empty + if( (staffs === undefined || staffs.length ==0) && + (clients===undefined || clients.length ==0)){ + //show all + $('div.workspace div.divTable').show(); + return; + } + + //if staffs is empty, we only filter by client + if (staffs === undefined || staffs.length ==0){ + filter_workspace_by_client(clients); + return; + } + + //if clients is empty, we only filter by staff + if (clients===undefined || clients.length ==0){ + filter_workspace_by_staff(staffs); + return; + } + //filter by both + filter_workspace_by_both(staffs, clients); + + } + + function filter_workspace_by_staff(staffs) + { + //filter some of them; + $('div.workspace div.divTable').each(function(i,e){ + var job = $(e).data().job; + var s = job.get_staff(); + + if (staffs.indexOf(s) ==-1) + $(this).fadeOut(); + else + $(this).fadeIn(); + }); + } + + function filter_workspace_by_client(clients) + { + //filter some of them; + $('div.workspace div.divTable').each(function(i,e){ + var job = $(e).data().job; + var c = job.get_client(); + + if (clients.indexOf(c) ==-1) + $(this).fadeOut(); + else + $(this).fadeIn(); + }); + } + + function filter_workspace_by_both(staffs, clients) + { + //filter some of them; + $('div.workspace div.divTable').each(function(i,e){ + var job = $(e).data().job; + var s = job.get_staff(); + var c = job.get_client(); + + if (staffs.indexOf(s) ==-1 || clients.indexOf(c) ==-1) + $(this).fadeOut(); + else + $(this).fadeIn(); + }); + } + + function filter_workspace_by_weeks(){ + var hide_week1 = $('div.week1').hasClass('filtered'); + var hide_week2 = $('div.week2').hasClass('filtered'); + + if (hide_week1 && hide_week2 ){ + alert("You are hiding both weeks"); + } + + $('div.workspace div.divTable').each(function(i,e){ + var job = $(e).data().job; + if ((hide_week1 && job.is_week1()) || + (hide_week2 && job.is_week2()) ){ + $(e).fadeOut(); + } + }); + } + + var debounced_calculate = debounce(calculate_total_hour_and_money, 2000); + + function calculate_total_hour_and_money() + { + //init pays for all staff; + var pays={ + total: 0, + hours: 0, + }; + $('.stafflist > div.peopleitem').each(function(i,e){ + var people = $(this).data().obj; + people.reset_summary(); + }); + + $('div.workspace > .divTable').each(function(i,e){ + if (! $(e).is(':visible')) + return; + + var job = $(e).data().job; //class Job + if (typeof job === 'undefined') + return; + var ps = job.get_payment_summary(); + + pays.total += ps.money; + pays.hours += ps.hour; + + var staff = job.get_staff(); + var people = find_staff(staff); //class People + if (people !=false) + people.add_payment_summary(ps); + }); + set_wages(pays.total.toFixed(2)); + set_working_hours(pays.hours.toFixed(2)); + } + + function find_staff(login) + { + var d = $('#p'+login).data(); + if (typeof d === 'undefined') + return false; + return $('#p'+login).data().obj; + } + + $(document).on('change', '.divTableRow select, .divTableRow input', function() { + var job = $(this).closest('.divTable').data().job; + job.validate(); + job.mark_dirty(); + debounced_calculate(); + }); + + function init_ts(){ + show_loading_jobs(); + list_staff(); + list_clients(); + xero(false); + wifi(false); + csv(false); + init_user_search(); + //ajax_earning_rate(); + reset_title_to_today(); + load_timesheet(); + } + + function do_email_jobs() + { + var selector = 'div.bts_message div.ult_modal-body'; + $(selector).html('Analysis staff jobs ... ok'); + var staff = bts().staff.slice(0);//copy this array; + var s = staff.pop(); + + //week2 start + var w2_begin = new Date($('span[name="w2d1"]').data().date); + var w2_end = new Date($('span[name="w2d7"]').data().date); + var start = format_date(w2_begin); + var finish = format_date(w2_end); + + function do_staff(){ + var el = $('

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

"); + $(selector).append(el); + el[0].scrollIntoView(); + + $.post(bts().ajax_url, { // POST request + _ajax_nonce: bts().nonce, // nonce + action: "email_job", // action + staff : s.login, + start : start, + finish: finish, + }, function(response, status, xhr){ + if (response.status == 'success'){ + if (response.sent){ + el.append('' + response.emailstatus + ''); + }else{ + el.append('' + response.emailstatus + ''); + } + }else{ + el.append(' Error[' + response.error + ' ...]'); + } + }).fail(function(){ + el.append('' + 'Network Error occured' + ''); + //clear staff pending list, stop further processing + s = []; + }).always(function(){//next staff + if (staff.length >0){ + s = staff.pop(); + setTimeout(do_staff, 100); //a short delay makes it looks nice + }else{ + $('div.bts_message .ult-overlay-close-inside').show(); + $('div.bts_message .ult-overlay-close-inside').addClass('blink_me'); + $('div.week2').removeClass('blink_me'); + $(selector).append('All staff confirmed! '); + } + }); + } + //execute + do_staff(); + } + +// function ajax_earning_rate(){ +// $.post(bts().ajax_url, { // POST request +// _ajax_nonce: bts().nonce, // nonce +// action: "earnings_rate", // action +// }, function(response, status, xhr){ +// bts().earnings_rate = response; +// console.log("%o", bts().earnings_rate); +// }); +// } + + + $( ".boundary_datepicker" ).datepicker(); + $( ".boundary_datepicker" ).datepicker("option", "dateFormat", "yy-mm-dd"); + + $(document).on('click', 'div.clientlist div[name="title"] a', function(e){ + e.preventDefault(); + e.stopPropagation(); + var id = $(this).closest('label.peopleitem').attr('data-id').substring(1); + var str = 'https://acaresydncy.com.au/feedback_card/' + id; + var name = $(this).html(); + if ( confirm ("Email feedback link of : " + name + "\n\n\n" + str + "\n\n\n to helen@acaresydney.com.au?")){ + $.post(bts().ajax_url, { // POST request + _ajax_nonce: bts().nonce, // nonce + action: "email_feedback_url", // action + client : id, + }, function(response, status, xhr){ + //alert('please check your email on the phone and SMS the link to your client'); + }).fail(function(){ + alert('network error '); + }); + } + }); + + init_ts(); + + +/*________________________________________________________________________*/ + }); +})(jQuery); + + +/*______________scrolling______________________________________________*/ +jQuery(document).ready(function(){ + var timeoutid =0; + + jQuery('button.peoplelist[name="down"]').mousedown(function(){ + var button = this; + timeoutid = setInterval(function(){ + //console.log("down scrotop %d ", jQuery(button).parent().find(".userlist").get(0).scrollTop ); + jQuery(button).parent().find(".userlist").get(0).scrollTop +=240; + }, 100); + }).on('mouseup mouseleave', function(){ + clearTimeout(timeoutid); + }); + + jQuery('button.peoplelist[name="up"]').mousedown(function(){ + var button = this; + timeoutid = setInterval(function(){ + //console.log("up scrotop %d ", jQuery(button).parent().find(".userlist").get(0).scrollTop ); + jQuery(button).parent().find(".userlist").get(0).scrollTop -=240; + }, 100); + }).on('mouseup mouseleave', function(){ + clearTimeout(timeoutid); + }); + +}); + + diff --git a/ts.php b/ts.php index 25a47e6..fd1445c 100644 --- a/ts.php +++ b/ts.php @@ -22,14 +22,23 @@ class AcareOffice{ private $bts_week_id = 1; //week 1, we will try to calculate current week; private $xero ; private $db; - private $table_name; + private $table_name; //default to job_table + private $job_table; + private $allowance_table; private $addr_table; + private $ndis_table; + private $apiv1; public function __construct() { + $this->setup_db_name(); + $this->class_loader(); + $this->apiv1 = new Apiv1($this, $this->job_table); + + add_option( "acare_ts_db_version", "1.0" ); register_activation_hook( __FILE__, array($this, 'db_install') ); - add_action('init', array($this, 'class_loader')); + //add_action('init', array($this, 'class_loader')); add_action('wp', array($this, 'check_auth')); add_action('wp_enqueue_scripts', array($this, 'register_js_css'), 99); @@ -61,6 +70,7 @@ class AcareOffice{ add_action('wp_ajax_list_client', array($this,'list_client' )); add_action('wp_ajax_save_job', array($this,'save_job' )); add_action('wp_ajax_list_job', array($this,'list_job' )); + add_action('wp_ajax_delete_job', array($this,'delete_job' )); add_action('wp_ajax_email_job', array($this,'email_job' )); add_action('wp_ajax_email_feedback_url', array($this,'email_feedback_url' )); @@ -93,12 +103,18 @@ class AcareOffice{ add_filter('query_vars', array($this,'add_query_vars')); + + } + + private function setup_db_name() + { global $wpdb; $this->db = $wpdb; - $this->table_name = $wpdb->prefix . 'acare_ts'; + $this->table_name = $wpdb->prefix . 'acare_ts'; //for backward compatability; + $this->job_table = $wpdb->prefix . 'acare_ts'; + $this->allowance_table = $wpdb->prefix . 'acare_allowance'; $this->addr_table = $wpdb->prefix . 'acare_addr_distance'; $this->ndis_table = $wpdb->prefix . 'acare_ndis_price'; - } /** @@ -144,7 +160,7 @@ class AcareOffice{ header("Content-Disposition:attachment; filename=$filename"); header("Content-Type: application/force-download"); //readfile(dirname(__FILE__) . "/img/circle.png"); - echo "fuck this file"; + echo "content of this file"; exit(); } @@ -398,6 +414,7 @@ class AcareOffice{ $this->bts_user_id = get_query_var( 'bts_user_id' ) ; $this->register_bts_js(); $this->register_timesheet_js_css(); + $this->register_office_js_css(); $this->register_task_js_css(); $this->register_feedback_card_js_css(); $this->register_xeroc_js_css(); @@ -439,6 +456,27 @@ class AcareOffice{ $url = plugins_url('jquery-ui-1.11.4.theme/jquery-ui.min.css', __FILE__); wp_enqueue_style('jquery-ui-smoothness', $url, false, null); } + + private function register_office_js_css(){ + global $pagename; + if ($pagename != 'office'){ + return; + } + wp_enqueue_style( 'bts_office', plugins_url('css/bts_office.css', __FILE__)); + wp_enqueue_script( 'bts_office', plugins_url('js/bts_office.js', __FILE__), array( 'jquery' , 'bts' )); + wp_enqueue_script('mustache', plugins_url('js/mustache.min.js', __FILE__), array('jquery')); + + global $wp_scripts; + wp_enqueue_script('jquery-ui-datepicker'); + // get registered script object for jquery-ui + //$ui = $wp_scripts->query('jquery-ui-core'); + // tell WordPress to load the Smoothness theme from Google CDN + //$protocol = is_ssl() ? 'https' : 'http'; + // $url = "$protocol://ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.min.css"; + $url = plugins_url('jquery-ui-1.11.4.theme/jquery-ui.min.css', __FILE__); + wp_enqueue_style('jquery-ui-smoothness', $url, false, null); + } + private function register_task_js_css(){ global $pagename;