Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #14: Chart & Laporan

Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #14: Chart & Laporan

Pendahuluan

Evaluasi usaha menjadi bagian penting untuk menentukan strategi lanjutan agar transaksi selalu bertambah dari waktu ke waktu. Pada seri Membuat Aplikasi Laundry Laravel 5.8 & Vue.js, dimana kita telah memasuki bagian ke-14 yang membahas Chart & Laporan. Dengan memanfaatkan library ChartJS yang telah dibuat kembali dengan versi Vue.js, maka kita akan men-generate laporan transaksi selama sebulan trakhir serta menampilkan rekap pendapatan hariannya.

Tidak hanya sebulan trakhir, akan tetapi akan disediakan fitur filter berdasarkan bulan dan tahun sehingga pengguna dapat melihat informasi berdasarkan waktu yang diinginkan. Mengunduh informasi ke local komputer juga tersedia dalam bentuk file Excel yang dapat digunakan sebagai salinan laporan untuk pemilik usaha.

Artikel ini juga sekaligus sebagai penutup karena ada banyak permintaan untuk membahas Laravel 6, sehingga kita akan menggunakan case baru untuk membahas versi Laravel terbaru ini.

Baca Juga: Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #13: List Transaksi

Chart Transaksi Harian

Sebelum proses development dimulai, langkah pertama adalah dengan meng-install library yang diperlukan. Pada command line, jalankan perintah:

npm install vue-chartjs chart.js --save

Kemudian buat component LineChart.vue di dalam folder resources/js/components dan tambahkan code berikut

<script>
    import { Line } from 'vue-chartjs'

    export default {
        extends: Line,
        props: ['data', 'options', 'labels'], //KETIKA COMPONENT INI DIGUNAKAN, AKAN MEMINTA DATA SEBAGAI PROPS
        mounted() {
            this.lineRenderChart() //KETIKA COMPONENT DI-LOAD JALANKAN METHOD INI
        },
        watch: {
            //KETIKA TERJADI PERUBAHAN VALUE DARI PROPS DATA
            data: {
                handler() {
                    this._data._chart.destroy() //MAKA HAPUS CHART
                    this.lineRenderChart() //DAN RENDER KEMBALI DENGAN DATA YANG BARU
                },
                deep: true
            }
        },
        methods: {
            lineRenderChart() {
                //FUNGSI UNTUK MERENDER CHART
                this.renderChart({
                    labels: this.labels, //LABELS NYA BERDASARKAN PROPS LABELS (VALUENYA AKAN KITA ISI LIST TANGGAL SELAMA 1 BULAN)
                    datasets: [{
                        label: 'Data Transaksi',
                        data: this.data, //DATA YANG AKAN MENJADI CHART (TOTAL TRANSAKSI PERHARI)
                        backgroundColor: [
                            'rgba(255, 99, 132, 0.2)',
                            'rgba(54, 162, 235, 0.2)',
                            'rgba(255, 206, 86, 0.2)',
                        ],
                        borderColor: [
                            'rgba(255, 99, 132, 1)',
                            'rgba(54, 162, 235, 1)',
                            'rgba(255, 206, 86, 1)',
                        ],
                        borderWidth: 1
                    }]
                }, this.options)
            }
        }
    }
</script>

Saatnya memodifikasi halaman Home/Dashboard, buka file Home.vue yang berada di dalam folder js/pages dan modifikasi menjadi

<template>
    <div class="container">
        <section class="content-header">
            <h1>
                Dashboard
            </h1>
            <ol class="breadcrumb">
                <li><router-link :to="'/'"><i class="fa fa-dashboard"></i> Home</router-link></li>
                <li><a href="#">Dashboard</a></li>
            </ol>
        </section>

        <section class="content">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel">
                        <div class="panel-heading">
                            <div class="row">
                              
                              	<!-- FORM FILTER BERDASARKAN BULAN DAN TAHUN -->
                                <div class="col-md-5">
                                    <div class="form-group">
                                        <label for="">Bulan</label>
                                        <select v-model="month" class="form-control">
                                            <option value="01">Januari</option>
                                            <option value="02">Februari</option>
                                            <option value="03">Maret</option>
                                            <option value="04">April</option>
                                            <option value="05">Mei</option>
                                            <option value="06">Juni</option>
                                            <option value="07">Juli</option>
                                            <option value="08">Agustus</option>
                                            <option value="09">September</option>
                                            <option value="10">Oktober</option>
                                            <option value="11">November</option>
                                            <option value="12">Desember</option>
                                        </select>
                                    </div>
                                </div>
                                <div class="col-md-5">
                                    <div class="form-group">
                                        <label for="">Tahun</label>
                                        <select v-model="year" class="form-control">
                                            <option v-for="(y, i) in years" :key="i" :value="y">{{ y }}</option>
                                        </select>
                                    </div>
                                </div>
                                <!-- FORM FILTER BERDASARKAN BULAN DAN TAHUN -->
                              
                                <!-- TOMBOL UNTUK EXPORT DATA KE EXCEL -->
                                <div class="col-md-2">
                                    <button class="btn btn-primary btn-sm pull-right" @click="exportData">Export</button>
                                </div>
                                <!-- TOMBOL UNTUK EXPORT DATA KE EXCEL -->
                            </div>
                        </div>
                        <div class="panel-body">
                            <!-- TAMPILKAN CHART DARI COMPONENT YANG SEBELUMNYA DIBUAT -->
                            <!-- DENGAN MENGIRIMKAN DATA, OPTIONS DAN LABELS SEBAGAI PROPS -->
                            <line-chart v-if="transactions.length > 0" :data="transaction_data" :options="chartOptions" :labels="labels"/>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </div>
</template>
<script>
    import moment from 'moment'
    import _ from 'lodash'
    import LineChart from '../components/LineChart.vue' //IMPORT COMPONENT CHART 
    import { mapActions, mapState } from 'vuex'
  
    export default {
        created() {
            //KETIKA HALAMAN INI DI-LOAD MAKA AKAN MEMINTA DATA KE SERVER
            //DAN MENGIRIMKAN PARAMETER BULAN DAN TAHUN YANG AKTIF
            this.getChartData({
                month: this.month,
                year: this.year
            })
        },
        data() {
            return {
                chartOptions: {
                    responsive: true,
                    maintainAspectRatio: false
                },
                month: moment().format('MM'), //DEFAULT BULAN YG AKTIF BERDASARKAN BULAN SAAT INI
                year: moment().format('Y') // //DEFAULT TAHUN YG AKTIF BERDASARKAN TAHUN SAAT INI
            }
        },
        watch: {
            //KETIKA VALUE BULAN BERUBAH, MAKA KITA REQUEST DATA BARU
            month() {
                this.getChartData({
                    month: this.month,
                    year: this.year
                })
            },
            //KETIKA VALUE TAHUN BERUBAH, MAKA KITA REQUEST DATA BARU
            year() {
                this.getChartData({
                    month: this.month,
                    year: this.year
                })
            }
        },
        computed: {
            ...mapState('dashboard', {
                transactions: state => state.transactions //AMBIL DATA DARI STATE
            }),
            //LIST TAHUN DARI 2010 SAMPAI TAHUN SAAT INI UNTUK DILOOPING DI FILTER TAG
            years() {
                return _.range(2010, moment().add(1, 'years').format('Y'))
            },
            //DATA LABELS YANG DITERIMA DARI SERVER
            labels() {
                //KARENA FORMAT DATANYA BERISI TOTAL DAN DATE, MAKA KITA FILTER HANYA AKAN MENGAMBIL DATENYA SAJA
                return _.map(this.transactions, function(o) {
                    return moment(o.date).format('DD')
                });
            },
            //DATA TOTAL TRANSAKSI YANG DITERIMA DARI SERVER
            transaction_data() {
                //KITA FILTER KARENA HANYA AKAN MENGAMBIL TOTAL VALUENYA SAJA
                return _.map(this.transactions, function(o) {
                    return o.total
                });
            }
        },
        methods: {
            ...mapActions('dashboard', ['getChartData']),
            //FUNGSI EXPORT DATA INI AKAN KITA KERJAKAN KEMUDIAN
            exportData() {
                
            }
        },
        components: { 'line-chart': LineChart }, //DEFINISIKAN CUSTOM TAG UNTUK COMPONENT YANG DIBUAT SEBELUMNYA
    }
