labor-entry-page.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. const app = require('../app')
  2. const moment = require('moment-immutable')
  3. const { editIcon, calculatedIcon, dollarIcon } = require('../assets')
  4. const _ = require('lodash')
  5. app.component('appLaborEntryPage', {
  6. template: html`
  7. <app-user-area title-text="{{ctrl.terminalKey}} Labor Entry">
  8. <app-breadcrumb links="[
  9. { text: 'Home', link: '/dashboard' },
  10. { text: ctrl.terminalKey + ' Labor', link: '/labor/' + ctrl.terminalKey },
  11. { text: ctrl.startDate.format('L') + ' - ' + ctrl.endDate.format('L'), link: '/labor/' + ctrl.terminalKey + '/' + ctrl.startDate.format('YYYY-MM-DD') }
  12. ]"></app-breadcrumb>
  13. <p>For week of {{ctrl.startDate.format('LL')}} to {{ctrl.endDate.format('LL')}}</p>
  14. <div>
  15. <md-input-container class="md-margin">
  16. <label>Labor Cost</label>
  17. <input type="text" readonly value="{{ctrl.model.workweek.laborCost | currency}}" />
  18. <md-icon md-svg-src="${calculatedIcon}">
  19. <md-tooltip md-direction="down" style="height: 7em">
  20. This field is calculated, <br/>
  21. and cannot be edited directly. <br />
  22. Save your changes to update.
  23. </md-tooltip>
  24. </md-icon>
  25. </md-input-container>
  26. <md-input-container class="md-margin">
  27. <label>Labor Charge</label>
  28. <input type="number" min="0" step="0.01" ng-model="ctrl.model.workweek.laborCharge" />
  29. <md-icon md-svg-src="${dollarIcon}"></md-icon>
  30. </md-input-container>
  31. <md-input-container class="md-margin" ng-if="ctrl.model.workweek.laborCharge && ctrl.model.workweek.laborCost">
  32. <label>Difference</label>
  33. <input type="text" readonly value="{{(ctrl.model.workweek.laborCost - ctrl.model.workweek.laborCharge) | currency}} or {{(ctrl.model.workweek.laborCharge / ctrl.model.workweek.laborCost) * 100 | number : 1}}%" />
  34. <md-icon md-svg-src="${calculatedIcon}">
  35. <md-tooltip md-direction="down" style="height: 7em">
  36. This field is calculated, <br/>
  37. and cannot be edited directly. <br />
  38. Save your changes to update.
  39. </md-tooltip>
  40. </md-icon>
  41. </md-input-container>
  42. </div>
  43. <form name="form" ng-submit="ctrl.submit()">
  44. <table md-table md-progress="ctrl.promise">
  45. <colgroup>
  46. <col style="width: 15%" />
  47. <col ng-repeat="weekday in ctrl.model.weekdays" style="width: 12%" />
  48. </colgroup>
  49. <thead md-head>
  50. <tr md-row>
  51. <th md-column>Staff Member</th>
  52. <th md-column ng-repeat="weekday in ::ctrl.weekdays">
  53. <span hide show-xs>{{::weekday.min}} {{::weekday.date.format('M/DD')}}</span>
  54. <span hide show-sm>{{::weekday.short}} {{::weekday.date.format('M/DD')}}</span>
  55. <span hide show-gt-sm>{{::weekday.name}}<br />{{::weekday.date.format('L')}}</span>
  56. </th>
  57. </tr>
  58. </thead>
  59. <tbody md-body>
  60. <tr md-row ng-repeat="sfl in ctrl.staffMemberLabor track by sfl.id" ng-init="$sflIndex = $index">
  61. <td md-cell>
  62. {{ctrl.staffMembers[sfl.id].name}}
  63. </td>
  64. <td md-cell ng-repeat="day in sfl.days track by $index" ng-init="$dayIndex = $index">
  65. <input
  66. class="hour-input md-button "
  67. tabindex="{{$index * ctrl.staffMemberLabor.length + ($parent.$index) + 1}}"
  68. ng-model="day.hours"
  69. type="number" min="0" step="0.01"
  70. ng-keypress="ctrl.keypress($event)"
  71. md-whiteframe="{{(day.hours == ctrl.saved[$sflIndex].days[$dayIndex].hours) ? 1 : 8}}">
  72. </td>
  73. </tr>
  74. </tbody>
  75. </table>
  76. <div>
  77. <md-button type="submit" class="md-raised md-primary">Submit</md-button>
  78. </div>
  79. </form>
  80. </app-user-area>
  81. `,
  82. controllerAs: 'ctrl',
  83. controller: function(api, $routeParams, weekdays, $mdToast) {
  84. this.terminalKey = $routeParams.terminal
  85. const week = moment($routeParams.week)
  86. if (!week.isSame(week.startOf('week'))) throw new Error('Date is not start of week')
  87. this.startDate = week
  88. this.endDate = week.endOf('week')
  89. this.weekdays = weekdays.map(d => Object.assign({}, d, {date: week.add(d.value, 'days')}))
  90. api.staffMemberDictionary().then(staffMembers => {
  91. this.staffMembers = staffMembers
  92. })
  93. const load = () => {
  94. this.promise = api.get(`/api/labor/${$routeParams.terminal}/${$routeParams.week}`).then((model) => {
  95. this.model = model
  96. const staffMemberIds = this.model.workdays[0].labor.map(x => x.staffMemberId)
  97. this.staffMemberLabor = staffMemberIds.map((id, i) => ({
  98. id,
  99. days: this.model.workdays.map(wd => wd.labor[i])
  100. }))
  101. this.saved = _.cloneDeep(this.staffMemberLabor)
  102. })
  103. }
  104. load()
  105. let saveTimer = null
  106. this.save = async (delay, then) => {
  107. if (saveTimer) {
  108. clearTimeout(saveTimer)
  109. }
  110. const model = {
  111. workweek: this.model.workweek,
  112. workdays: this.model.workdays.map(workday => ({
  113. labor: workday.labor.map(labor => ({
  114. staffMemberId: labor.staffMemberId,
  115. hours: labor.hours || null
  116. }))
  117. }))
  118. }
  119. const saved = _.cloneDeep(this.staffMemberLabor)
  120. saveTimer = setTimeout(async () => {
  121. saveTimer = null
  122. try {
  123. await api.patch(`/api/labor/${$routeParams.terminal}/${$routeParams.week}`, model)
  124. $mdToast.showSimple('Labor saved.')
  125. this.saved = saved
  126. if (then) {
  127. then()
  128. }
  129. } catch (err) {
  130. window.err = err
  131. console.error(err)
  132. $mdToast.showSimple(`Could not save Labor: ${err.message || err.statusText || err}`)
  133. }
  134. }, delay || 0)
  135. }
  136. this.submit = () => this.save(0, load)
  137. this.keypress = async ($event) => {
  138. if ($event.key === 'Enter') {
  139. const tabIndex = +$event.srcElement.getAttribute('tabindex') + 1
  140. const nextElement = document.querySelector(`[tabindex="${tabIndex}"]`)
  141. if (nextElement) {
  142. $event.returnValue = false
  143. nextElement.focus()
  144. nextElement.select && nextElement.select()
  145. }
  146. await this.save(2000)
  147. }
  148. }
  149. }
  150. })