feat: chart Total Data Terverifikasi & Aktif Transaksi
parent
9622ee1809
commit
d55bca99b9
|
|
@ -35,9 +35,27 @@ namespace BankSampahApp.Controllers.Main
|
|||
["Offtaker"] = new() { 1050, 1080, 1100, 1140, 1180, 1220, 1260, 1300, 1340, 1380, 1420, 1480 }
|
||||
};
|
||||
|
||||
var entities = new List<string> { "BSI", "BSU", "Offtaker" };
|
||||
var verifiedActiveBase = new Dictionary<string, (List<int> Verified, List<int> Active)>
|
||||
{
|
||||
["BSI"] = (
|
||||
new List<int> { 400, 420, 460, 500, 520, 560, 580, 600, 640, 660, 700, 720 },
|
||||
new List<int> { 300, 320, 350, 370, 390, 420, 440, 460, 500, 520, 550, 580 }
|
||||
),
|
||||
["BSU"] = (
|
||||
new List<int> { 380, 400, 420, 450, 470, 500, 520, 550, 580, 600, 640, 670 },
|
||||
new List<int> { 250, 270, 300, 330, 350, 380, 400, 420, 450, 480, 500, 530 }
|
||||
),
|
||||
["Offtaker"] = (
|
||||
new List<int> { 600, 620, 640, 660, 700, 720, 750, 780, 820, 850, 880, 910 },
|
||||
new List<int> { 420, 440, 460, 500, 520, 540, 580, 600, 640, 670, 700, 740 }
|
||||
)
|
||||
};
|
||||
|
||||
var years = Enumerable.Range(2020, 6).ToList();
|
||||
var firstYear = years.Min();
|
||||
var yearlyData = new Dictionary<int, DashboardChartPayload>();
|
||||
var verifiedActiveYearlyData = new Dictionary<int, Dictionary<string, VerifiedActiveChartPayload>>();
|
||||
|
||||
foreach (var year in years)
|
||||
{
|
||||
|
|
@ -64,12 +82,40 @@ namespace BankSampahApp.Controllers.Main
|
|||
}
|
||||
|
||||
yearlyData[year] = payload;
|
||||
|
||||
var entityPayloads = new Dictionary<string, VerifiedActiveChartPayload>();
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var basePair = verifiedActiveBase[entity];
|
||||
var verifiedRandom = new Random(HashCode.Combine(year, entity, "verified"));
|
||||
var activeRandom = new Random(HashCode.Combine(year, entity, "active"));
|
||||
|
||||
var verifiedData = basePair.Verified
|
||||
.Select(baseValue => Math.Max(0, baseValue + verifiedRandom.Next(-60, 90) + offset * 25))
|
||||
.ToList();
|
||||
|
||||
var activeData = basePair.Active
|
||||
.Select(baseValue => Math.Max(0, baseValue + activeRandom.Next(-40, 75) + offset * 20))
|
||||
.ToList();
|
||||
|
||||
entityPayloads[entity] = new VerifiedActiveChartPayload
|
||||
{
|
||||
Labels = new List<string>(monthLabels),
|
||||
VerifiedData = verifiedData,
|
||||
ActiveData = activeData
|
||||
};
|
||||
}
|
||||
|
||||
verifiedActiveYearlyData[year] = entityPayloads;
|
||||
}
|
||||
|
||||
return new DashboardChartViewModel
|
||||
{
|
||||
YearlyData = yearlyData,
|
||||
SelectedYear = yearlyData.Keys.Max()
|
||||
SelectedYear = yearlyData.Keys.Max(),
|
||||
VerifiedActiveYearlyData = verifiedActiveYearlyData,
|
||||
Entities = entities,
|
||||
SelectedEntity = entities.FirstOrDefault() ?? string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,21 @@ public class DashboardChartViewModel
|
|||
/// Daftar tahun yang tersedia untuk filter, disortir menurun.
|
||||
/// </summary>
|
||||
public IEnumerable<int> AvailableYears => YearlyData.Keys.OrderByDescending(x => x);
|
||||
|
||||
/// <summary>
|
||||
/// Data grafik verifikasi vs aktif transaksi per tahun dan entitas.
|
||||
/// </summary>
|
||||
public Dictionary<int, Dictionary<string, VerifiedActiveChartPayload>> VerifiedActiveYearlyData { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Daftar entitas yang dapat difilter.
|
||||
/// </summary>
|
||||
public List<string> Entities { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Entitas yang dipilih secara default.
|
||||
/// </summary>
|
||||
public string SelectedEntity { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -48,3 +63,13 @@ public class DashboardChartDataset
|
|||
public string Label { get; set; } = string.Empty;
|
||||
public List<int> Data { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Payload untuk grafik data terverifikasi & aktif transaksi.
|
||||
/// </summary>
|
||||
public class VerifiedActiveChartPayload
|
||||
{
|
||||
public List<string> Labels { get; set; } = new();
|
||||
public List<int> VerifiedData { get; set; } = new();
|
||||
public List<int> ActiveData { get; set; } = new();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@
|
|||
@using System.Text.Json
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
var chartJson = JsonSerializer.Serialize(Model.YearlyData, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
});
|
||||
var serializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
||||
var chartJson = JsonSerializer.Serialize(Model.YearlyData, serializerOptions);
|
||||
var verificationTrendJson = JsonSerializer.Serialize(Model.VerifiedActiveYearlyData, serializerOptions);
|
||||
}
|
||||
|
||||
<div class="flex gap-2 flex-row justify-between md:gap-0 mt-2 mb-2">
|
||||
|
|
@ -269,6 +268,68 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-6"></div>
|
||||
|
||||
<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">
|
||||
<div class="prose max-w-2xl">
|
||||
<span class="text-xl font-semibold text-gray-900 font-['Plus_Jakarta_Sans']">
|
||||
Total Data Terverifikasi & Aktif Transaksi
|
||||
</span>
|
||||
<p class="mt-1 text-sm text-gray-500">
|
||||
Pantau tren jumlah entitas yang sudah terverifikasi dan aktif bertransaksi setiap bulan.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 md:flex-row">
|
||||
<fieldset class="fieldset w-full md:w-40">
|
||||
<label class="fieldset-label text-gray-500 text-xs uppercase tracking-wide" for="verificationYear">
|
||||
Filter Tahun
|
||||
</label>
|
||||
<select id="verificationYear" class="select select-sm" onchange="handleVerificationFilterChange()">
|
||||
@foreach (var year in Model.AvailableYears)
|
||||
{
|
||||
if (year == Model.SelectedYear)
|
||||
{
|
||||
<option value="@year" selected>@year</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@year">@year</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset w-full md:w-48">
|
||||
<label class="fieldset-label text-xs uppercase tracking-wide text-gray-500" for="verificationEntity">
|
||||
Jenis Entitas
|
||||
</label>
|
||||
<select id="verificationEntity" class="select select-sm" onchange="handleVerificationFilterChange()">
|
||||
@foreach (var entity in Model.Entities)
|
||||
{
|
||||
if (entity == Model.SelectedEntity)
|
||||
{
|
||||
<option value="@entity" selected>@entity</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@entity">@entity</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full overflow-x-auto">
|
||||
<div class="min-w-[900px]">
|
||||
<div class="h-64 md:h-96 relative">
|
||||
<canvas id="chartVerificationTrend" class="w-full h-full"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="~/js/chart.js"></script>
|
||||
<script>
|
||||
const chartDataByYear = @Html.Raw(chartJson);
|
||||
|
|
@ -365,11 +426,98 @@
|
|||
});
|
||||
};
|
||||
|
||||
const verificationChartData = @Html.Raw(verificationTrendJson);
|
||||
const verificationChartCanvas = document.getElementById('chartVerificationTrend');
|
||||
const verificationChartCtx = verificationChartCanvas ? verificationChartCanvas.getContext('2d') : null;
|
||||
let verificationChartInstance = null;
|
||||
|
||||
const buildVerificationDatasets = (payload) => [
|
||||
{
|
||||
label: 'Data Terverifikasi',
|
||||
data: payload.verifiedData,
|
||||
backgroundColor: '#22C55E',
|
||||
borderColor: '#16A34A',
|
||||
borderWidth: 1
|
||||
},
|
||||
{
|
||||
label: 'Data Aktif Transaksi',
|
||||
data: payload.activeData,
|
||||
backgroundColor: '#3B82F6',
|
||||
borderColor: '#2563EB',
|
||||
borderWidth: 1
|
||||
}
|
||||
];
|
||||
|
||||
const renderVerificationChart = (year, entity) => {
|
||||
if (!verificationChartCtx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const yearData = verificationChartData[year];
|
||||
const payload = yearData ? yearData[entity] : null;
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedData = {
|
||||
labels: payload.labels,
|
||||
datasets: buildVerificationDatasets(payload)
|
||||
};
|
||||
|
||||
if (verificationChartInstance) {
|
||||
verificationChartInstance.data = updatedData;
|
||||
verificationChartInstance.update();
|
||||
return;
|
||||
}
|
||||
|
||||
verificationChartInstance = new Chart(verificationChartCtx, {
|
||||
type: 'bar',
|
||||
data: updatedData,
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (context) => `${context.dataset.label}: ${context.formattedValue} Unit`
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: (value) => `${value} Unit`
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
window.handleYearChange = (event) => {
|
||||
renderChart(event.target.value);
|
||||
};
|
||||
|
||||
window.handleVerificationFilterChange = () => {
|
||||
const yearSelect = document.getElementById('verificationYear');
|
||||
const entitySelect = document.getElementById('verificationEntity');
|
||||
if (!yearSelect || !entitySelect) {
|
||||
return;
|
||||
}
|
||||
renderVerificationChart(yearSelect.value, entitySelect.value);
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
renderChart(@Model.SelectedYear);
|
||||
renderVerificationChart(@Model.SelectedYear, @Html.Raw(JsonSerializer.Serialize(Model.SelectedEntity)));
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue