Add BarChart Vue component
MODIFY
app/Http/Controllers/DashboardController.php
app/Http/Controllers/DashboardController.php
@@ -60,6 +60,16 @@ class DashboardController extends Controller {
->where('month', $month)
->get();
+ $spendingsByTags = [];
+
+ foreach ($spendings as $spending) {
+ if (!isset($spendingsByTag[$spending->tag->name])) {
+ $spendingsByTags[$spending->tag->name] = 0;
+ }
+
+ $spendingsByTags[$spending->tag->name] += $spending->amount;
+ }
+
return view('dashboard.index', compact(
'year',
'month',
@@ -73,7 +83,8 @@ class DashboardController extends Controller {
'balance',
'earnings',
'spendings',
- 'budgets'
+ 'budgets',
+ 'spendingsByTags'
));
}
}

MODIFY
package.json
package.json
"cross-env": "^5.0.1",
"laravel-mix": "^1.0",
"lodash": "^4.17.4",
- "vue": "^2.5.3"
+ "vue": "^2.5.3",
+ "axios": "^0.17.0"
}
}

MODIFY
resources/assets/js/app.js
resources/assets/js/app.js
window.Vue = require('vue')
Vue.component('datepicker', require('./components/DatePicker.vue'))
+Vue.component('barchart', require('./components/BarChart.vue'))
const app = new Vue({
el: '#app'

ADD
resources/assets/js/components/BarChart.vue
+<template>
+ <canvas ref="canvas"></canvas>
+</template>
+
+<script>
+ export default {
+ data() {
+ return {
+ colors: ['#2F323A', '#5E9CF2', '#FED52F', '#9CD926'],
+ textColor: '#758193',
+ barHeight: 8,
+ barTagsOffset: 16,
+ tagsDiameter: 16,
+ font: 'Muli'
+ }
+ },
+
+ props: ['slices'],
+
+ methods: {
+ drawRectangle(context, x, y, width, height, color = null) {
+ if (color) {
+ context.fillStyle = color
+ }
+
+ context.fillRect(x, y, width, height)
+ },
+
+ drawCircle(context, x, y, radius, color = null) {
+ if (color) {
+ context.fillStyle = color
+ }
+
+ context.beginPath()
+ context.arc(x, y, radius, 0, 2 * Math.PI)
+ context.fill()
+ },
+
+ drawText(context, x, y, text, color = null) {
+ context.font = '16px ' + this.font
+ context.textBaseline = 'middle'
+
+ if (color) {
+ context.fillStyle = color
+ }
+
+ context.fillText(text, x, y)
+ },
+
+ render() {
+ const slices = JSON.parse(this.slices)
+
+ const canvas = this.$refs.canvas
+ const context = canvas.getContext('2d')
+
+ canvas.width = canvas.offsetWidth
+ canvas.height = this.barHeight + this.barTagsOffset + this.tagsDiameter
+
+ let total = 0
+
+ for (const key in slices) {
+ total += slices[key]
+ }
+
+ let barX = 0
+ let tagX = this.tagsDiameter / 2
+ let index = 0
+
+ const last = Object.keys(slices).length - 1
+
+ for (const key in slices) {
+ const slice = slices[key]
+
+ const color = this.colors[index]
+ const barWidth = (slice / total) * canvas.width
+
+ // Bar
+ if (index === 0) {
+ this.drawCircle(context, barX + this.barHeight / 2, this.barHeight / 2, this.barHeight / 2, color)
+ this.drawRectangle(context, barX + this.barHeight / 2, 0, barWidth - this.barHeight / 2, this.barHeight, color)
+ } else if (index === last) {
+ this.drawCircle(context, barX + barWidth - this.barHeight / 2, this.barHeight / 2, this.barHeight / 2, color)
+ this.drawRectangle(context, barX, 0, barWidth - this.barHeight / 2, this.barHeight, color)
+ } else {
+ this.drawRectangle(context, barX, 0, barWidth, this.barHeight, color)
+ }
+
+ // Tags
+ this.drawCircle(context, tagX, this.barHeight + this.barTagsOffset + this.tagsDiameter / 2, this.tagsDiameter / 2, color)
+ this.drawText(context, tagX + 16, this.barHeight + this.barTagsOffset + this.tagsDiameter / 2, key)
+
+ barX += barWidth
+ tagX += 16 + context.measureText(key).width + 32
+ index ++
+ }
+ },
+ },
+
+ mounted() {
+ this.render()
+
+ window.addEventListener('resize', this.render)
+ }
+ }
+</script>

MODIFY
resources/assets/sass/app.scss
resources/assets/sass/app.scss
@@ -18,3 +18,7 @@ body {
width: 90%;
max-width: 1280px;
}
+
+canvas {
+ width: 100%;
+}

MODIFY
resources/views/dashboard/index.blade.php
resources/views/dashboard/index.blade.php
</div>
</div>
@if ($spendings->count())
+ <div class="section">
+ <BarChart slices='{!! json_encode($spendingsByTags) !!}'></BarChart>
+ </div>
<ul class="section">
@foreach ($spendings as $spending)
<li>

MODIFY
yarn.lock
yarn.lock
@@ -272,6 +272,13 @@ aws4@^1.2.1, aws4@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
+axios@^0.17.0:
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.0.tgz#7da747916db803f761651d6091d708789b953c6a"
+ dependencies:
+ follow-redirects "^1.2.3"
+ is-buffer "^1.1.5"
+
babel-code-frame@^6.11.0, babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -1592,7 +1599,7 @@ de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
-debug@2.6.9, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8:
+debug@2.6.9, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
@@ -2285,6 +2292,12 @@ flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
+follow-redirects@^1.2.3:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.5.tgz#ffd3e14cbdd5eaa72f61b6368c1f68516c2a26cc"
+ dependencies:
+ debug "^2.6.9"
+
for-in@^0.1.3:
version "0.1.8"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"

Add BarChart Vue component