refactor chart shortcode to use static js instead

This commit is contained in:
Sam 2025-01-10 12:20:38 +00:00
parent eb6f97f54a
commit 9c12ccadc5
7 changed files with 234 additions and 280 deletions

View File

@ -18,6 +18,8 @@ The chart always reflects the countries selected in the table.
{{< dropdown_filter id="days_ago_dropdown_filter" id_filter="days_ago" options="1 day:1,7 day:7,28 day:28,1 year:365,5 year:1826,10 year:3652,all time:10000" default_selection="7 day" targets="bitcoin-business-growth-chart bitcoin-business-growth-table" >}} {{< dropdown_filter id="days_ago_dropdown_filter" id_filter="days_ago" options="1 day:1,7 day:7,28 day:28,1 year:365,5 year:1826,10 year:3652,all time:10000" default_selection="7 day" targets="bitcoin-business-growth-chart bitcoin-business-growth-table" >}}
{{< table id="bitcoin-business-growth-table" endpoint="bitcoin_business_growth_percent_diff" headers="{'country_name': 'Country', 'date_range': 'Date Range', 'first_value': 'Previous #', 'last_value': 'Current #', 'difference': 'Diff', 'percent_difference': '% Diff'}" maxHeight="400px" sortable="true" valueId="country_name" selectableRows="multi" targets="bitcoin-business-growth-chart" defaultFirstSelected="true" >}} {{< table id="bitcoin-business-growth-table" endpoint="bitcoin_business_growth_percent_diff" headers="{'country_name': 'Country', 'date_range': 'Date Range', 'first_value': 'Previous #', 'last_value': 'Current #', 'difference': 'Diff', 'percent_difference': '% Diff'}" maxHeight="400px" sortable="true" valueId="country_name" selectableRows="multi" targets="bitcoin-business-growth-chart" defaultFirstSelected="true" >}}
{{< chart id="bitcoin-business-growth-table" src="/js/bitcoin-business-growth-chart.js" >}}
{{< chart chartMethod="tableRowSelectChart" id="bitcoin-business-growth-chart" endpoint="bitcoin_business_growth_timeseries" chartType="line" xAxisField="date" yAxisField="cumulative_value" scaleChart=true xAxisType="category" >}} {{< chart chartMethod="tableRowSelectChart" id="bitcoin-business-growth-chart" endpoint="bitcoin_business_growth_timeseries" chartType="line" xAxisField="date" yAxisField="cumulative_value" scaleChart=true xAxisType="category" >}}
#### Attribution and License #### Attribution and License

View File

@ -13,4 +13,4 @@ The following chart shows daily miner revenue in USD for the period selected in
{{< dropdown_filter id="days_ago_dropdown_filter" id_filter="days_ago" options="1 day:1,7 day:7,28 day:28,1 year:365,5 year:1826,10 year:3652,all time:10000" default_selection="7 day" targets="miner-rewards-chart" >}} {{< dropdown_filter id="days_ago_dropdown_filter" id_filter="days_ago" options="1 day:1,7 day:7,28 day:28,1 year:365,5 year:1826,10 year:3652,all time:10000" default_selection="7 day" targets="miner-rewards-chart" >}}
{{< chart chartMethod="simpleChart" id="miner-rewards-chart" endpoint="miner_rewards" chartType="line" xAxisField="date" yAxisField="total_reward_usd" scaleChart=true xAxisType="category" >}} {{< chart id="miner-rewards" src="/js/miner-rewards.js" >}}

View File

@ -97,18 +97,19 @@
}); });
} }
chartData = [];
function simpleChart( function simpleChart(
id, id,
endpoint, endpoint,
chartType, chartType,
xAxisField, xAxisField,
yAxisField, yAxisField,
series,
sortField = null, sortField = null,
scaleChart = false, scaleChart = false,
xAxisType = "time", xAxisType = "time",
formatValueDecimalPlaces = null, formatValueDecimalPlaces = null,
) { ) {
console.log(series);
async function fetchDataForChart(query) { async function fetchDataForChart(query) {
try { try {
const apiEndpoint = `${apiURL}/${endpoint}?${query}`; const apiEndpoint = `${apiURL}/${endpoint}?${query}`;
@ -145,12 +146,7 @@
scale: scaleChart, scale: scaleChart,
type: "value", type: "value",
}, },
series: [ series: JSON.parse(`{${series}}`),
{
data: chartData.map((item) => item.total_reward_usd),
type: "line",
},
],
}; };
myChart.setOption(option, true); myChart.setOption(option, true);

View File

@ -1,20 +1,7 @@
{{ partial "chart.html" }} <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.js"></script>
<section class = 'chart-container'> <section class = 'chart-container'>
<script>let id = '{{ .Get "id" }}'</script>
<div class = "chart" id='{{ .Get "id" }}'> <div class = "chart" id='{{ .Get "id" }}'>
<script> <script src={{ .Get "src" }}></script>
document.addEventListener("DOMContentLoaded", function () {
{{ .Get "chartMethod" | safeJS }}(
id={{ .Get "id" }},
endpoint={{ .Get "endpoint" }},
chartType={{ .Get "chartType" }},
xAxisField={{ .Get "xAxisField" }},
yAxisField={{ .Get "yAxisField" }},
sortField={{ .Get "sortField" }},
scaleChart={{ .Get "scaleChart" }},
xAxisType={{ .Get "xAxisType" }})
});
</script>
</div> </div>
</section> </section>

View File

@ -2,7 +2,7 @@
.chart-container { .chart-container {
display: flex; display: flex;
/* height: 600px; */ /* height: 600px; */
aspect-ratio: 1 / 1; aspect-ratio: 2 / 1;
} }
.chart { .chart {
@ -10,3 +10,12 @@
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
@media (max-width: 600px) {
.chart-container {
display: flex;
/* height: 600px; */
aspect-ratio: 1 / 1;
}
}

View File

@ -1,109 +1,102 @@
let chartData = []; var chartDom = document.getElementById(id);
let myChart; var myChart = echarts.init(chartDom);
chartData = [];
let periodIndex = 3; function tableRowSelectChart(
endpoint,
const periods = ["1 day", "7 day", "28 day", "365 day"]; xAxisField,
yAxisField,
async function fetchDataForChart(str, period) { chartType,
try { xAxisType,
const apiEndpoint = `${apiURL}/bitcoin_business_growth_by_country?cumulative_period_type=${period}&countries=${str}`; scaleChart = false,
console.log("Fetching from " + apiEndpoint); formatValueDecimalPlaces = null,
const response = await fetch(apiEndpoint); ) {
if (!response.ok) { async function fetchDataForChart(query, valueId) {
throw new Error(`HTTP error! status: ${response.status}`); try {
} const apiEndpoint = `${apiURL}/${endpoint}?${query}`;
const fetchedData = await response.json(); const response = await fetch(apiEndpoint);
const newData = fetchedData.reduce((acc, item) => { if (!response.ok) {
const objectId = item.country_name; throw new Error(`HTTP error! status: ${response.status}`);
if (!acc[objectId]) {
acc[objectId] = [];
} }
acc[objectId].push([item.date, item.cumulative_current_value]); const fetchedData = await response.json();
return acc; console.log(fetchedData);
}, {}); const newData = fetchedData.reduce((acc, item) => {
chartData = { ...chartData, ...newData }; const objectId = item[valueId];
updateChart(); if (!acc[objectId]) {
} catch (error) { acc[objectId] = [];
console.error("Fetching data failed:", error); }
acc[objectId].push([item[xAxisField], item[yAxisField]]);
return acc;
}, {});
chartData = { ...chartData, ...newData };
updateChart();
} catch (error) {
console.error("Fetching data failed:", error);
}
} }
}
function updateChart() { function updateChart() {
let chartDataMap = new Map(); let chartDataMap = new Map();
for (let objectId in chartData) { for (let objectId in chartData) {
chartDataMap.set(objectId, chartData[objectId]); chartDataMap.set(objectId, chartData[objectId]);
} }
option = {
backgroundColor: backgroundColor, var option = {
grid: grid, tooltip: {
tooltip: tooltip, ...tooltip,
toolbox: toolboxParams, valueFormatter(value, index) {
xAxis: { return formatValueDecimalPlaces == null
axisTick: axisTick, ? value
axisLabel: axisLabel, : nFormatter(value, formatValueDecimalPlaces);
axisLine: axisLine,
type: "category",
},
yAxis: {
axisTick: axisTick,
scale: true,
splitLine: {
show: false,
},
axisLabel: {
fontSize: 12 * fontScale,
color: textColor,
formatter(value, index) {
return nFormatter(value, 2);
}, },
}, },
axisLine: axisLine, xAxis: {
}, type: xAxisType,
},
series: Array.from(chartDataMap.entries()).map(([name, data]) => ({ yAxis: {
name, scale: scaleChart,
type: "line", type: "value",
data, },
showSymbol: false, series: Array.from(chartDataMap.entries()).map(([name, data]) => ({
})), name,
}; type: chartType,
myChart.setOption(option, true); data,
} showSymbol: false,
})),
function handleCheckboxChange(event) { };
if (event.target.type === "checkbox") { myChart.setOption(option, true);
const boxChecked = event.target.checked;
const boxId = event.target.id;
let selectedPeriod = getPeriodFromDropdown();
// Remove unchecked boxes
if (boxChecked === false) {
delete chartData[boxId];
updateChart();
// Add checked boxes
} else {
fetchDataForChart(boxId, selectedPeriod);
}
} }
}
document.addEventListener("DOMContentLoaded", function () { // listen for filter events for this target
periodDropdown(periods, periodIndex); document.addEventListener("filterChange", function (event) {
document.addEventListener("reloadTable", () => { tableId = document.getElementById(id).id;
myChart = echarts.init(document.getElementById("chart")); console.log(event.detail);
jsonTableCheckedBoxes = document.querySelectorAll( eventDetail = event.detail;
'#jsonTableContainer input[type="checkbox"]:checked', if (eventDetail.filterActions.includes("refresh")) {
); chartData = [];
const checkedBoxes = Array.from(jsonTableCheckedBoxes).map( updateChart();
(checkbox) => checkbox.id, } else {
); if (eventDetail.filterTargets.includes(tableId)) {
const str = checkedBoxes.join(","); if (eventDetail.filterActions.includes("selected")) {
let selectedPeriod = getPeriodFromDropdown(); valueId = eventDetail.filterId;
chartData = []; let selectedRow = {
fetchDataForChart(str, selectedPeriod); [valueId]: eventDetail.filterValue,
};
query = queryConstructor(selectedRow);
jsonTable = document.getElementById("jsonTableContainer"); fetchDataForChart(query, valueId);
jsonTable.removeEventListener("change", handleCheckboxChange); console.log(valueId)
jsonTable.addEventListener("change", handleCheckboxChange); } else {
delete chartData[eventDetail.filterValue];
updateChart();
}
}
}
}); });
}); }
tableRowSelectChart(
endpoint = "bitcoin_business_growth_timeseries",
xAxisField = "date",
yAxisField = "cumulative_value",
chartType = "line",
xAxisType = "category",
);

View File

@ -1,169 +1,136 @@
let dataArr = []; var chartDom = document.getElementById(id);
const myChart = echarts.init(document.getElementById("chart")); var myChart = echarts.init(chartDom);
const filename = "final__miner_rewards.json"; chartData = [];
const periods = [ function simpleChart(
"all time", endpoint,
"last 7 days", formatValueDecimalPlaces = null,
"last 28 days", ) {
"last 365 days", async function fetchDataForChart(query) {
"last 2 years", try {
]; const apiEndpoint = `${apiURL}/${endpoint}?${query}`;
const response = await fetch(apiEndpoint);
async function fetchDataForChart(selectedValue) { if (!response.ok) {
try { throw new Error(`HTTP error! status: ${response.status}`);
const apiEndpoint = `${apiURL}/get_json/${filename}?period=${selectedValue}`; }
const response = await fetch(apiEndpoint); chartData = await response.json();
if (!response.ok) { updateChart();
throw new Error(`HTTP error! status: ${response.status}`); console.log(chartData);
} catch (error) {
console.error("Fetching data failed:", error);
} }
const dataArr = await response.json();
initEchart(dataArr);
} catch (error) {
console.error("Fetching data failed:", error);
} }
}
function initEchart(dataArr) { function updateChart() {
console.log(dataArr); var option = {
const option = { tooltip: {
backgroundColor: backgroundColor, ...tooltip,
tooltip: { valueFormatter(value, index) {
...tooltip, return formatValueDecimalPlaces == null
formatter: function (params) { ? value
let colors = ["#ee6666", "#000000", "#b0da9d", "#8699d5"]; : nFormatter(value, formatValueDecimalPlaces);
let colorSpan = (color) =>
'<span style="display:inline-block;margin-right:1px;border-radius:5px;width:9px;height:9px;background-color:' +
color +
'"></span>';
let tooltip = "<p>" + params[0].axisValue + "</p>";
params.reverse().forEach((item, index) => {
let color = colors[index % colors.length];
let labels =
"<p>" +
colorSpan(color) +
" " +
item.seriesName +
": " +
nFormatter(item.data, 3);
("</p>");
tooltip += labels;
});
return tooltip;
},
valueFormatter(value, index) {
return nFormatter(value, 3);
},
},
toolbox: toolboxParams,
xAxis: {
data: dataArr.map((row) => row.date),
axisTick: axisTick,
axisLabel: axisLabel,
axisLine: axisLine,
},
grid: grid,
dataZoom: dataZoom(0),
yAxis: [
{
type: "value",
name: "Rewards (USD)",
nameLocation: "middle",
nameGap: 30,
nameTextStyle: textStyleMain,
position: "left",
alignTicks: true,
axisTick: axisTick,
axisLine: axisLine,
splitLine: {
show: false,
}, },
axisLabel: { },
...axisLabel, xAxis: {
formatter(value, index) { type: "category",
return nFormatter(value, 0); data: chartData.map((item) => item.date),
},
yAxis: [
{
type: "value",
name: "Rewards (USD)",
nameLocation: "middle",
nameGap: 30,
nameTextStyle: textStyleMain,
position: "left",
alignTicks: true,
axisTick: axisTick,
axisLine: axisLine,
splitLine: {
show: false,
},
axisLabel: {
...axisLabel,
formatter(value, index) {
return nFormatter(value, 0);
},
}, },
}, },
}, {
{ type: "value",
type: "value", name: "Subsidy (BTC)",
name: "Subsidy (BTC)", nameLocation: "middle",
nameLocation: "middle", nameGap: 20,
nameGap: 20, nameTextStyle: {
nameTextStyle: { fontSize: 12 * fontScale,
fontSize: 12 * fontScale, color: textColor,
color: textColor, },
axisTick: axisTick,
position: "right",
alignTicks: true,
axisLine: axisLine,
splitLine: {
show: false,
},
axisLabel: {
fontSize: 12 * fontScale,
color: textColor,
},
}, },
axisTick: axisTick, ],
position: "right", series: [
alignTicks: true, {
axisLine: axisLine, type: "line",
splitLine: { name: "Subsidy (USD)",
show: false, stack: "Total",
areaStyle: {},
symbol: "none",
lineStyle: {
width: 0,
},
data: chartData.map((row) => row.subsidy_usd),
}, },
axisLabel: { {
fontSize: 12 * fontScale, type: "line",
color: textColor, name: "Fees (USD)",
stack: "Total",
areaStyle: {},
symbol: "none",
lineStyle: {
width: 0,
},
data: chartData.map((row) => row.totalfee_usd),
}, },
}, {
], type: "line",
series: [ name: "Total (USD)",
{ symbol: "none",
type: "line", lineStyle: {
name: "Subsidy (USD)", width: 1,
stack: "Total", color: textColor,
areaStyle: {}, },
symbol: "none", data: chartData.map((row) => row.total_reward_usd),
lineStyle: {
width: 0,
}, },
data: dataArr.map((row) => row.subsidy_usd), {
}, type: "line",
{ yAxisIndex: 1,
type: "line", name: "Block Subsidy (BTC)",
name: "Fees (USD)", symbol: "none",
stack: "Total", lineStyle: {
areaStyle: {}, width: 3,
symbol: "none", },
lineStyle: { data: chartData.map((row) => row.block_subsidy),
width: 0,
}, },
data: dataArr.map((row) => row.totalfee_usd), ],
}, };
{
type: "line",
name: "Total (USD)",
symbol: "none",
lineStyle: {
width: 1,
color: textColor,
},
data: dataArr.map((row) => row.total_reward_usd),
},
{
type: "line",
yAxisIndex: 1,
name: "Block Subsidy (BTC)",
symbol: "none",
lineStyle: {
width: 3,
},
data: dataArr.map((row) => row.block_subsidy),
},
],
};
myChart.setOption(option); myChart.setOption(option, true);
} }
document.addEventListener("DOMContentLoaded", function () { query = queryConstructor();
let periodIndex = 2; fetchDataForChart(query);
periodDropdown(periods, periodIndex); document.addEventListener("filterChange", function (event) {
fetchDataForChart(periods[periodIndex]); query = queryConstructor();
const selectElement = document.getElementById("select-period-dropdown"); fetchDataForChart(query);
selectElement.addEventListener("change", function (event) {
const selectedValue = event.target.value;
fetchDataForChart(selectedValue);
}); });
}); }
simpleChart((endpoint = "miner_rewards"));