Skip to content

Commit 73a30a6

Browse files
committed
feat(gas): 24h/31d gas price chart
1 parent 4c4956c commit 73a30a6

File tree

2 files changed

+109
-21
lines changed

2 files changed

+109
-21
lines changed

components/modules/gas/GasEfficiencyChart.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ onBeforeUnmount(() => {
298298
</script>
299299

300300
<template>
301-
<Flex direction="column" gap="16" wide>
301+
<Flex direction="column" gap="24" wide>
302302
<Flex align="center" justify="between">
303303
<Flex align="center" gap="6">
304304
<Icon name="stars" size="13" color="primary" />

components/modules/gas/GasPriceChart.vue

Lines changed: 108 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,31 @@ import { DateTime } from "luxon"
44
import * as d3 from "d3"
55
import { useDebounceFn } from "@vueuse/core"
66
7+
/** UI */
8+
import { Dropdown, DropdownItem } from "@/components/ui/Dropdown"
9+
import Button from "@/components/ui/Button.vue"
10+
711
/** Services */
812
import { truncate } from "@/services/utils"
913
1014
/** API */
1115
import { fetchGasPriceSeries } from "@/services/api/gas"
1216
17+
const selectedPeriodIdx = ref(0)
18+
const periods = ref([
19+
{
20+
title: "24 hours",
21+
value: 24,
22+
timeframe: "hour",
23+
},
24+
{
25+
title: "31 days",
26+
value: 30,
27+
timeframe: "day",
28+
},
29+
])
30+
const selectedPeriod = computed(() => periods.value[selectedPeriodIdx.value])
31+
1332
/** Chart El */
1433
const chartWrapperEl = ref()
1534
const gasPriceChartEl = ref()
@@ -71,12 +90,15 @@ const buildChart = (chartEl, data, onEnter, onLeave) => {
7190
}
7291
}
7392
74-
badgeText.value = DateTime.fromJSDate(data[idx].date).toFormat("hh:mm a")
93+
badgeText.value =
94+
selectedPeriod.value.timeframe === "day"
95+
? DateTime.fromJSDate(data[idx].date).toFormat("LLL dd")
96+
: DateTime.fromJSDate(data[idx].date).set({ minutes: 0 }).toFormat("hh:mm a")
7597
7698
if (!badgeEl.value) return
77-
if (idx === 0) {
99+
if (idx < 1) {
78100
badgeOffset.value = 0
79-
} else if (idx === 23) {
101+
} else if (idx > selectedPeriod.value.value - 2) {
80102
badgeOffset.value = badgeEl.value.getBoundingClientRect().width
81103
} else {
82104
badgeOffset.value = badgeEl.value.getBoundingClientRect().width / 2
@@ -126,15 +148,15 @@ const buildChart = (chartEl, data, onEnter, onLeave) => {
126148
.attr("stroke-width", 2)
127149
.attr("stroke-linecap", "round")
128150
.attr("stroke-linejoin", "round")
129-
.attr("d", line(data.slice(0, 23)))
151+
.attr("d", line(data.slice(0, data.length - 1)))
130152
svg.append("path")
131153
.attr("fill", "none")
132154
.attr("stroke", "var(--green)")
133155
.attr("stroke-width", 2)
134156
.attr("stroke-linecap", "round")
135157
.attr("stroke-linejoin", "round")
136158
.attr("stroke-dasharray", "4")
137-
.attr("d", line(data.slice(22, 24)))
159+
.attr("d", line(data.slice(data.length - 2, data.length)))
138160
139161
svg.append("circle")
140162
.attr("cx", x(data[data.length - 1].date))
@@ -147,24 +169,37 @@ const buildChart = (chartEl, data, onEnter, onLeave) => {
147169
}
148170
149171
const getGasPriceSeries = async () => {
172+
gasPriceSeries.value = []
173+
150174
const sizeSeriesRawData = await fetchGasPriceSeries({
151-
timeframe: "hour",
152-
from: parseInt(DateTime.now().minus({ hours: 26 }).ts / 1_000),
153-
to: parseInt(DateTime.now().ts / 1_000),
175+
timeframe: selectedPeriod.value.timeframe,
176+
to: parseInt(DateTime.now().plus({ minutes: 1 }).ts / 1_000),
177+
from: parseInt(
178+
DateTime.now()
179+
.set({ minutes: 0, seconds: 0 })
180+
.minus({
181+
days: selectedPeriod.value.timeframe === "day" ? selectedPeriod.value.value : 0,
182+
hours: selectedPeriod.value.timeframe === "hour" ? selectedPeriod.value.value - 1 : 0,
183+
}).ts / 1_000,
184+
),
154185
})
155186
156187
const gasPriceSeriesMap = {}
157188
sizeSeriesRawData.forEach((item) => {
158-
gasPriceSeriesMap[DateTime.fromISO(item.time).toFormat("dd-HH")] = item.value
189+
gasPriceSeriesMap[DateTime.fromISO(item.time).toFormat(selectedPeriod.value.timeframe === "day" ? "y-LL-dd" : "y-LL-dd-HH")] =
190+
item.value
159191
})
160192
161-
for (let i = 0; i < 24; i++) {
193+
for (let i = 1; i < selectedPeriod.value.value + 1; i++) {
162194
const dt = DateTime.now()
163-
.minus({ hours: 24 - i })
164195
.set({ minutes: 0, seconds: 0 })
196+
.minus({
197+
days: selectedPeriod.value.timeframe === "day" ? selectedPeriod.value.value - i : 0,
198+
hours: selectedPeriod.value.timeframe === "hour" ? selectedPeriod.value.value - i : 0,
199+
})
165200
gasPriceSeries.value.push({
166201
date: dt.toJSDate(),
167-
value: parseFloat(gasPriceSeriesMap[dt.toFormat("dd-HH")]) || 0,
202+
value: parseFloat(gasPriceSeriesMap[dt.toFormat(selectedPeriod.value.timeframe === "day" ? "y-LL-dd" : "y-LL-dd-HH")]) || 0,
168203
})
169204
}
170205
}
@@ -179,6 +214,13 @@ const buildGasTrackingCharts = async () => {
179214
)
180215
}
181216
217+
watch(
218+
() => selectedPeriodIdx.value,
219+
() => {
220+
buildGasTrackingCharts()
221+
},
222+
)
223+
182224
const debouncedRedraw = useDebounceFn((e) => {
183225
buildGasTrackingCharts()
184226
}, 500)
@@ -195,10 +237,43 @@ onBeforeUnmount(() => {
195237
</script>
196238

197239
<template>
198-
<Flex direction="column" gap="16" wide>
199-
<Flex align="center" gap="6">
200-
<Icon name="chart" size="13" color="primary" />
201-
<Text size="13" weight="600" color="primary">Average Gas Price</Text>
240+
<Flex direction="column" gap="24" wide>
241+
<Flex align="start" justify="between">
242+
<Flex align="center" gap="6">
243+
<Icon name="chart" size="13" color="primary" />
244+
<Text size="13" weight="600" color="primary">Average Gas Price</Text>
245+
</Flex>
246+
247+
<Flex align="center" gap="8">
248+
<Dropdown>
249+
<Button size="mini" type="secondary">
250+
<Icon name="chart" size="12" color="primary" />
251+
<Text color="primary">Line</Text>
252+
<Icon name="chevron" size="12" color="secondary" />
253+
</Button>
254+
255+
<template #popup>
256+
<DropdownItem> Line Chart </DropdownItem>
257+
<DropdownItem disabled> Heatmap </DropdownItem>
258+
</template>
259+
</Dropdown>
260+
261+
<Dropdown>
262+
<Button size="mini" type="secondary">
263+
{{ selectedPeriod.title }}
264+
<Icon name="chevron" size="12" color="secondary" />
265+
</Button>
266+
267+
<template #popup>
268+
<DropdownItem v-for="(period, idx) in periods" @click="selectedPeriodIdx = idx">
269+
<Flex align="center" gap="8">
270+
<Icon :name="idx === selectedPeriodIdx ? 'check' : ''" size="12" color="secondary" />
271+
{{ period.title }}
272+
</Flex>
273+
</DropdownItem>
274+
</template>
275+
</Dropdown>
276+
</Flex>
202277
</Flex>
203278

204279
<Flex ref="chartWrapperEl" direction="column" :class="$style.chart_wrapper">
@@ -219,10 +294,23 @@ onBeforeUnmount(() => {
219294

220295
<Flex :class="[$style.axis, $style.x]">
221296
<Flex align="end" justify="between" wide>
222-
<Text size="12" weight="600" color="tertiary">
223-
{{ DateTime.now().minus({ hours: 24 }).toFormat("dd LLL") }}
297+
<Text v-if="selectedPeriod.timeframe === 'day'" size="12" weight="600" color="tertiary">
298+
{{
299+
DateTime.now()
300+
.minus({ days: selectedPeriod.value - 1 })
301+
.toFormat("LLL dd")
302+
}}
224303
</Text>
225-
<Text size="12" weight="600" color="tertiary">Now</Text>
304+
<Text v-else size="12" weight="600" color="tertiary">
305+
{{
306+
DateTime.now()
307+
.minus({ hours: selectedPeriod.value - 1 })
308+
.set({ minutes: 0 })
309+
.toFormat("hh:mm a")
310+
}}
311+
</Text>
312+
313+
<Text size="12" weight="600" color="tertiary">{{ selectedPeriod.timeframe === "day" ? "Today" : "Now" }}</Text>
226314
</Flex>
227315
</Flex>
228316

@@ -244,7 +332,7 @@ onBeforeUnmount(() => {
244332
>
245333
<Flex align="center" gap="16">
246334
<Text size="12" weight="600" color="secondary">Price</Text>
247-
<Text size="12" weight="600" color="primary"> {{ truncate(tooltipText) }} UTIA</Text>
335+
<Text size="12" weight="600" color="primary"> {{ tooltipText.toFixed(6) }} UTIA</Text>
248336
</Flex>
249337
</Flex>
250338
</div>

0 commit comments

Comments
 (0)