</script>

Membuat module Vuex untuk meng-handle request dan menyimpan state data yang diterima dari server, buat file dashboard.js dan tempatkan di dalam folder /js/stores

import $axios from '../api.js'

const state = () => ({
    transactions: [], //STATE UNTUK MENYIMPAN DATA TRANSAKSI
})

const mutations = {
    //MUTATION UNTUK MEMANIPULASI DATA STATE TRANSAKSI
    ASSIGN_DATA_TRANSACTION(state, payload) {
        state.transactions = payload
    }
}

const actions = {
    //ACTION UNTUK MENG-HANDLE REQUEST KE SERVER
    getChartData({ commit }, payload) {
        return new Promise((resolve, reject) => {
            //MINTA DATA CHART TRANSAKSI KE SERVER BERDASARKAN BULAN DAN TAHUN
            $axios.get(`/chart?month=${payload.month}&year=${payload.year}`)
            .then((response) => {
                //KEMUDIAN KIRIM DATA NYA KE MUTATION UNTUK KEMUDIAN DISIMPAN KE STATE
                commit('ASSIGN_DATA_TRANSACTION', response.data)
                resolve(response.data)
            })
        })
    },
}

export default {
    namespaced: true,
    state,
    actions,
    mutations
}

Agar module diatas dapat digunakan maka kita perlu me-register nya terlebih dahulu, buka file /js/store.js dan tambahkan beberapa bagian code berikut

modules: {
    auth,
    outlet,
    courier,
    product,
    user,
    expenses,
    notification,
    customer,
    transaction,
    dashboard //TAMBAHKAN BAGIAN INI
},

Masih dengan file yang sama, tambahkan code berikut pada import statement:

import dashboard from './stores/dashboard.js'

Langkah terakhir adalah dengan menyediakan API untuk meng-handle request data transaksi yang akan ditampilkan sebagai chart, pada command line, buat controller baru dengan command:

php artisan make:controller API/DashboardController

Kemudian buka file DashboardController.php dan tambahkan code berikut:

<?php

namespace App\Http\Controllers\API;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Carbon\Carbon;
use App\Transaction;
use DB;

class DashboardController extends Controller
{
    public function chart()
    {
        $filter = request()->year . '-' . request()->month; //GET DATA BULAN & TAHUN YANG DIKIRIMKAN SEBAGAI PARAMETER
      
        $parse = Carbon::parse($filter); //UBAH FORMATNYA MENJADI FORMAT CARBON
        //BUAT RANGE TANGGAL PADA BULAN TERKAIT
        $array_date = range($parse->startOfMonth()->format('d'), $parse->endOfMonth()->format('d'));
      
        //GET DATA TRANSAKSI BERDASARKAN BULAN & TANGGAL YANG DIMINTA.
        //GROUP / KELOMPOKKAN BERDASARKAN TANGGALNYA
        //SUM DATA AMOUNT DAN SIMPAN KE NAMA BARU YAKNI TOTAL
        $transaction = Transaction::select(DB::raw('date(created_at) as date,sum(amount) as total'))
            ->where('created_at', 'LIKE', '%' . $filter . '%')
            ->groupBy(DB::raw('date(created_at)'))->get();
        
        $data = [];
        //LOOPING RANGE TANGGAL BULAN SAAT INI
        foreach ($array_date as $row) {
            //KITA CEK TANGGALNYA, JIKA HANYA 1 ANGKA (1-9) MAKA TAMBAHKAN 0 DIDEPANNYA
            $f_date = strlen($row) == 1 ? 0 . $row:$row;
            //BUAT FORMAT TANGGAL YYYY-MM-DD
            $date = $filter . '-' . $f_date;
            //CARI DATA BERDASARKAN $DATE PADA COLLECTION HASIL QUERY
            $total = $transaction->firstWhere('date', $date);

            //SIMPAN DATANYA KE DALAM VARIABLE $DATA
            $data[] = [
                'date' =>$date,
                'total' => $total ? $total->total:0
            ];
        }
        return $data;
    }
}

