Vue, setting table rows of data based on date

Viewed 288

My vue application is currently iterating through a data object of employees with hours and swipes, which is being multiplied for each employee every day.

This is working in computing new rows of data so that I have all given data for each day.

However, in my template, I'm trying to set the multipled data to show in the column with the corresponding date and give only one row for each employee. My expected results would be

Employee  |  2021-08-31  |  2021-09-01  |  2021-09-02
-----------------------------------------------------
Evan        60                60
Stan        100               200
Kelly       60                             164

How can I modify what I'm doing so that I can end up with only one row per employee and put the data into the correct column based on the column-header date and the date in the object for that record?

<div id="app">
  <table>
    <thead>
      <tr>
        <th>Employee</th>
        <th>2021-08-31</th>
        <th>2021-09-01</th>
        <th>2021-09-02</th>
        <th>Grand Total</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(row, index) in compRows">
        <td v-html="row.employee"></td>
        <td v-if="row.workDate == {{$date->addDay()->format($format)}}"></td>
        <td v-html="row.hours"></td>
      </tr>
    </tbody>
  </table>
</div>


@endsection

@section('loadjs')

<script>

new Vue({
  el: "#app",
  data: {
    rows: [
        {  
            employee: "Evan",
            hours: "15",
            workDate: "2021-08-31",
            swipes: "4",
            
        },
        {  
            employee: "Kelly",
            hours: "15",
            workDate: "2021-08-31",
            swipes: "4",
            
        },
        {  
            employee: "Evan",
            hours: "15",
            workDate: "2021-09-01",
            swipes: "4",
           
        },
        {  
            employee: "Stan",
            hours: "25",
            workDate: "2021-08-31",
            swipes: "4",
            
        },
        {  
            employee: "Kelly",
            hours: "82",
            workDate: "2021-09-02",
            swipes: "2",
            
        },
        {  
            employee: "Stan",
            hours: "40",
            workDate: "2021-09-01",
            swipes: "5",
           
        }
    ]
  },
  methods: {

  },
  computed: {
    compRows() {
      const grouped = this.rows.reduce((r, o) => {
        r[o.employee] ??= {};
        r[o.employee][o.workDate] ??= {employee: o.employee, workDate: o.workDate, swipes: 0, hours: 0};
        r[o.employee][o.workDate].swipes += +o.swipes;
        r[o.employee][o.workDate].hours += +o.hours;
        return r;
      }, {});
      return Object.values(grouped).map(o => Object.values(o)).flat();
    }
  }
});


</script>
1 Answers
  1. Instead of flattening the grouped data in the compRows prop (renamed below as hoursByEmployee), keep it as an object to lookup employee hours by date later. In addition, track a total property that stores the grand total (swipes by hours).

    export default {
      computed: {
        hoursByEmployee() {
          return this.rows.reduce((r, o) => {
            r[o.employee] ??= {}
            r[o.employee][o.workDate] ??= {swipes: 0, hours: 0}
            r[o.employee][o.workDate].swipes += +o.swipes
            r[o.employee][o.workDate].hours += +o.hours
            r[o.employee].total ??= 0 
            r[o.employee].total += (+o.swipes * +o.hours) 
            return r
          }, {})
        },
      }
    }
    
  2. Use another computed property to get the available dates from the rows' workDate, sorted chronologically:

    export default {
      computed: {
        dates() {
          const dates = [...new Set(this.rows.map(r => r.workDate))]
          return dates.sort((a,b) => new Date(a) - new Date(b))
        },
      }
    }
    
  3. In the template's table header, render the computed dates:

    <thead>
      <tr>
        <th>Employee</th>
        <th v-for="date in dates" :key="date">{{ date }}</th> 
        <th>Grand Total</th>
      </tr>
    </thead>
    
  4. In the template's table body, render the computed employee hours by date, using the hoursByEmployee lookup and dates created above. If the hours exist for the date, render the daily total. Also, render the grand total property in the last column.

    <tbody>
      <tr v-for="(hours, employee) in hoursByEmployee" :key="employee">
        <td>{{ employee }}</td>
        <td v-for="date in dates" :key="date">{{ hours[date] && hours[date].swipes * hours[date].hours }}</td> 
        <td>{{ hours.total }}</td> 
      </tr>
    </tbody>
    

demo

Related