wangonya/auto-repair-saas

View on GitHub
auto_repair_saas/templates/dashboard/index.html

Summary

Maintainability
Test Coverage
{% extends "base.html" %}
{% block content %}
    <div class="row mb-4">
        <div class="col">
            <h4>Dashboard</h4>
        </div>
        <div class="col">
            <div class="btn-group shadow-0 float-end" role="group">
                <button type="button" class="btn btn-primary" id="data-week"
                        onclick="fetchDashboardData('week')">
                    <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"
                          id="data-week-loading" hidden></span>
                    Week
                </button>
                <button type="button" class="btn btn-primary" id="data-month"
                        onclick="fetchDashboardData('month')">
                    <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"
                          id="data-month-loading" hidden></span>
                    Month
                </button>
                <button type="button" class="btn btn-primary" id="data-year"
                        onclick="fetchDashboardData('year')">
                    <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"
                          id="data-year-loading" hidden></span>
                    Year
                </button>
            </div>
        </div>
    </div>
    <div class="row mb-4">
        <div class="col-12">
            <div class="card">
                <div class="card-body">
                    <p class="card-title text-muted text-uppercase">sales</p>
                    <div class="row">
                        <span class="card-text fs-3" id="total-sales"></span>
                        <small class="text-success">
                            <i id="total-sales-growth-icon"></i>
                            <span id="total-sales-growth"></span>
                        </small>
                    </div>
                    <div class="row">
                        <div class="col-12">
                            <canvas id="sales-chart" height="80"></canvas>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="row mb-3">
        <div class="col-6">
            <div class="card">
                <div class="card-body">
                    <p class="card-title text-muted text-uppercase">Top earners</p>
                    <div class="row">
                        <div class="col-12">
                            <canvas id="top-earners-chart" height="110"></canvas>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-6">
            <div class="card">
                <div class="card-body">
                    <p class="card-title text-muted text-uppercase">jobs overview</p>
                    <table class="table table-sm">
                        <tbody>
                        <tr>
                            <th scope="row">Pending Estimates</th>
                            <td id="pending-estimates-count"></td>
                            <td id="pending-estimates-charged"></td>
                        </tr>
                        <tr>
                            <th scope="row">Confirmed Estimates</th>
                            <td id="confirmed-estimates-count"></td>
                            <td id="confirmed-estimates-charged"></td>
                        </tr>
                        <tr>
                            <th scope="row">In Progress</th>
                            <td id="in-progress-jobs-count"></td>
                            <td id="in-progress-jobs-charged"></td>
                        </tr>
                        <tr>
                            <th scope="row">Done</th>
                            <td id="done-jobs-count"></td>
                            <td id="done-jobs-charged"></td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <script>
        let salesChart, topEarnersChart
        const fetchDashboardData = (period) => {
            let active = document.querySelectorAll(".custom-active-blue");
            [].forEach.call(active, function (el) {
                el.classList.remove("custom-active-blue")
            })
            if (period === 'month') {
                document.getElementById('data-month').classList.add('custom-active-blue')
                document.getElementById('data-month').setAttribute('disabled', 'true')
                document.getElementById('data-month-loading').removeAttribute('hidden')
            } else if (period === 'year') {
                document.getElementById('data-year').classList.add('custom-active-blue')
                document.getElementById('data-year').setAttribute('disabled', 'true')
                document.getElementById('data-year-loading').removeAttribute('hidden')
            } else {
                document.getElementById('data-week').classList.add('custom-active-blue')
                document.getElementById('data-week').setAttribute('disabled', 'true')
                document.getElementById('data-week-loading').removeAttribute('hidden')
            }
            fetch(`/dashboard/data?period=${period}`)
                .then(response => response.json())
                .then(data => {
                    document.getElementById('total-sales').textContent = `KSh. ${data.sales.toLocaleString()}`
                    populateSalesChart(data)
                    populateTopEarnersChart(data.top_earners)
                    document.getElementById('pending-estimates-count').textContent = `${data.pending_estimates_count} Orders`
                    document.getElementById('pending-estimates-charged').textContent = `KSh. ${data.pending_estimates_charged}`
                    document.getElementById('confirmed-estimates-count').textContent = `${data.confirmed_estimates_count} Orders`
                    document.getElementById('confirmed-estimates-charged').textContent = `KSh. ${data.confirmed_estimates_charged}`
                    document.getElementById('in-progress-jobs-count').textContent = `${data.jobs_in_progress_count} Orders`
                    document.getElementById('in-progress-jobs-charged').textContent = `KSh. ${data.jobs_in_progress_charged}`
                    document.getElementById('done-jobs-count').textContent = `${data.jobs_done_count} Orders`
                    document.getElementById('done-jobs-charged').textContent = `KSh. ${data.jobs_done_charged}`
                    clearLoaders()
                })
                .catch((error) => {
                    iziToast.error({
                        title: 'Error',
                        message: error,
                        maxWidth: 310
                    })
                })
        }

        const populateSalesChart = salesData => {
            salesChart && salesChart.destroy()
            let chartOptions = {
                scales: {
                    yAxes: [{
                        ticks: {
                            beginAtZero: true
                        }
                    }]
                }
            }
            const salesCard = document.getElementById('sales-chart')
            salesChart = new Chart(salesCard, {
                type: 'bar',
                data: {
                    datasets: [
                        {
                            label: 'Sales',
                            data: salesData.overall_job_charges,
                            backgroundColor: 'rgba(54, 162, 235, 0.2)',
                            borderColor: 'rgba(54, 162, 235, 1)',
                        },
                        {
                            label: 'Cash',
                            data: salesData.cash_job_charges,
                            backgroundColor: 'rgba(255, 99, 132, 0.2)',
                            borderColor: 'rgba(255, 99, 132, 1)',
                            type: 'line',
                            fill: false,
                        },
                        {
                            label: 'Card',
                            data: salesData.card_job_charges,
                            backgroundColor: 'rgba(153, 102, 255, 0.2)',
                            borderColor: 'rgba(153, 102, 255, 1)',
                            type: 'line',
                            fill: false,
                        },
                        {
                            label: 'M-Pesa',
                            data: salesData.mpesa_job_charges,
                            backgroundColor: 'rgba(75, 192, 192, 0.2)',
                            borderColor: 'rgba(75, 192, 192, 1)',
                            type: 'line',
                            fill: false,
                        },
                    ],
                    labels: salesData.chart_dates
                },
                options: chartOptions
            })
        }

        const populateTopEarnersChart = topEarnersData => {
            topEarnersChart && topEarnersChart.destroy()
            let chartOptions = {
                scales: {
                    yAxes: [{
                        ticks: {
                            beginAtZero: true
                        }
                    }]
                }
            }
            const topEarnersCard = document.getElementById('top-earners-chart')
            topEarnersChart = new Chart(topEarnersCard, {
                type: 'horizontalBar',
                data: {
                    datasets: [
                        {
                            label: 'Sales',
                            data: topEarnersData.map(staff => staff.earnings),
                            backgroundColor: 'rgba(54, 162, 235, 0.2)',
                            borderColor: 'rgba(54, 162, 235, 1)',
                        },
                    ],
                    labels: topEarnersData.map(staff => staff.name)
                },
                options: chartOptions
            })
        }

        const clearLoaders = () => {
            let loaders = document.getElementsByClassName('spinner-border');
            [].forEach.call(loaders, function (el) {
                el.setAttribute('hidden', 'true')
            })
            let buttons = document.getElementsByClassName('btn');
            [].forEach.call(buttons, function (el) {
                el.removeAttribute('disabled')
            })
        }

        fetchDashboardData(period = 'week')
    </script>
{% endblock %}