Buat routing untuk meng-handle controller diatas, buka file routes/api.php dan tambahkan code:

Route::get('chart', 'API\DashboardController@chart');

Export Laporan Harian

Laporan transaksinya serupa dengan chart hanya saja kita masukkan ke dalam file excel yang secara otomatis akan disimpan ke dalam komputer pengguna. Install package Laravel Excel dengan command

composer require maatwebsite/excel

Kemudian generate class TranscationExport dengan command

php artisan make:export TransactionExport --model=Transaction

Buka file app/Exports/TransactionExport.php dan modifikasi menjadi

<?php

namespace App\Exports;

use App\Transaction;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;

class TransactionExport implements FromView, ShouldAutoSize
{
    protected $transaction;
    protected $month;
    protected $year;
  
    //KITA MEMINTA DATA TRANSAKSI, DATA BULAN DAN TAHUN YANG DI REQUEST
    public function __construct($transaction, $month, $year)
    {
        $this->transaction = $transaction;
        $this->month = $month;
        $this->year = $year;
    }

    public function view(): View
    {
        //LOAD VIEW transaction.blade.php DAN PASSING DATA YANG DIMINTA DIATAS
        return view('exports.transaction', [
            'transaction' => $this->transaction,
            'month' => $this->month,
            'year' => $this->year
        ]);
    }
}

Buat file transactions.blade.php di dalam folder resources/views/exports dan tambahkan code html dibawah ini

<table>
    <thead>
        <tr>
            <th colspan="2"><strong>Laporan Transaksi {{ $month }} - {{ $year }}</strong></th>
        </tr>
        <tr>
            <th colspan="2"></th>
        </tr>
        <tr>
            <th>Tanggal</th>
            <th>Pemasukan</th>
        </tr>
    </thead>
    <tbody>
        @foreach($transaction as $row)
            <tr>
                <td>{{ $row['date'] }}</td>
                <td>Rp {{ number_format($row['total']) }}</td>
            </tr>
        @endforeach
    </tbody>
</table>

Note: Code diatas hanya HTML dimana terdapat tag table dan didalamnya ada looping untuk menampilkan data transaksi.

Buka file DashboardController.php dan tambahkan method berikut untuk meng-handle request export laporan

public function exportData(Request $request)
{
    $transaction = $this->chart(); //KARENA QUERYNYA SAMA, MAKA KITA AMBIL DATA DARI METHOD CHART() SAJA SEHINGGA KITA TIDAK PERLU MEMBUAT CODE YANG SAMA
    //REQUEST UNTUK MENDOWNLOAD FILE EXCEL
    //MENGGUNAKAN TRANSACTIONEXPORT() DAN MENGIRIMKAN 3 BUAH DATA
    //DATA TRANSAKSI, DATA BULAN DAN TAHUN YANG DIMINTA
    //DAN NAMA FILE YANG DIHASILKAN ADALAH transaction.xlsx
    return Excel::download(new TransactionExport($transaction, request()->month, request()->year), 'transaction.xlsx');
}

Jangan lupa untuk mendefinisikan routing-nya, buka file routes/api.php dan tambahkan route berikut

Route::get('export', 'API\DashboardController@exportData');

Sentuhan terakhir adalah dengan modifikasi method exportData(), buka file /pages/Home.vue dan modifikasi method tersebut

exportData() {
    window.open(`/api/export?api_token=${this.token}&month=${this.month}&year=${this.year}`)
}

Baca Juga: Aplikasi Qur'an Digital & Play Audio Menggunakan Flutter

Kesimpulan

Sebagai materi penutup maka kita belajar banyak hal diantaranya bagaimana menggunakan library ChartJS untuk menampilkan data transaksi dengan mode chart, selain itu juga terdapat fitur export data ke excel sehingga cara menggunakan package Laravel Excel juga sudah dibahas.

Tentu saja di dalam materi ini masih banyak kekurangan yang perlu dilengkapi, tapi saya merasa dengan semua teknik dan method yang digunakan maka teman-teman sudah bisa dengan mandiri menambahkan fitur lainnya yang terlewatkan.

Adapun dokumentasi code dari artikel ini bisa kamu lihat di Github.

Category:
Share:

Comments