JSFiddle - React, Tailwind, and code Playground

by Damian Dulisz

HTML

<div id="app">
 <table>
   <thead>
      <tr>
        <th>Metrics</th>
        <th v-for="metric of metrics">
          {{ metric.month }}
        </th>
        <th>{{ diff.name }}</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Revenue</td>
        <td v-for="metric of metrics">
          <input v-model="metric.revenue" type="number">
        </td>
        <td :class="getClass(diff.revenue)">{{ diff.revenue }}%</td>
      </tr>
      <tr>
        <td>Costs</td>
        <td v-for="metric of metrics">
          <input v-model="metric.costs" type="number">
        </td>
        <td :class="getClass(diff.costs)">{{ diff.costs }}%</td>
      </tr>
      <tr>
        <td>Income</td>
        <td v-for="metric of metrics">
          <input v-model="metric.income" type="number">
        </td>
        <td :class="getClass(diff.income)">{{ diff.income }}%</td>
      </tr>
      <tr>
        <td>cash</td>
        <td v-for="metric of metrics">
          <input v-model="metric.cash" type="number">
        </td>
        <td :class="getClass(diff.cash)">{{ diff.cash }}%</td>
      </tr>
      <tr>
        <td>Customers</td>
        <td v-for="metric of metrics">
          <input v-model="metric.customers" type="number">
        </td>
        <td :class="getClass(diff.customers)">{{ diff.customers }}%</td>
      </tr>
      <tr>
        <td>Projects sold</td>
        <td v-for="metric of metrics">
          <input v-model="metric.sold" type="number">
        </td>
        <td :class="getClass(diff.customers)">{{ diff.sold }}%</td>
      </tr>
    </tbody>   
 </table>
<input type="text" @keyup.enter="addMonth($event)" placeholder="Add month and press enter">
</div>

CSS

body {
  font-family: Helvetica Neue, Arial, sans-serif;
  font-size: 14px;
  color: #444;
}

input {
  padding: 10px;
}

table {
  border: 2px solid #42b983;
  border-radius: 3px;
  background-color: #fff;
}

th {
  background-color: #42b983;
  color: rgba(255,255,255,0.66);
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

td {
  padding: 0;
}

th, td {
  min-width: 120px;
  height: 40px;
  text-align: right;
}

td input {
  width: 100%;
  height: 100%;
  border: none;
  text-align: right;
  padding: 10px;
  box-sizing: border-box;
  font-size: 13px;
}

.green {
  color: green;
}

.red {
  color: red;
}
}
}

Babel + JSX

let metrics = ['Feb 2017', 'Mar 2017', 'Apr 2017', 'May 2017'].map(m => ({
  month: m,
  revenue: Math.round(20000 * Math.random()),
  costs: Math.round(20000 * Math.random()),
  income: Math.round(20000 * Math.random()),
  cash: Math.round(20000 * Math.random()),
  customers: Math.round(20000 * Math.random()),
  sold: Math.round(20000 * Math.random())
}))

new Vue({
	data: {
    metrics
  },
  computed: {
  	diff () {
    	const lastMetric = this.metrics[this.metrics.length - 1]
      const prevMetric = this.metrics[this.metrics.length - 2]
    	return Object.keys(lastMetric).reduce((acc, key) => {
      	return {
        	name: `${prevMetric.month}-${lastMetric.month}`,
        	...acc,
          [key]: Math.round((lastMetric[key] - prevMetric[key]) / prevMetric[key] * 100)
        }
      })
    }
  },
  methods: {
  	getClass (v) {
    	return v > 0 ? 'green' : 'red'
    },
    addMonth (e) {
    	this.metrics.push({
      	month: e.target.value,
        revenue: Math.round(20000 * Math.random()),
        costs: Math.round(20000 * Math.random()),
        income: Math.round(20000 * Math.random()),
        cash: Math.round(20000 * Math.random()),
        customers: Math.round(20000 * Math.random()),
        sold: Math.round(20000 * Math.random())
      })
    }
  }
}).$mount('#app')