dashboard-page.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. const app = require('../app')
  2. const moment = require('moment-immutable')
  3. const _ = require('lodash')
  4. const { previousWeekIcon, previousDayIcon, nextDayIcon, nextWeekIcon } = require('../assets')
  5. app.component('appDashboardPage', {
  6. template: html`
  7. <app-user-area title-text="Dashboard">
  8. <div>
  9. <md-button
  10. class="md-fab"
  11. ng-click="$ctrl.setOffset($ctrl.offset - 7)">
  12. <md-icon md-svg-src="${previousWeekIcon}"></md-icon>
  13. </md-button>
  14. <md-button
  15. class="md-fab"
  16. ng-click="$ctrl.setOffset($ctrl.offset - 1)">
  17. <md-icon md-svg-src="${previousDayIcon}"></md-icon>
  18. </md-button>
  19. <span
  20. ng-repeat="workday in $ctrl.workdays track by workday.formatted"
  21. ng-init="
  22. invalid = workday.hasData && (!workday.hasLabor || !workday.hasService);
  23. valid = workday.hasData && workday.hasLabor && workday.hasService;
  24. tooltip = workday.hasData
  25. ? (
  26. workday.hasLabor && workday.hasService
  27. ? ''
  28. : workday.hasLabor
  29. ? workday.formatted + ' has Labor data, but no Service data.'
  30. : workday.hasService
  31. ? workday.formatted + ' has Service data, but no Service data.'
  32. : workday.formatted + ' is missing Labor and Service data.'
  33. ): 'No data exists for ' + workday.formatted + '.';"
  34. title="{{tooltip}}"
  35. ng-click="tooltip && $ctrl.$mdToast.showSimple(tooltip)">
  36. <md-button
  37. style="min-width: auto; width: 3em;"
  38. class="md-raised md-mini"
  39. ng-class="{
  40. 'md-primary': workday.date.valueOf() == $ctrl.date.valueOf()
  41. }"
  42. ng-disabled="!valid"
  43. ng-click="$ctrl.setDate(workday.date)"
  44. md-badge="{{
  45. invalid
  46. ? '!'
  47. : ''
  48. }}">
  49. {{workday.short}}
  50. </md-button>
  51. </span>
  52. <md-button
  53. class="md-fab"
  54. ng-click="$ctrl.setOffset($ctrl.offset + 1)">
  55. <md-icon md-svg-src="${nextDayIcon}"></md-icon>
  56. </md-button>
  57. <md-button
  58. class="md-fab"
  59. ng-click="$ctrl.setOffset($ctrl.offset + 7)">
  60. <md-icon md-svg-src="${nextWeekIcon}"></md-icon>
  61. </md-button>
  62. </div>
  63. <h2>{{$ctrl.date.format('LL')}}</h2>
  64. <div flex layout="row" ng-show="$ctrl.statistics">
  65. <md-card flex ng-repeat="serviceCategory in $ctrl.statistics.serviceCategories">
  66. <md-card-content>
  67. <h2>{{::$ctrl.serviceCategories[serviceCategory.key].name}}</h2>
  68. <div flex layout="row">
  69. <md-card flex="30" md-whiteframe="15">
  70. <md-card-title><md-card-title-text style="text-align: center">
  71. Cartons
  72. </md-card-title-text></md-card-title>
  73. <md-card-content>
  74. <h1 style="text-align: center">{{serviceCategory.lastMetrics[serviceCategory.serviceColumn] || 0}}</h1>
  75. </md-card-content>
  76. </md-card>
  77. <md-card flex="30" md-whiteframe="15">
  78. <md-card-title><md-card-title-text style="text-align: center">
  79. Labor Cost
  80. </md-card-title-text></md-card-title>
  81. <md-card-content>
  82. <h1 ng-if="serviceCategory.lastMetrics.laborCost" style="text-align: center">$\{{Math.floor(serviceCategory.lastMetrics.laborCost) || null}}</h1>
  83. <h1 ng-if="!serviceCategory.lastMetrics.laborCost" style="text-align: center">N/A</h1>
  84. </md-card-content>
  85. </md-card>
  86. <md-card flex="30" md-whiteframe="15">
  87. <md-card-title><md-card-title-text style="text-align: center">
  88. Cost per Carton
  89. </md-card-title-text></md-card-title>
  90. <md-card-content>
  91. <h1 ng-if="serviceCategory.lastMetrics.costPer" style="text-align: center">{{serviceCategory.lastMetrics.costPer | currency}}</h1>
  92. <h1 ng-if="!serviceCategory.lastMetrics.costPer" style="text-align: center">N/A</h1>
  93. </md-card-content>
  94. </md-card>
  95. </div>
  96. <div ng-repeat="terminal in serviceCategory.terminals">
  97. <table md-table>
  98. <thead>
  99. <th align="left">
  100. <h4>{{::$ctrl.terminals[terminal.terminal].name}}</h4>
  101. </th>
  102. <th align="right">
  103. <span ng-if="$index == 0">Cost per Carton</span>
  104. </th>
  105. </thead>
  106. <tbody>
  107. <tr ng-repeat="laborCategory in terminal.laborCategories">
  108. <th align="left">{{::$ctrl.laborCategories[laborCategory.laborCategory].name}}</th>
  109. <td align="right">{{laborCategory.costPer | currency}}</td>
  110. </tr>
  111. <tr ng-if="terminal.laborCategories.length > 1">
  112. <th align="left">All</th>
  113. <td align="right">{{terminal.costPer | currency}}</td>
  114. </tr>
  115. </tbody>
  116. </table>
  117. <hr />
  118. </div>
  119. <div ng-if="serviceCategory.terminals.length > 1">
  120. <table md-table>
  121. <thead>
  122. <th align="left">
  123. <h4>Overall</h4>
  124. </th>
  125. <th align="right"></th>
  126. </thead>
  127. <tbody>
  128. <tr ng-repeat="laborCategory in serviceCategory.laborCategories">
  129. <th align="left">{{::$ctrl.laborCategories[laborCategory.laborCategory].name}}</th>
  130. <td align="right">{{laborCategory.costPer | currency}}</td>
  131. </tr>
  132. <tr ng-if="serviceCategory.laborCategories.length > 1">
  133. <th align="left">All</th>
  134. <td align="right">{{serviceCategory.costPer | currency}}</td>
  135. </tr>
  136. </tbody>
  137. </table>
  138. </div>
  139. </md-card-content>
  140. </md-card>
  141. </div>
  142. <div flex layout="column" ng-show="$ctrl.charts" layout-gt-sm="row">
  143. <md-card flex="50" class="mg-margin md-padding">
  144. <md-card-title>
  145. <md-card-title-text>
  146. <span class="md-headline">Labor Costs</span>
  147. <span class="md-subhead"></span>
  148. </md-card-title-text>
  149. </md-card-title>
  150. <md-card-content>
  151. <div style="width: 520px; height: 260px;" >
  152. <canvas class="chart chart-line" chart-data="$ctrl.charts.laborCost.data" chart-colors="$ctrl.charts.laborCost.colors"
  153. chart-labels="$ctrl.charts.laborCost.labels" chart-series="$ctrl.charts.laborCost.series" chart-options="$ctrl.charts.laborCost.options">
  154. </canvas>
  155. </div>
  156. <div ng-repeat="color in $ctrl.charts.laborCost.colors">
  157. <div style="background-color: {{color}}; width: 16px; height: 16px; display: inline-block; margin: 0 4px;"></div>
  158. {{$ctrl.charts.laborCost.series[$index]}}
  159. </div>
  160. </md-card-content>
  161. </md-card>
  162. <md-card flex="50" class="mg-margin md-padding">
  163. <md-card-title>
  164. <md-card-title-text>
  165. <span class="md-headline">Cost per Carton</span>
  166. <span class="md-subhead"></span>
  167. </md-card-title-text>
  168. </md-card-title>
  169. <md-card-content>
  170. <div style="width: 520px; height: 260px;" >
  171. <canvas class="chart chart-line" chart-data="$ctrl.charts.costPerCarton.data" chart-colors="$ctrl.charts.costPerCarton.colors"
  172. chart-labels="$ctrl.charts.costPerCarton.labels" chart-series="$ctrl.charts.costPerCarton.series" chart-options="$ctrl.charts.costPerCarton.options">
  173. </canvas>
  174. </div>
  175. <div ng-repeat="color in $ctrl.charts.costPerCarton.colors">
  176. <div style="background-color: {{color}}; width: 16px; height: 16px; display: inline-block; margin: 0 4px;"></div>
  177. {{$ctrl.charts.costPerCarton.series[$index]}}
  178. </div>
  179. </md-card-content>
  180. </md-card>
  181. </div>
  182. </app-user-area>
  183. `,
  184. controller: function(api, statistics, $scope, $mdToast) {
  185. this.$mdToast = $mdToast
  186. $scope.Math = Math
  187. const load = (date) => {
  188. api.statistics(date).then(stats => {
  189. this.statistics = stats
  190. this.charts = statistics.charts(stats.metricsOverTime)
  191. this.last = stats.last
  192. })
  193. }
  194. api.terminals().then(terminals => {
  195. this.terminals = terminals
  196. })
  197. api.laborCategories().then(laborCategories => {
  198. this.laborCategories = laborCategories
  199. })
  200. api.serviceCategories().then(serviceCategories => {
  201. this.serviceCategories = serviceCategories
  202. })
  203. api.get(`/api/workdays`).then(results => {
  204. const wds = {}
  205. results.workdays.forEach(wd => (wds[moment(wd.date).format('YYYY-MM-DD')] = wd))
  206. const getWorkdays = (offset) => {
  207. const dates = _.range(offset - 6, offset + 1)
  208. .map(x => {
  209. const date = moment().startOf('day').add(x, 'days')
  210. const workday = wds[date.format('YYYY-MM-DD')]
  211. return {
  212. date,
  213. formatted: date.format('L'),
  214. short: date.format('M/DD'),
  215. hasData: !!workday,
  216. hasLabor: workday && workday.hasLabor,
  217. hasService: workday && workday.hasService
  218. }
  219. })
  220. return dates
  221. }
  222. this.setOffset = (offset) => {
  223. this.offset = offset
  224. this.workdays = getWorkdays(offset)
  225. }
  226. this.setDate = (date) => {
  227. this.date = date
  228. this.now = moment()
  229. load(date)
  230. }
  231. const defaultWorkday = results.workdays.slice(0).reverse().find(x => x.hasLabor && x.hasService)
  232. if (defaultWorkday) {
  233. this.setDate(moment(defaultWorkday.date))
  234. this.setOffset(3 - (moment().diff(moment(defaultWorkday.date), 'days')))
  235. } else {
  236. this.setOffset(0)
  237. }
  238. // this.setDate(moment(
  239. // results.workdays
  240. // .map(wd => moment(wd.date).valueOf())
  241. // .sort()
  242. // .pop() || moment.now()
  243. // ))
  244. })
  245. // statistics.costPerCarton().then(statistics => {
  246. // Object.assign(this, statistics)
  247. // })
  248. }
  249. })
  250. window.moment = moment