refactor: chart Total Sampah per Bulan (BSI, BSU, Offtaker)
parent
62912c2a43
commit
9622ee1809
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using BankSampahApp.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BankSampahApp.Controllers.Main
|
||||
{
|
||||
|
|
@ -7,12 +8,69 @@ namespace BankSampahApp.Controllers.Main
|
|||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View("~/Views/Main/Dashboard/Index.cshtml");
|
||||
var model = BuildDashboardChartModel();
|
||||
return View("~/Views/Main/Dashboard/Index.cshtml", model);
|
||||
}
|
||||
|
||||
public IActionResult Bsu()
|
||||
{
|
||||
return View("~/Views/Main/Dashboard/Bsu.cshtml");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dummy chart data agar view dapat menampilkan grafik tanpa ketergantungan pada API.
|
||||
/// </summary>
|
||||
private static DashboardChartViewModel BuildDashboardChartModel()
|
||||
{
|
||||
var monthLabels = new List<string>
|
||||
{
|
||||
"Januari", "Februari", "Maret", "April", "Mei", "Juni",
|
||||
"Juli", "Agustus", "September", "Oktober", "November", "Desember"
|
||||
};
|
||||
|
||||
var baseDatasets = new Dictionary<string, List<int>>
|
||||
{
|
||||
["BSI"] = new() { 520, 540, 560, 590, 620, 650, 680, 710, 740, 770, 800, 830 },
|
||||
["BSU"] = new() { 610, 630, 640, 670, 690, 720, 760, 780, 810, 830, 860, 890 },
|
||||
["Offtaker"] = new() { 1050, 1080, 1100, 1140, 1180, 1220, 1260, 1300, 1340, 1380, 1420, 1480 }
|
||||
};
|
||||
|
||||
var years = Enumerable.Range(2020, 6).ToList();
|
||||
var firstYear = years.Min();
|
||||
var yearlyData = new Dictionary<int, DashboardChartPayload>();
|
||||
|
||||
foreach (var year in years)
|
||||
{
|
||||
var offset = year - firstYear;
|
||||
var payload = new DashboardChartPayload
|
||||
{
|
||||
Labels = new List<string>(monthLabels)
|
||||
};
|
||||
|
||||
foreach (var dataset in baseDatasets)
|
||||
{
|
||||
var random = new Random(HashCode.Combine(year, dataset.Key));
|
||||
payload.Datasets.Add(new DashboardChartDataset
|
||||
{
|
||||
Label = dataset.Key,
|
||||
Data = dataset.Value
|
||||
.Select(baseValue =>
|
||||
{
|
||||
var variation = random.Next(-100, 120) + offset * 30;
|
||||
return Math.Max(0, baseValue + variation);
|
||||
})
|
||||
.ToList()
|
||||
});
|
||||
}
|
||||
|
||||
yearlyData[year] = payload;
|
||||
}
|
||||
|
||||
return new DashboardChartViewModel
|
||||
{
|
||||
YearlyData = yearlyData,
|
||||
SelectedYear = yearlyData.Keys.Max()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BankSampahApp.Models;
|
||||
|
||||
/// <summary>
|
||||
/// View model yang menampung data grafik dashboard beserta opsi filter tahunan.
|
||||
/// </summary>
|
||||
public class DashboardChartViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Data grafik per tahun (key berupa tahun).
|
||||
/// </summary>
|
||||
public Dictionary<int, DashboardChartPayload> YearlyData { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Tahun yang dipilih saat halaman pertama kali dimuat.
|
||||
/// </summary>
|
||||
public int SelectedYear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Daftar tahun yang tersedia untuk filter, disortir menurun.
|
||||
/// </summary>
|
||||
public IEnumerable<int> AvailableYears => YearlyData.Keys.OrderByDescending(x => x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Struktur data payload untuk chart per tahun.
|
||||
/// </summary>
|
||||
public class DashboardChartPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Label (umumnya nama bulan) yang ditampilkan pada sumbu X.
|
||||
/// </summary>
|
||||
public List<string> Labels { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Dataset yang ditampilkan pada grafik.
|
||||
/// </summary>
|
||||
public List<DashboardChartDataset> Datasets { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dataset individual untuk Chart.js.
|
||||
/// </summary>
|
||||
public class DashboardChartDataset
|
||||
{
|
||||
public string Label { get; set; } = string.Empty;
|
||||
public List<int> Data { get; set; } = new();
|
||||
}
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
@{
|
||||
@model BankSampahApp.Models.DashboardChartViewModel
|
||||
@using System.Text.Json
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
var chartJson = JsonSerializer.Serialize(Model.YearlyData, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
});
|
||||
}
|
||||
|
||||
<div class="flex gap-2 flex-row justify-between md:gap-0 mt-2 mb-2">
|
||||
|
|
@ -223,19 +229,40 @@
|
|||
|
||||
<div class="h-6"></div>
|
||||
|
||||
<div class="card bg-white shadow-sm">
|
||||
<div class="card-body pt-4 px-3">
|
||||
<div class="flex flex-col gap-2 md:flex-row md:justify-between md:gap-0">
|
||||
<div class="prose">
|
||||
<div class="card bg-white">
|
||||
<div class="card-body">
|
||||
<div class="flex flex-col gap-3 md:flex-row md:items-center md:justify-between md:gap-4">
|
||||
<div class="prose max-w-2xl">
|
||||
<span class="text-xl font-semibold text-gray-900 font-['Plus_Jakarta_Sans']">
|
||||
Data Terverifikasi
|
||||
Grafik Total Sampah per Bulan (BSI, BSU, Offtaker)
|
||||
</span>
|
||||
<p class="mt-1 text-sm text-gray-500">
|
||||
Menampilkan total tonase sampah yang dikelola setiap bulan berdasarkan sumber BSI, BSU, dan Offtaker.
|
||||
</p>
|
||||
</div>
|
||||
<fieldset class="fieldset w-full lg:w-48">
|
||||
<label class="fieldset-label text-gray-500 text-xs uppercase tracking-wide" for="chartYear">
|
||||
Filter Tahun
|
||||
</label>
|
||||
<select id="chartYear" class="select select-sm" onchange="handleYearChange(event)">
|
||||
@foreach (var year in Model.AvailableYears)
|
||||
{
|
||||
if (year == Model.SelectedYear)
|
||||
{
|
||||
<option value="@year" selected>@year</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@year">@year</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="w-full overflow-x-auto">
|
||||
<div class="min-w-[900px]">
|
||||
<div class="h-64 md:h-96 relative">
|
||||
<canvas id="chartBankSampah" class="w-full h-full"></canvas>
|
||||
<canvas id="chartBankSampah" class="w-full h-full"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -243,4 +270,106 @@
|
|||
</div>
|
||||
|
||||
<script src="~/js/chart.js"></script>
|
||||
<script src="/plugins/chartjs/setup.js"></script>
|
||||
<script>
|
||||
const chartDataByYear = @Html.Raw(chartJson);
|
||||
const chartCanvas = document.getElementById('chartBankSampah');
|
||||
const chartCtx = chartCanvas.getContext('2d');
|
||||
let chartInstance = null;
|
||||
|
||||
const datasetStyles = {
|
||||
BSI: {
|
||||
borderColor: '#247332',
|
||||
gradientStart: 'rgba(36, 115, 50, 1)',
|
||||
gradientEnd: 'rgba(136, 194, 147, 0.25)'
|
||||
},
|
||||
BSU: {
|
||||
borderColor: '#386BCA',
|
||||
gradientStart: 'rgba(56, 107, 202, 1)',
|
||||
gradientEnd: 'rgba(138, 164, 242, 0.25)'
|
||||
},
|
||||
Offtaker: {
|
||||
borderColor: '#F79009',
|
||||
gradientStart: 'rgba(247, 144, 9, 1)',
|
||||
gradientEnd: 'rgba(255, 219, 172, 0.25)'
|
||||
}
|
||||
};
|
||||
|
||||
const defaultDatasetStyle = {
|
||||
borderColor: '#0F172A',
|
||||
gradientStart: 'rgba(15, 23, 42, 0.85)',
|
||||
gradientEnd: 'rgba(15, 23, 42, 0.15)'
|
||||
};
|
||||
|
||||
const buildDatasets = (payload) => payload.datasets.map((dataset) => {
|
||||
const style = datasetStyles[dataset.label] ?? defaultDatasetStyle;
|
||||
const gradient = chartCtx.createLinearGradient(0, 0, 0, chartCanvas.clientHeight || 400);
|
||||
gradient.addColorStop(0, style.gradientStart);
|
||||
gradient.addColorStop(1, style.gradientEnd);
|
||||
|
||||
return {
|
||||
label: dataset.label,
|
||||
data: dataset.data,
|
||||
borderColor: style.borderColor,
|
||||
backgroundColor: gradient,
|
||||
borderWidth: 2,
|
||||
};
|
||||
});
|
||||
|
||||
const renderChart = (year) => {
|
||||
const payload = chartDataByYear[year];
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedData = {
|
||||
labels: payload.labels,
|
||||
datasets: buildDatasets(payload)
|
||||
};
|
||||
|
||||
if (chartInstance) {
|
||||
chartInstance.data = updatedData;
|
||||
chartInstance.update();
|
||||
return;
|
||||
}
|
||||
|
||||
chartInstance = new Chart(chartCtx, {
|
||||
type: 'bar',
|
||||
data: updatedData,
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (context) => `${context.dataset.label}: ${context.formattedValue} Kg`
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: (value) => `${value} Kg`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.handleYearChange = (event) => {
|
||||
renderChart(event.target.value);
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
renderChart(@Model.SelectedYear);
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue