{
padding: 0 12px;
}
+.setting_item {
+ min-height: 24px;
+}
+
+.chart_selector {
+ padding: 4px 6px 4px 6px;
+ box-shadow: inset 0 0 0 1px var(--op-10);
+ border-radius: 5px;
+ cursor: pointer;
+ transition: all 1s ease-in-out;
+}
+
.data {
border-radius: 4px 4px 8px 8px;
background: var(--card-background);
diff --git a/components/modules/namespace/tables/BlobsTable.vue b/components/modules/namespace/tables/BlobsTable.vue
index 8ca07b9b..9a73aa1d 100644
--- a/components/modules/namespace/tables/BlobsTable.vue
+++ b/components/modules/namespace/tables/BlobsTable.vue
@@ -83,15 +83,15 @@ const handleViewBlob = (blob) => {
-
+
-
+
-
+
- {{ blob.signer }}
+ {{ blob.signer.hash }}
diff --git a/components/modules/navigation/NavLink.vue b/components/modules/navigation/NavLink.vue
index 7f9985c5..cb911566 100644
--- a/components/modules/navigation/NavLink.vue
+++ b/components/modules/navigation/NavLink.vue
@@ -10,6 +10,58 @@ const props = defineProps({
const isExpanded = ref(props.link.children?.some((l) => l.path === route.path) ? true : (props.link.name === 'Rollups' ? true : false))
+const isSubRouteActive = (link) => {
+ let res = false
+ if (link.queryParam) {
+ const queryString = route.fullPath.split('?')[1]
+ const params = new URLSearchParams(queryString)
+
+ for (const [key, value] of params.entries()) {
+ if (link.queryParam[key] === value) {
+ res = true
+ break;
+ }
+ }
+ }
+
+ return res
+}
+
+const isAnyChildrenActive = computed(() => {
+ let res = false
+
+ if (props.link.children?.length) {
+ for (const link of props.link.children) {
+ if (isSubRouteActive(link)) {
+ res = true
+ break;
+ }
+ }
+ }
+
+ return res
+})
+
+const isActive = computed(() => {
+ let res = false
+
+ if (props.link.queryParam) {
+ res = isSubRouteActive(props.link)
+ } else {
+ if (route.path?.includes("/txs")) {
+ if (props.link.name === "Transactions") {
+ res = !route.fullPath?.includes("message_type=MsgPayForBlobs")
+ } else if (props.link.name === "Pay for Blobs") {
+ res = route.fullPath?.includes("message_type=MsgPayForBlobs")
+ }
+ } else {
+ res = route.path === props.link.path
+ }
+ }
+
+ return res
+})
+
const handleClick = () => {
emit("onClose")
if (props.link.callback) props.link.callback()
@@ -18,7 +70,12 @@ const handleClick = () => {
-
+
{{ link.name }}
@@ -70,6 +127,16 @@ const handleClick = () => {
fill: var(--brand);
}
}
+
+ &.parentActive {
+ & span {
+ color: var(--txt-primary);
+ }
+
+ & .link_icon:first-of-type {
+ fill: var(--brand);
+ }
+ }
}
.chevron_icon {
diff --git a/components/modules/navigation/Search.vue b/components/modules/navigation/Search.vue
index a232586f..d9812cce 100644
--- a/components/modules/navigation/Search.vue
+++ b/components/modules/navigation/Search.vue
@@ -13,6 +13,9 @@ import Spinner from "@/components/ui/Spinner.vue"
/** API */
import { search } from "@/services/api/search"
+/** Services */
+import { shortHex } from "@/services/utils"
+
const wrapperEl = ref()
const inputRef = ref()
const searchTerm = ref("")
@@ -57,7 +60,20 @@ const debouncedSearch = useDebounceFn(async () => {
isSearching.value = true
const { data } = await search(searchTerm.value.trim())
- results.value = data.value
+ if (data.value?.length) {
+ results.value = data.value.map(res => {
+ const metadata = getResultMetadata(res)
+ return {
+ ...res,
+ type: metadata.type,
+ title: metadata.title,
+ subtitle: metadata.subtitle,
+ routerLink: metadata.routerLink,
+ }
+ })
+ } else {
+ results.value = []
+ }
isSearching.value = false
}, 250)
@@ -68,7 +84,7 @@ const handleInput = () => {
}
const getResultMetadata = (target) => {
- let metadata = { type: null, title: null, routerLink: null }
+ let metadata = { type: null, title: null, subtitle: null, routerLink: null }
switch (target.type.toLowerCase()) {
case "tx":
@@ -92,7 +108,7 @@ const getResultMetadata = (target) => {
case "address":
metadata.type = target.type
- metadata.title = target.result.alias || target.result.hash
+ metadata.title = target.result.celestials?.name || target.result.alias || target.result.hash
metadata.routerLink = `/address/${target.bookmark ? target.result.id : target.result.hash}`
break
@@ -152,11 +168,11 @@ const handleSelect = () => {
Results
-
-
-
-
- {{ getResultMetadata(result).title }}
+
+
+
+
+ {{ result.title }}
{{ result.type }}
@@ -202,7 +218,8 @@ const handleSelect = () => {
.popup {
position: absolute;
max-width: 600px;
- top: 40px;
+ /* top: 40px; */
+ top: calc(100% + 10px);
left: 0;
right: 0;
z-index: 1000;
@@ -239,19 +256,36 @@ const handleSelect = () => {
&:hover {
background: var(--op-5);
}
+
+ .title_wrapper {
+ width: 90%;
+
+ .title {
+ max-width: 100%;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+ }
}
@media (max-width: 800px) {
.wrapper {
- position: initial;
+ position: relative;
}
.popup {
max-width: initial;
- top: 160px;
+ /* top: 160px; */
left: 12px;
right: 12px;
}
+
+ .item {
+ .title_wrapper {
+ width: 70%;
+ }
+ }
}
diff --git a/components/modules/rollup/RollupCharts.vue b/components/modules/rollup/RollupCharts.vue
index ebbfbecc..c025c53e 100644
--- a/components/modules/rollup/RollupCharts.vue
+++ b/components/modules/rollup/RollupCharts.vue
@@ -9,13 +9,19 @@ import Button from "@/components/ui/Button.vue"
import { Dropdown, DropdownItem } from "@/components/ui/Dropdown"
import Input from "@/components/ui/Input.vue"
import Popover from "@/components/ui/Popover.vue"
+import Toggle from "@/components/ui/Toggle.vue"
+import Tooltip from "@/components/ui/Tooltip.vue"
/** Services */
import { abbreviate, formatBytes, sortArrayOfObjects, spaces, tia } from "@/services/utils"
/** API */
import { fetchRollupSeries } from "@/services/api/stats"
-import { fetchRollups } from "@/services/api/rollup"
+import { fetchRollups, fetchRollupTVL } from "@/services/api/rollup"
+
+/** Store */
+import { useSettingsStore } from "@/store/settings"
+const settingsStore = useSettingsStore()
const props = defineProps({
rollup: {
@@ -24,7 +30,8 @@ const props = defineProps({
},
})
-const selectedPeriodIdx = ref(0)
+/** Chart settings */
+const selectedPeriodIdx = ref(2)
const periods = ref([
{
title: "Last 24 hours",
@@ -48,20 +55,48 @@ const periods = ref([
},
])
const selectedPeriod = computed(() => periods.value[selectedPeriodIdx.value])
+const chartView = ref("line")
+const loadLastValue = ref(true)
+
+const isOpen = ref(false)
+const handleOpen = () => {
+ isOpen.value = true
+}
+const handleClose = () => {
+ isOpen.value = false
+}
+
+const handleChangeChartView = () => {
+ if (chartView.value === 'line') {
+ chartView.value = 'bar'
+ } else {
+ chartView.value = 'line'
+ }
+}
+
+const updateUserSettings = () => {
+ settingsStore.chart = {
+ ...settingsStore.chart,
+ view: chartView.value,
+ loadLastValue: loadLastValue.value,
+ }
+}
/** Charts */
const chartWrapperEl = ref()
const sizeSeriesChartEl = ref()
const pfbSeriesChartEl = ref()
const feeSeriesChartEl = ref()
+const tvlSeriesChartEl = ref()
const comparisonChartEl = ref()
-const barWidth = ref(0)
+const comparisonBarWidth = ref(0)
/** Data */
const isLoading = ref(false)
const sizeSeries = ref([])
const pfbSeries = ref([])
const feeSeries = ref([])
+const tvlSeries = ref([])
const rollupsList = ref()
const comparisonData = ref([])
const selectedRollup = ref()
@@ -70,7 +105,7 @@ const selectedRollup = ref()
const showSeriesTooltip = ref(false)
const showPfbTooltip = ref(false)
const showFeeTooltip = ref(false)
-const showComparisonTooltip = ref(false)
+const showTVLTooltip = ref(false)
const tooltipEl = ref()
const tooltipXOffset = ref(0)
const tooltipYOffset = ref(0)
@@ -82,13 +117,44 @@ const badgeEl = ref()
const badgeText = ref("")
const badgeOffset = ref(0)
-const buildChart = (chartEl, data, onEnter, onLeave) => {
+const getXAxisLabels = (start, tvl = false) => {
+ let res = ""
+
+ let tf = selectedPeriod.value.timeframe
+ let periodValue = selectedPeriod.value.value
+ if ( tvl && ["hour", "week"].includes(selectedPeriod.value.timeframe)) {
+ tf = "day"
+ periodValue = 30
+ }
+
+ switch (tf) {
+ case "month":
+ start
+ ? res = DateTime.now().minus({ months: periodValue - 1 }).toFormat("LLL y")
+ : res = loadLastValue.value ? DateTime.now().toFormat("LLL") : DateTime.now().minus({ months: 1 }).toFormat("LLL")
+ break;
+ case "day":
+ start
+ ? res = DateTime.now().minus({ days: periodValue - 1 }).toFormat("LLL dd")
+ : res = loadLastValue.value ? "Today" : DateTime.now().minus({ days: 1 }).toFormat("LLL dd")
+ break;
+ default:
+ start
+ ? res = DateTime.now().minus({ hours: periodValue - 1 }).set({ minutes: 0 }).toFormat("hh:mm a")
+ : res = loadLastValue.value ? "Now" : DateTime.now().minus({ hours: 1 }).set({ minutes: 0 }).toFormat("hh:mm a")
+ break;
+ }
+
+ return res
+}
+
+const buildLineChart = (chartEl, data, onEnter, onLeave, metric) => {
const width = chartWrapperEl.value.wrapper.getBoundingClientRect().width
const height = 180
const marginTop = 0
const marginRight = 0
const marginBottom = 24
- const marginLeft = 40
+ const marginLeft = 52
const MAX_VALUE = d3.max(data, (d) => d.value) ? d3.max(data, (d) => d.value) : 1
@@ -123,20 +189,25 @@ const buildChart = (chartEl, data, onEnter, onLeave) => {
}
}
+ let tf = selectedPeriod.value.timeframe
+ if (metric === "tvl" && ["hour", "week"].includes(selectedPeriod.value.timeframe)) {
+ tf = "day"
+ }
badgeText.value =
- selectedPeriod.value.timeframe === "month"
+ tf === "month"
? DateTime.fromJSDate(data[idx].date).toFormat("LLL")
- : selectedPeriod.value.timeframe === "day"
+ : tf === "day"
? DateTime.fromJSDate(data[idx].date).toFormat("LLL dd")
: DateTime.fromJSDate(data[idx].date).set({ minutes: 0 }).toFormat("hh:mm a")
if (!badgeEl.value) return
- if (idx < 2) {
+ const badgeWidth = badgeEl.value.getBoundingClientRect().width
+ if (tooltipXOffset.value - marginLeft < badgeWidth / 2) {
badgeOffset.value = 0
- } else if (idx > selectedPeriod.value.value - 3) {
- badgeOffset.value = badgeEl.value.getBoundingClientRect().width
+ } else if (badgeWidth + tooltipXOffset.value > width) {
+ badgeOffset.value = Math.abs(width - (badgeWidth + tooltipXOffset.value)) + ((data.length - 1 - idx) * 2)
} else {
- badgeOffset.value = badgeEl.value.getBoundingClientRect().width / 2
+ badgeOffset.value = badgeWidth / 2
}
}
const onPointerleft = () => {
@@ -177,27 +248,229 @@ const buildChart = (chartEl, data, onEnter, onLeave) => {
.attr("d", `M${0},${height - marginBottom - 6} L${width},${height - marginBottom - 6}`)
/** Chart Line */
+ let path1 = null
+ let path2 = null
+ path1 = svg
+ .append("path")
+ .attr("fill", "none")
+ .attr("stroke", "var(--brand)")
+ .attr("stroke-width", 2)
+ .attr("stroke-linecap", "round")
+ .attr("stroke-linejoin", "round")
+ .attr("d", line(loadLastValue.value ? data.slice(0, data.length - 1) : data))
+
+ if (loadLastValue.value) {
+ // Create pattern
+ const defs = svg.append("defs")
+ const pattern = defs.append("pattern")
+ .attr("id", "dashedPattern")
+ .attr("width", 8)
+ .attr("height", 2)
+ .attr("patternUnits", "userSpaceOnUse")
+ pattern.append("rect")
+ .attr("width", 4)
+ .attr("height", 2)
+ .attr("fill", "var(--brand)")
+ pattern.append("rect")
+ .attr("x", 8)
+ .attr("width", 4)
+ .attr("height", 2)
+ .attr("fill", "transparent")
+
+ // Last dash segment
+ path2 = svg
+ .append("path")
+ .attr("fill", "none")
+ .attr("stroke", "url(#dashedPattern)")
+ .attr("stroke-width", 2)
+ .attr("stroke-linecap", "round")
+ .attr("stroke-linejoin", "round")
+ .attr("d", line(data.slice(data.length - 2, data.length)))
+ }
+
+ const totalDuration = 1_000
+ const path1Duration = loadLastValue.value ? totalDuration / data.length * (data.length - 1) : totalDuration
+ const path1Length = path1.node().getTotalLength()
+
+ path1
+ .attr("stroke-dasharray", path1Length)
+ .attr("stroke-dashoffset", path1Length)
+ .transition()
+ .duration(path1Duration)
+ .ease(d3.easeLinear)
+ .attr("stroke-dashoffset", 0)
+
+ if (loadLastValue.value) {
+ const path2Duration = totalDuration / data.length
+ const path2Length = path2.node().getTotalLength() + 1
+
+ path2
+ .attr("stroke-dasharray", path2Length)
+ .attr("stroke-dashoffset", path2Length)
+ .transition()
+ .duration(path2Duration)
+ .ease(d3.easeLinear)
+ .delay(path1Duration)
+ .attr("stroke-dashoffset", 0)
+ }
+
+ const point = svg.append("circle")
+ .attr("cx", x(data[data.length - 1].date))
+ .attr("cy", y(data[data.length - 1].value))
+ .attr("fill", "var(--brand)")
+ .attr("r", 3)
+ .attr("opacity", 0)
+
+ point.transition()
+ .delay(totalDuration)
+ .duration(200)
+ .attr("opacity", 1)
+
+ if (chartEl.children[0]) chartEl.children[0].remove()
+ chartEl.append(svg.node())
+}
+
+const buildBarChart = (chartEl, data, onEnter, onLeave, metric) => {
+ const width = chartWrapperEl.value.wrapper.getBoundingClientRect().width
+ const height = 180
+ const marginTop = 0
+ const marginRight = 2
+ const marginBottom = 24
+ const marginLeft = 52
+
+ const barWidth = Math.max(Math.round((width - marginLeft - marginRight) / data.length - (data.length > 7 ? 4 : 8)), 4)
+
+ const MAX_VALUE = d3.max(data, (d) => d.value) ? d3.max(data, (d) => d.value) : 1
+
+ /** Scale */
+ const x = d3.scaleUtc(
+ d3.extent(data, (d) => d.date),
+ [marginLeft, width - marginRight - barWidth],
+ )
+ const y = d3.scaleLinear([0, MAX_VALUE], [height - marginBottom, marginTop])
+
+ /** Tooltip */
+ const bisect = d3.bisector((d) => d.date).center
+ const onPointermoved = (event) => {
+ onEnter()
+
+ // const idx = bisect(data, x.invert(d3.pointer(event)[0]))
+ const idx = bisect(data, x.invert(d3.pointer(event)[0] - barWidth / 2))
+
+ const elements = document.querySelectorAll(`[metric="${metric}"]`)
+ elements.forEach(el => {
+ if (+el.getAttribute('data-index') === idx) {
+ el.style.filter = "brightness(1.2)"
+ } else {
+ el.style.filter = "brightness(0.6)"
+ }
+
+ })
+
+ tooltipXOffset.value = x(data[idx].date)
+ tooltipYDataOffset.value = y(data[idx].value)
+ tooltipYOffset.value = event.layerY
+ tooltipText.value = data[idx].value
+
+ if (tooltipEl.value) {
+ if (idx > parseInt(selectedPeriod.value.value / 2)) {
+ tooltipDynamicXPosition.value = tooltipXOffset.value - tooltipEl.value.wrapper.getBoundingClientRect().width - 16
+ } else {
+ tooltipDynamicXPosition.value = tooltipXOffset.value + 16
+ }
+ }
+
+ let tf = selectedPeriod.value.timeframe
+ if (metric === "tvl" && ["hour", "week"].includes(selectedPeriod.value.timeframe)) {
+ tf = "day"
+ }
+ badgeText.value =
+ tf === "month"
+ ? DateTime.fromJSDate(data[idx].date).toFormat("LLL")
+ : tf === "day"
+ ? DateTime.fromJSDate(data[idx].date).toFormat("LLL dd")
+ : DateTime.fromJSDate(data[idx].date).set({ minutes: 0 }).toFormat("hh:mm a")
+
+ if (!badgeEl.value) return
+ const badgeWidth = badgeEl.value.getBoundingClientRect().width
+ if (tooltipXOffset.value - marginLeft < badgeWidth / 2) {
+ badgeOffset.value = 0
+ } else if (badgeWidth + tooltipXOffset.value > width) {
+ badgeOffset.value = Math.abs(width - (badgeWidth + tooltipXOffset.value)) + ((data.length - 1 - idx) * 2)
+ } else {
+ badgeOffset.value = (badgeWidth - barWidth) / 2
+ }
+ }
+ const onPointerleft = () => {
+ onLeave()
+
+ const elements = document.querySelectorAll('[data-index]')
+ elements.forEach(el => {
+ el.style.filter = ""
+ })
+ badgeText.value = ""
+ }
+
+ /** SVG Container */
+ const svg = d3
+ .create("svg")
+ .attr("width", width)
+ .attr("height", height)
+ .attr("viewBox", [0, 0, width, height])
+ .attr("preserveAspectRatio", "none")
+ .attr("style", "max-width: 100%; height: intrinsic;")
+ .style("-webkit-tap-highlight-color", "transparent")
+ .on("pointerenter pointermove", onPointermoved)
+ .on("pointerleave", onPointerleft)
+ .on("touchstart", (event) => event.preventDefault())
+
+ /** Vertical Lines */
svg.append("path")
.attr("fill", "none")
- .attr("stroke", "var(--brand)")
+ .attr("stroke", "var(--op-10)")
.attr("stroke-width", 2)
- .attr("stroke-linecap", "round")
- .attr("stroke-linejoin", "round")
- .attr("d", line(data.slice(0, data.length - 1)))
+ .attr("d", `M${marginLeft},${height - marginBottom + 2} L${marginLeft},${height - marginBottom - 5}`)
svg.append("path")
.attr("fill", "none")
- .attr("stroke", "var(--brand)")
+ .attr("stroke", "var(--op-10)")
.attr("stroke-width", 2)
- .attr("stroke-linecap", "round")
- .attr("stroke-linejoin", "round")
- .attr("stroke-dasharray", "8")
- .attr("d", line(data.slice(data.length - 2, data.length)))
+ .attr("d", `M${width - 1},${height - marginBottom + 2} L${width - 1},${height - marginBottom - 5}`)
- svg.append("circle")
- .attr("cx", x(data[data.length - 1].date))
- .attr("cy", y(data[data.length - 1].value))
- .attr("fill", "var(--brand)")
- .attr("r", 3)
+ /** Default Horizontal Line */
+ svg.append("path")
+ .attr("fill", "none")
+ .attr("stroke", "var(--op-10)")
+ .attr("stroke-width", 2)
+ .attr("d", `M${0},${height - marginBottom - 6} L${width},${height - marginBottom - 6}`)
+
+ /** Chart Bars */
+ svg.append("defs")
+ .append("pattern")
+ .attr("id", "diagonal-stripe")
+ .attr("width", 6)
+ .attr("height", 6)
+ .attr("patternUnits", "userSpaceOnUse")
+ .attr("patternTransform", "rotate(45)")
+ .append("rect")
+ .attr("width", 2)
+ .attr("height", 6)
+ .attr("transform", "translate(0,0)")
+ .attr("fill", "var(--brand)")
+
+ svg.append('g')
+ .selectAll("g")
+ .data(data)
+ .enter().append("rect")
+ .attr("class", "bar")
+ .attr('data-index', (d, i) => i)
+ .attr('metric', metric)
+ .attr("x", d => x(new Date(d.date)))
+ .attr('y', d => y(d.value))
+ .attr("width", barWidth)
+ .attr('fill', (d, i) => (loadLastValue.value && i === data.length - 1) ? `url(#diagonal-stripe)` : "var(--brand)")
+ .transition()
+ .duration(1_000)
+ .attr('height', d => Math.max(height - marginBottom - 6 - y(d.value), 0))
if (chartEl.children[0]) chartEl.children[0].remove()
chartEl.append(svg.node())
@@ -211,12 +484,12 @@ const getRollupsList = async () => {
rollupsList.value = sortArrayOfObjects(data, 'slug').filter(r => r.id !== props.rollup.id)
}
-const fetchData = async (rollup, metric) => {
+const fetchData = async (rollup, metric, from, timeframe) => {
const data = await fetchRollupSeries({
id: rollup.id,
name: metric,
- timeframe: selectedPeriod.value.timeframe,
- from: parseInt(
+ timeframe: timeframe ? timeframe : selectedPeriod.value.timeframe,
+ from: from ? from : parseInt(
DateTime.now().minus({
days: selectedPeriod.value.timeframe === "day" ? selectedPeriod.value.value : 0,
hours: selectedPeriod.value.timeframe === "hour" ? selectedPeriod.value.value : 0,
@@ -248,7 +521,8 @@ const getSizeSeries = async () => {
days: selectedPeriod.value.timeframe === "day" ? selectedPeriod.value.value - i : 0,
hours: selectedPeriod.value.timeframe === "hour" ? selectedPeriod.value.value - i : 0,
})
- } sizeSeries.value.push({
+ }
+ sizeSeries.value.push({
date: dt.toJSDate(),
value: parseInt(sizeSeriesMap[dt.toFormat(["day", "month"].includes(selectedPeriod.value.timeframe) ? "y-LL-dd" : "y-LL-dd-HH")]) || 0,
})
@@ -313,6 +587,48 @@ const getFeeSeries = async () => {
}
}
+const getTVLSeries = async () => {
+ tvlSeries.value = []
+
+ let from = ""
+ let tf = selectedPeriod.value.timeframe
+ let periodValue = selectedPeriod.value.value
+ if (["hour", "week"].includes(selectedPeriod.value.timeframe)) {
+ from = parseInt(DateTime.now().minus({ days: 30 }).ts / 1_000)
+ tf = "day"
+ periodValue = 30
+ }
+
+ const tvlSeriesRawData = await fetchRollupTVL({
+ slug: props.rollup.slug,
+ period: tf,
+ from,
+ })
+
+ const tvlSeriesMap = {}
+ tvlSeriesRawData.forEach((item) => {
+ tvlSeriesMap[DateTime.fromISO(item.time).toFormat(["day", "month"].includes(tf) ? "y-LL-dd" : "y-LL-dd-HH")] = item.value
+ })
+
+ for (let i = 1; i < periodValue + 1; i++) {
+ let dt
+ if (tf === "month") {
+ dt = DateTime.now().startOf('month').minus({
+ months: tf === "month" ? periodValue - i : 0,
+ })
+ } else {
+ dt = DateTime.now().minus({
+ days: tf === "day" ? periodValue - i : 0,
+ hours: tf === "hour" ? periodValue - i : 0,
+ })
+ }
+ tvlSeries.value.push({
+ date: dt.toJSDate(),
+ value: parseFloat(tvlSeriesMap[dt.toFormat(["day", "month"].includes(tf) ? "y-LL-dd" : "y-LL-dd-HH")]) || 0,
+ })
+ }
+}
+
const prepareComparisonData = async () => {
isLoading.value = true
@@ -362,39 +678,79 @@ const filteredRollupsList = computed(() => {
return rollupsList.value.filter((r) => r.name.toLowerCase().includes(searchTerm.value.trim().toLowerCase()))
})
-const buildRollupCharts = async () => {
+const buildRollupCharts = async (loadData = true) => {
isLoading.value = true
- await getRollupsList()
- if (!selectedRollup.value) {
- selectedRollup.value = rollupsList.value[0]
- }
+ if (loadData) {
+ await getRollupsList()
+ if (!selectedRollup.value) {
+ selectedRollup.value = rollupsList.value[0]
+ }
- barWidth.value = comparisonChartEl.value.wrapper.getBoundingClientRect().width
+ comparisonBarWidth.value = comparisonChartEl.value.wrapper.getBoundingClientRect().width
- await getSizeSeries()
- buildChart(
- sizeSeriesChartEl.value.wrapper,
- sizeSeries.value,
- () => (showSeriesTooltip.value = true),
- () => (showSeriesTooltip.value = false),
- )
-
- await getPfbSeries()
- buildChart(
- pfbSeriesChartEl.value.wrapper,
- pfbSeries.value,
- () => (showPfbTooltip.value = true),
- () => (showPfbTooltip.value = false),
- )
+ await getSizeSeries()
+ await getPfbSeries()
+ await getFeeSeries()
+ await getTVLSeries()
+ }
- await getFeeSeries()
- buildChart(
- feeSeriesChartEl.value.wrapper,
- feeSeries.value,
- () => (showFeeTooltip.value = true),
- () => (showFeeTooltip.value = false),
- )
+ if (chartView.value === "line") {
+ buildLineChart(
+ sizeSeriesChartEl.value.wrapper,
+ loadLastValue.value ? sizeSeries.value : sizeSeries.value.slice(0, sizeSeries.value.length - 1),
+ () => (showSeriesTooltip.value = true),
+ () => (showSeriesTooltip.value = false),
+ )
+ buildLineChart(
+ pfbSeriesChartEl.value.wrapper,
+ loadLastValue.value ? pfbSeries.value : pfbSeries.value.slice(0, pfbSeries.value.length - 1),
+ () => (showPfbTooltip.value = true),
+ () => (showPfbTooltip.value = false),
+ )
+ buildLineChart(
+ feeSeriesChartEl.value.wrapper,
+ loadLastValue.value ? feeSeries.value : feeSeries.value.slice(0, feeSeries.value.length - 1),
+ () => (showFeeTooltip.value = true),
+ () => (showFeeTooltip.value = false),
+ )
+ buildLineChart(
+ tvlSeriesChartEl.value.wrapper,
+ loadLastValue.value ? tvlSeries.value : tvlSeries.value.slice(0, tvlSeries.value.length - 1),
+ () => (showTVLTooltip.value = true),
+ () => (showTVLTooltip.value = false),
+ "tvl",
+ )
+ } else {
+ buildBarChart(
+ sizeSeriesChartEl.value.wrapper,
+ loadLastValue.value ? sizeSeries.value : sizeSeries.value.slice(0, sizeSeries.value.length - 1),
+ () => (showSeriesTooltip.value = true),
+ () => (showSeriesTooltip.value = false),
+ "size",
+ )
+ buildBarChart(
+ pfbSeriesChartEl.value.wrapper,
+ loadLastValue.value ? pfbSeries.value : pfbSeries.value.slice(0, pfbSeries.value.length - 1),
+ () => (showPfbTooltip.value = true),
+ () => (showPfbTooltip.value = false),
+ "pfb",
+ )
+ buildBarChart(
+ feeSeriesChartEl.value.wrapper,
+ loadLastValue.value ? feeSeries.value : feeSeries.value.slice(0, feeSeries.value.length - 1),
+ () => (showFeeTooltip.value = true),
+ () => (showFeeTooltip.value = false),
+ "fee",
+ )
+ buildBarChart(
+ tvlSeriesChartEl.value.wrapper,
+ loadLastValue.value ? tvlSeries.value : tvlSeries.value.slice(0, tvlSeries.value.length - 1),
+ () => (showTVLTooltip.value = true),
+ () => (showTVLTooltip.value = false),
+ "tvl",
+ )
+ }
await prepareComparisonData()
@@ -410,6 +766,16 @@ watch(
},
)
+watch(
+ () => [chartView.value, loadLastValue.value],
+ () => {
+ updateUserSettings()
+ if (!isLoading.value) {
+ buildRollupCharts(false)
+ }
+ }
+)
+
watch(
() => selectedRollup.value,
() => {
@@ -424,6 +790,13 @@ const debouncedRedraw = useDebounceFn((e) => {
buildRollupCharts()
}, 500)
+onBeforeMount(() => {
+ isLoading.value = true
+ const settings = JSON.parse(localStorage.getItem("settings"))
+ chartView.value = settings?.chart?.view || "bar"
+ loadLastValue.value = settings?.chart?.view ? settings.chart.loadLastValue : true
+})
+
onMounted(async () => {
window.addEventListener("resize", debouncedRedraw)
@@ -443,21 +816,64 @@ onBeforeUnmount(() => {
Analytics
-
-
-
-
-
-
-
- {{ period.title }}
+
+
+
+
+
+
+
+
+ {{ period.title }}
+
+
+
+
+
+
+
+
+
+
+
+ Chart view
+
+
+
+
+
+
+
+
+
+ Show last value
+
+
-
-
-
+
+
+
@@ -500,44 +916,24 @@ onBeforeUnmount(() => {
-
+
- {{
- DateTime.now()
- .minus({ months: selectedPeriod.value - 1 })
- .toFormat("LLL y")
- }}
+ {{ getXAxisLabels(true) }}
- {{ DateTime.now().toFormat("LLL") }}
-
-
- {{
- DateTime.now()
- .minus({ days: selectedPeriod.value - 1 })
- .toFormat("LLL dd")
- }}
+ {{ getXAxisLabels(false) }}
-
- Today
-
-
-
- {{ DateTime.now().minus({ hours: selectedPeriod.value }).set({ minutes: 0 }).toFormat("hh:mm a") }}
-
-
- Now
-
-
+
{
-
+
- {{
- DateTime.now()
- .minus({ months: selectedPeriod.value - 1 })
- .toFormat("LLL y")
- }}
+ {{ getXAxisLabels(true) }}
- {{ DateTime.now().toFormat("LLL") }}
-
-
- {{
- DateTime.now()
- .minus({ days: selectedPeriod.value - 1 })
- .toFormat("LLL dd")
- }}
+ {{ getXAxisLabels(false) }}
-
- Today
-
-
-
- {{ DateTime.now().minus({ hours: selectedPeriod.value }).set({ minutes: 0 }).toFormat("hh:mm a") }}
-
-
- Now
-
-
+
{
-
+
Fee Spent
@@ -712,44 +1088,24 @@ onBeforeUnmount(() => {
-
+
- {{
- DateTime.now()
- .minus({ months: selectedPeriod.value - 1 })
- .toFormat("LLL y")
- }}
+ {{ getXAxisLabels(true) }}
- {{ DateTime.now().toFormat("LLL") }}
-
-
- {{
- DateTime.now()
- .minus({ days: selectedPeriod.value - 1 })
- .toFormat("LLL dd")
- }}
+ {{ getXAxisLabels(false) }}
-
- Today
-
-
-
- {{ DateTime.now().minus({ hours: selectedPeriod.value }).set({ minutes: 0 }).toFormat("hh:mm a") }}
-
-
- Now
-
-
+
{
-
+
+
+ TVL
+
+
+
+
+
+
+
+ Grouping by day or month is only available for this chart.
+
+
+
+
+
+
+
+
+
+ {{ Math.max(...tvlSeries.map((d) => d.value)) < 1_000_000
+ ? abbreviate(Math.max(...tvlSeries.map((d) => d.value)), 0)
+ : abbreviate(Math.max(...tvlSeries.map((d) => d.value))) }} $
+
+
+
+
+ {{ Math.round(Math.max(...tvlSeries.map((d) => d.value)) / 2) < 1_000_000
+ ? abbreviate(Math.round(Math.max(...tvlSeries.map((d) => d.value)) / 2), 0)
+ : abbreviate(Math.round(Math.max(...tvlSeries.map((d) => d.value)) / 2)) }} $
+
+
+
+ 0
+
+
+
+
+
+
+ {{ getXAxisLabels(true, true) }}
+
+
+
+ {{ getXAxisLabels(false, true) }}
+
+
+
+
+
+
+
+
+
+
+ {{ badgeText }}
+
+
+
+
+
+ {{ abbreviate(tooltipText) }} $
+
+
+
+
+
+
+
+
+
+
+
+
Rollups Comparison
@@ -847,7 +1306,7 @@ onBeforeUnmount(() => {
Size
-
+
Blobs
-
+
Fee
-
+
+
+
+
+
+
+ {{ `https://defillama.com/chain/${rollup.defi_lama}` }}
+
+
+
@@ -463,7 +473,7 @@ const handleCSVDownload = async (value) => {
- {{ DateTime.fromISO(rollup.first_message_time).setLocale("en").toFormat("LLL d, t") }}
+ {{ DateTime.fromISO(rollup.first_message_time).setLocale("en").toFormat("LLL d y, t") }}
@@ -476,7 +486,7 @@ const handleCSVDownload = async (value) => {
- {{ DateTime.fromISO(rollup.last_message_time).setLocale("en").toFormat("LLL d, t") }}
+ {{ DateTime.fromISO(rollup.last_message_time).setLocale("en").toFormat("LLL d y, t") }}
diff --git a/components/modules/rollup/tables/BlobsTable.vue b/components/modules/rollup/tables/BlobsTable.vue
index f4602399..63024058 100644
--- a/components/modules/rollup/tables/BlobsTable.vue
+++ b/components/modules/rollup/tables/BlobsTable.vue
@@ -53,15 +53,15 @@ const handleViewBlob = (blob) => {
|
-
+
-
+
-
+
- {{ blob.signer }}
+ {{ blob.signer.hash }}
diff --git a/components/modules/stats/BarChart.vue b/components/modules/stats/BarChart.vue
index 3a381a4f..289b41e2 100644
--- a/components/modules/stats/BarChart.vue
+++ b/components/modules/stats/BarChart.vue
@@ -38,9 +38,9 @@ const buildChart = (chart, cData, pData, onEnter, onLeave) => {
const marginTop = 12
const marginRight = 12
const marginBottom = 24
- const marginLeft = 36
- const marginAxisX = 20
- const barWidth = Math.round(Math.max((width - marginLeft - marginRight) / (cData.data.length) - (pData.data.length ? 2 : 5)), 4)
+ const marginLeft = 48
+ const marginAxisX = 24
+ const barWidth = Math.max(Math.round((width - marginLeft - marginRight) / (cData.data.length) - (pData.data.length ? 2 : 5)), 4)
const MIN_VALUE = d3.min([...cData.data.map(s => s.value), ...pData.data?.map(s => s.value)])
const MAX_VALUE = d3.max([...cData.data.map(s => s.value), ...pData.data?.map(s => s.value)])
@@ -53,7 +53,12 @@ const buildChart = (chart, cData, pData, onEnter, onLeave) => {
const x1 = d3.scaleBand(
['prev', 'current'],
- [0, barWidth],
+ [0, barWidth / 1.1],
+ )
+
+ const scaleX = d3.scaleUtc(
+ d3.extent(cData.data, (d) => new Date(d.date)),
+ [marginLeft - barWidth / 2, width - marginRight - barWidth / 2],
)
let data = cData.data.map((d, i) => ({
@@ -96,6 +101,8 @@ const buildChart = (chart, cData, pData, onEnter, onLeave) => {
return `${tia(value, 2)} TIA`
case 'seconds':
return `${truncateDecimalPart(value / 1_000, 3)}s`
+ case 'usd':
+ return `${abbreviate(value)} $`
default:
return comma(value)
}
@@ -127,11 +134,11 @@ const buildChart = (chart, cData, pData, onEnter, onLeave) => {
svg.append("g")
.attr("transform", `translate( ${barWidth / 2 - 3}, ${height - marginAxisX} )`)
.attr("color", "var(--op-20)")
- .call(d3.axisBottom(x0).ticks(6).tickFormat(d3.timeFormat(props.series.timeframe === 'hour' ? "%H:%M" : "%b %d")))
+ .call(d3.axisBottom(scaleX).ticks(Math.min(cData.data.length, 6)).tickFormat(d3.timeFormat(props.series.timeframe === 'hour' ? "%H:%M" : "%b %d")))
.selectAll(".tick line")
.filter(function(d) { return d === 0; })
.remove();
-
+
svg.append("g")
.attr("transform", `translate(0,0)`)
.attr("color", "var(--op-20)")
@@ -254,12 +261,16 @@ const drawChart = () => {
watch(
() => [currentData.value, prevData.value],
() => {
- drawChart()
+ if (chartEl?.value?.wrapper) {
+ drawChart()
+ }
},
)
onMounted(async () => {
- drawChart()
+ if (chartEl?.value?.wrapper) {
+ drawChart()
+ }
})
diff --git a/components/modules/stats/ChartCardPreview.vue b/components/modules/stats/ChartCardPreview.vue
index 2338a65e..2e83471d 100644
--- a/components/modules/stats/ChartCardPreview.vue
+++ b/components/modules/stats/ChartCardPreview.vue
@@ -10,7 +10,7 @@ import DiffChip from "@/components/modules/stats/DiffChip.vue"
import { abbreviate, comma, formatBytes, tia } from "@/services/utils"
/** API */
-import { fetchSeries, fetchSeriesCumulative } from "@/services/api/stats"
+import { fetchSeries, fetchSeriesCumulative, fetchTVS } from "@/services/api/stats"
const props = defineProps({
series: {
@@ -39,7 +39,19 @@ const chartElPrev = ref()
const getSeries = async () => {
let data = []
-
+ let from = parseInt(
+ DateTime.now().minus({
+ days: props.period.timeframe === "day" ? props.period.value * 2 + 1 : 0,
+ hours: props.period.timeframe === "hour" ? props.period.value * 2 + 1 : 0,
+ }).ts / 1_000
+ )
+ let to = parseInt(
+ DateTime.now().minus({
+ days: props.period.timeframe === "day" ? 1 : 0,
+ hours: props.period.timeframe === "hour" ? 1 : 0,
+ }).ts / 1_000
+ )
+
if (props.series.aggregate === 'cumulative') {
data = await fetchSeriesCumulative({
name: props.series.name,
@@ -49,24 +61,18 @@ const getSeries = async () => {
days: 48,
}).ts / 1_000)
})
- // data = (await fetchSeriesCumulative({
- // name: props.series.name,
- // period: props.period.timeframe,
- // from: parseInt(
- // DateTime.now().minus({
- // days: props.period.timeframe === "day" ? props.period.value * 2 : 0,
- // hours: props.period.timeframe === "hour" ? props.period.value * 2 : 0,
- // }).ts / 1_000)
- // })).reverse()
+ } else if (props.series.name === "tvs") {
+ data = (await fetchTVS({
+ period: props.period.timeframe,
+ from: from,
+ to: to,
+ })).map(v => { return { time: v.time, value: v.close } }).reverse()
} else {
data = (await fetchSeries({
table: props.series.name,
period: props.period.timeframe,
- from: parseInt(
- DateTime.now().minus({
- days: props.period.timeframe === "day" ? props.period.value * 2 : 0,
- hours: props.period.timeframe === "hour" ? props.period.value * 2 : 0,
- }).ts / 1_000)
+ from: from,
+ to: to,
})).reverse()
}
@@ -90,7 +96,10 @@ const getSeries = async () => {
})
}
- if (props.series.aggregate !== 'cumulative') {
+ if (props.series.name === 'tvs') {
+ currentTotal.value = currentData.value[currentData.value.length - 1].value // Math.max(...currentData.value.map(d => d.value))
+ prevTotal.value = prevData.value[prevData.value.length - 1].value // Math.max(...prevTotal.value.map(d => d.value))
+ } else if (props.series.aggregate !== 'cumulative') {
currentTotal.value = currentData.value.reduce((sum, el) => {
return sum + +el.value;
}, 0);
@@ -238,6 +247,10 @@ watch(
{{ series.name === 'gas_price' ? `${currentTotal.toFixed(4)} UTIA` : `${tia(currentTotal, 2)} TIA` }}
{{ series.name === 'gas_price' ? `${prevTotal.toFixed(4)} UTIA` : `${tia(prevTotal, 2)} TIA` }}
+
+ {{ `${abbreviate(currentTotal)} $` }}
+ {{ `${abbreviate(prevTotal)} $` }}
+
{{ series.units === 'bytes' ? formatBytes(currentTotal) : comma(currentTotal) }}
{{ `${series.units === 'bytes' ? formatBytes(prevTotal) : abbreviate(prevTotal)} previous ${period.title.replace('Last ', '')}` }}
@@ -258,7 +271,11 @@ watch(
- Today
+ {{ DateTime.now().minus({
+ days: period.timeframe === "day" ? 1 : 0,
+ hours: period.timeframe === "hour" ? 1 : 0,
+ }).toFormat("LLL dd")
+ }}
diff --git a/components/modules/stats/CircularChartCard.vue b/components/modules/stats/CircularChartCard.vue
deleted file mode 100644
index db276187..00000000
--- a/components/modules/stats/CircularChartCard.vue
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/components/modules/stats/LineChart.vue b/components/modules/stats/LineChart.vue
index 78242717..68d43e68 100644
--- a/components/modules/stats/LineChart.vue
+++ b/components/modules/stats/LineChart.vue
@@ -3,15 +3,9 @@
import * as d3 from "d3"
import { DateTime } from "luxon"
-/** Stats Components */
-import DiffChip from "@/components/modules/stats/DiffChip.vue"
-
/** Services */
import { abbreviate, comma, formatBytes, tia, truncateDecimalPart } from "@/services/utils"
-/** API */
-import { fetchSeries, fetchSeriesCumulative } from "@/services/api/stats"
-
const props = defineProps({
series: {
type: Object,
@@ -83,6 +77,8 @@ const buildChart = (chart, cData, pData, onEnter, onLeave) => {
return `${tia(value, 2)} TIA`
case 'seconds':
return `${truncateDecimalPart(value / 1_000, 3)}s`
+ case 'usd':
+ return `${abbreviate(value)} $`
default:
return comma(value)
}
@@ -252,7 +248,6 @@ const buildChart = (chart, cData, pData, onEnter, onLeave) => {
const drawChart = () => {
currentData.value.color = "var(--mint)"
prevData.value.color = "var(--txt-tertiary)"
-
buildChart(
chartEl.value.wrapper,
currentData.value,
@@ -265,12 +260,16 @@ const drawChart = () => {
watch(
() => [currentData.value, prevData.value],
() => {
- drawChart()
+ if (chartEl?.value?.wrapper) {
+ drawChart()
+ }
},
)
-onMounted(async () => {
- drawChart()
+onMounted(() => {
+ if (chartEl?.value?.wrapper) {
+ drawChart()
+ }
})
diff --git a/components/modules/stats/ParallelCoordinatesChart.vue b/components/modules/stats/ParallelCoordinatesChart.vue
index 6a11e15a..a0ebe42a 100644
--- a/components/modules/stats/ParallelCoordinatesChart.vue
+++ b/components/modules/stats/ParallelCoordinatesChart.vue
@@ -234,21 +234,23 @@ const buildChart = (chart, data) => {
.style("opacity", 0.5)
.style("filter", "brightness(0.6)")
.style("transition", "all 0.5s ease")
- // .attr("stroke-linecap", "round")
.attr("stroke-linejoin", "round")
.attr("d", path )
+ .each(function () {
+ const length = this.getTotalLength();
+ if (!isNaN(length) && length > 0) {
+ d3.select(this)
+ .attr("stroke-dasharray", length)
+ .attr("stroke-dashoffset", length)
+ .transition()
+ .duration(1000)
+ .ease(d3.easeCubic)
+ .attr("stroke-dashoffset", 0);
+ }
+ })
.on('mouseover', (event, d) => highlight(d.slug))
.on('mouseleave', (event, d) => unhighlight(d.slug))
.on('click', (event, d) => selectRollup(d.slug))
- .style("stroke-dasharray", function() { return this.getTotalLength(); })
- // .style("stroke-dashoffset", function() {
- // return Math.random() > 0.5 ? this.getTotalLength() : -this.getTotalLength();
- // })
- .style("stroke-dashoffset", function() { return this.getTotalLength(); })
- .transition()
- .duration(1000)
- .ease(d3.easeCubic)
- .style("stroke-dashoffset", 0)
svg.selectAll("first-last-axis")
.data(edgeAxis).enter()
diff --git a/components/modules/stats/SquareSizeCard.vue b/components/modules/stats/SquareSizeCard.vue
index 8930d48d..a50b3040 100644
--- a/components/modules/stats/SquareSizeCard.vue
+++ b/components/modules/stats/SquareSizeCard.vue
@@ -144,7 +144,7 @@ onMounted(async () => {
/>
-
+
/** Stats Components */
+import ChartCardPreview from "@/components/modules/stats/ChartCardPreview.vue"
import PieChartCard from "@/components/modules/stats/PieChartCard.vue"
import RollupsBubbleChart from "@/components/modules/stats/RollupsBubbleChart.vue"
import RollupsActivity from "~/components/modules/stats/RollupsActivity.vue"
+/** Stats Constants */
+import { STATS_PERIODS } from "@/services/constants/stats.js"
+
/** UI */
import Button from "@/components/ui/Button.vue"
import Checkbox from "@/components/ui/Checkbox.vue"
@@ -253,7 +257,7 @@ watch(
+
+
+
+ Economics
+
+
+
+
+
+
diff --git a/components/modules/tx/TxOverview.vue b/components/modules/tx/TxOverview.vue
index 48956da5..0c2acb2a 100644
--- a/components/modules/tx/TxOverview.vue
+++ b/components/modules/tx/TxOverview.vue
@@ -206,9 +206,9 @@ const handleViewRawTransaction = () => {
Signer
-
+
-
+
diff --git a/components/modules/validator/ValidatorOverview.vue b/components/modules/validator/ValidatorOverview.vue
index 5de93aaa..c4001db3 100644
--- a/components/modules/validator/ValidatorOverview.vue
+++ b/components/modules/validator/ValidatorOverview.vue
@@ -277,9 +277,9 @@ const handleDelegate = () => {
- {{ splitAddress(validator.address) }}
+ {{ splitAddress(validator.address.hash) }}
-
+
@@ -351,8 +351,8 @@ const handleDelegate = () => {
Delegator Address
-
-
+
+
diff --git a/components/modules/validator/tables/DelegatorsTable.vue b/components/modules/validator/tables/DelegatorsTable.vue
index 78c15eef..afa182a3 100644
--- a/components/modules/validator/tables/DelegatorsTable.vue
+++ b/components/modules/validator/tables/DelegatorsTable.vue
@@ -32,13 +32,13 @@ const props = defineProps({
|
|
-
+
- {{ $getDisplayName('addresses', d.delegator) }}
+ {{ $getDisplayName('addresses', d.delegator.hash) }}
-
+
@@ -51,12 +51,12 @@ const props = defineProps({
|
-
+
|
-
+
{{ shareOfTotalString(d.amount, validator.stake) }}
diff --git a/components/shared/tables/Events.vue b/components/shared/tables/Events.vue
index d54c1124..08e868fc 100644
--- a/components/shared/tables/Events.vue
+++ b/components/shared/tables/Events.vue
@@ -677,14 +677,14 @@ watch(
-
+
- {{ splitAddress(event.data.granter.replace(/"/g,'')) }}
+ {{ splitAddress(event.data.granter.hash.replace(/"/g,'')) }}
- {{ event.data.granter.replace(/"/g,'') }}
+ {{ event.data.granter.hash.replace(/"/g,'') }}
@@ -697,28 +697,28 @@ watch(
for
-
+
- {{ splitAddress(event.data.grantee.replace(/"/g,'')) }}
+ {{ splitAddress(event.data.grantee.hash.replace(/"/g,'')) }}
- {{ event.data.grantee.replace(/"/g,'') }}
+ {{ event.data.grantee.hash.replace(/"/g,'') }}
-
+
{{ splitAddress(event.data.granter.replace(/"/g,'')) }}
- {{ event.data.granter.replace(/"/g,'') }}
+ {{ event.data.granter.hash.replace(/"/g,'') }}
@@ -731,42 +731,42 @@ watch(
from
-
+
- {{ splitAddress(event.data.grantee.replace(/"/g,'')) }}
+ {{ splitAddress(event.data.grantee.hash.replace(/"/g,'')) }}
- {{ event.data.grantee.replace(/"/g,'') }}
+ {{ event.data.grantee.hash.replace(/"/g,'') }}
-
+
- {{ splitAddress(event.data.granter) }}
+ {{ splitAddress(event.data.granter.hash) }}
- {{ event.data.granter }}
+ {{ event.data.granter.hash }}
grants fee allowances to
-
+
- {{ splitAddress(event.data.grantee) }}
+ {{ splitAddress(event.data.grantee.hash) }}
- {{ event.data.grantee }}
+ {{ event.data.grantee.hash }}
diff --git a/components/widgets/BlobsWidget.vue b/components/widgets/BlobsWidget.vue
index 351f379e..1371d43e 100644
--- a/components/widgets/BlobsWidget.vue
+++ b/components/widgets/BlobsWidget.vue
@@ -112,7 +112,7 @@ const selectDay = (d) => {
@click="selectDay(day)"
:class="[$style.day, day?.value > 0 && $style.shadow]"
:style="{
- background: parseInt(day?.value) > 0 ? `rgb(10, 219, 111)` : 'var(--op-10)',
+ background: parseInt(day?.value) > 0 ? `var(--brand)` : 'var(--op-10)',
opacity: calculateOpacity(day?.value),
}"
/>
diff --git a/components/widgets/BlockWidget.vue b/components/widgets/BlockWidget.vue
index c98b7cf2..95d879dd 100644
--- a/components/widgets/BlockWidget.vue
+++ b/components/widgets/BlockWidget.vue
@@ -110,7 +110,7 @@ onBeforeUnmount(() => {
Block
- {{ comma(lastBlock.height) }}
+ {{ comma(lastBlock.height) }}
@@ -253,7 +253,9 @@ onBeforeUnmount(() => {
width: 100%;
- background: var(--block-progress-fill-background);
+ /* background: var(--dark-mint); */
+ background: var(--neutral-mint);
+ /* background: var(--block-progress-fill-background); */
z-index: -1;
diff --git a/components/widgets/GasWidget.vue b/components/widgets/GasWidget.vue
index bf84b309..e92581ed 100644
--- a/components/widgets/GasWidget.vue
+++ b/components/widgets/GasWidget.vue
@@ -119,7 +119,8 @@ onMounted(async () => {
padding: 0 12px;
&.fast {
- background: linear-gradient(rgba(10, 219, 111, 25%), rgba(10, 219, 111, 10%));
+ /* background: linear-gradient(rgba(10, 219, 111, 25%), rgba(10, 219, 111, 10%)); */
+ background: linear-gradient(var(--dark-mint), rgba(10, 219, 111, 10%));
box-shadow: inset 0 0 0 1px rgba(10, 219, 111, 50%);
}
diff --git a/components/widgets/StakingWidget.vue b/components/widgets/StakingWidget.vue
index e0de18c5..76ce59e3 100644
--- a/components/widgets/StakingWidget.vue
+++ b/components/widgets/StakingWidget.vue
@@ -35,7 +35,7 @@ const validatorsGraph = ref([
title: "active",
count: 0,
width: 0,
- color: "var(--validator-active)",
+ color: "var(--brand)",
},
{
title: "inactive",
@@ -253,7 +253,7 @@ onMounted(() => {
border-radius: 2px;
- background: linear-gradient(90deg, var(--staking) var(--percentStaking), var(--supply) var(--percentStaking));
+ background: linear-gradient(90deg, var(--brand) var(--percentStaking), var(--supply) var(--percentStaking));
}
.validator_bar {
diff --git a/components/widgets/TransactionsWidget.vue b/components/widgets/TransactionsWidget.vue
index 3ceba6dd..45c13d57 100644
--- a/components/widgets/TransactionsWidget.vue
+++ b/components/widgets/TransactionsWidget.vue
@@ -223,7 +223,7 @@ const getSectorName = (item) => {
.hour.current {
.bar {
- background: var(--blue);
+ background: var(--supply);
animation: blink 1.5s ease infinite;
}
}
@@ -249,7 +249,7 @@ const getSectorName = (item) => {
background: var(--txt-tertiary);
&.green {
- background: var(--green);
+ background: var(--brand);
}
}
diff --git a/package.json b/package.json
index dc279b12..64853ccb 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"@keplr-wallet/cosmos": "^0.12.70",
"@openzeppelin/merkle-tree": "^1.0.6",
"@pinia/nuxt": "0.4.11",
+ "@sentry/vue": "^8.50.0",
"@vueuse/core": "^10.9.0",
"buffer": "^6.0.3",
"codemirror": "^6.0.1",
diff --git a/pages/addresses.vue b/pages/addresses.vue
index 165a2d0d..1c27779e 100644
--- a/pages/addresses.vue
+++ b/pages/addresses.vue
@@ -243,7 +243,7 @@ const handleLast = async () => {
-
+
diff --git a/pages/blob.vue b/pages/blob.vue
index 35f39738..5d1d0b9f 100644
--- a/pages/blob.vue
+++ b/pages/blob.vue
@@ -3,7 +3,7 @@
import { DateTime } from "luxon"
/** API */
-import { fetchBlobBlockscoutData, fetchBlobMetadata, fetchBlobByMetadata } from "@/services/api/namespace"
+import { fetchBlobBlockscoutData, fetchBlobByMetadata, fetchBlobMetadata, fetchBlobProof } from "@/services/api/namespace"
/** Components */
import HexViewer from "@/components/modules/blob/HexViewer.vue"
@@ -19,7 +19,6 @@ import { space, formatBytes, comma, strToHex } from "@/services/utils"
import { useCacheStore } from "@/store/cache"
import { useModalsStore } from "@/store/modals"
import { useNotificationsStore } from "@/store/notifications"
-import { blockscoutURL } from "~/services/config"
const cacheStore = useCacheStore()
const modalsStore = useModalsStore()
const notificationsStore = useNotificationsStore()
@@ -101,7 +100,7 @@ cacheStore.current.blob = {
}
onMounted(() => {
- if (!supportedContentTypeForPreview.includes(blob.content_type)) cards.value.preview = false
+ if (!supportedContentTypeForPreview.includes(blob.value?.content_type)) cards.value.preview = false
innerWidth.value = window.innerWidth
if (innerWidth.value <= 1020) {
@@ -178,17 +177,40 @@ const handleDownload = () => {
.map((e) => parseInt(e, 16)),
)
+ let extension = "bin"
+ if (supportedContentTypeForPreview.includes(blob.value?.content_type)) {
+ const ct = blob.value.content_type.split(";")[0].split("/")[1]
+ extension = ct === "plain" ? "txt" : ct
+ }
+
const a = window.document.createElement("a")
a.href = window.URL.createObjectURL(new Blob([byteArray], { type: "application/octet-stream" }))
a.download = `${metadata.value.namespace.namespace_id}_${blob.value.commitment.slice(
blob.value.commitment.length - 8,
blob.value.commitment.length,
- )}.bin`
+ )}.${extension}`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
}
+const handleViewProof = async () => {
+ const { data } = await fetchBlobProof({
+ hash: hash.replaceAll(" ", "+"),
+ height: parseInt(height),
+ commitment: commitment.replaceAll(" ", "+"),
+ })
+
+ if (!data.value) {
+ cacheStore.current.proof = "Failed to load proof.."
+ } else {
+ cacheStore.current.proof = data.value
+ }
+
+ cacheStore.current._target = "proof"
+ modalsStore.open("rawData")
+}
+
const handleCopy = (text) => {
window.navigator.clipboard.writeText(text)
@@ -222,6 +244,10 @@ const handleCopy = (text) => {
View batch
+
-
+
Signer
- {{ metadata.signer }}
+ {{ $getDisplayName("addresses", "", metadata.signer) }}
diff --git a/pages/privacy-policy.vue b/pages/privacy-policy.vue
index 2cc5c44c..c8207e7a 100644
--- a/pages/privacy-policy.vue
+++ b/pages/privacy-policy.vue
@@ -75,6 +75,9 @@ useHead({
Terminal device ID, type, operating system, date, time, region, country, latitude, longitude
Browser type, user agent, language, screen resolution
IP address, network provider, traffic routing data, system configuration information, and other information about traffic to and from the Website
+ Mouse movements, clicks, scrolling behavior
+ Keystrokes (excluding sensitive fields such as passwords)
+ Pages visited, time spent, and navigation paths
Any collection and processing of this technical data is for the purpose of enabling the use and functionality of our Website, continuously ensuring system security and stability, optimising our Website, to show you Website customized for your region, and for internal statistical purposes. Furthermore, the collection of technical data shall ensure the functionality and security of our services.
diff --git a/pages/stats/[metric].vue b/pages/stats/[metric].vue
index ad1014f7..8b1c84b3 100644
--- a/pages/stats/[metric].vue
+++ b/pages/stats/[metric].vue
@@ -3,7 +3,7 @@
import { DateTime } from "luxon"
/** Stats Components/Constants */
-import { getSeriesByPage, STATS_PERIODS } from "@/services/constants/stats.js"
+import { getSeriesByPage, STATS_PERIODS, STATS_TIMEFRAMES } from "@/services/constants/stats.js"
import BarChart from "@/components/modules/stats/BarChart.vue"
import LineChart from "@/components/modules/stats/LineChart.vue"
import SquareSizeChart from "@/components/modules/stats/SquareSizeChart.vue"
@@ -14,7 +14,7 @@ import { exportSVGToPNG, exportToCSV } from "@/services/utils/export"
import { capitalizeAndReplaceUnderscore } from "@/services/utils"
/** API */
-import { fetchSeries, fetchSeriesCumulative } from "@/services/api/stats"
+import { fetchSeries, fetchSeriesCumulative, fetchTVS } from "@/services/api/stats"
/** UI */
import Button from "@/components/ui/Button.vue"
@@ -23,13 +23,14 @@ import Popover from "@/components/ui/Popover.vue"
import Toggle from "@/components/ui/Toggle.vue"
/** Store */
-/** Store */
+import { useCacheStore } from "@/store/cache"
import { useModalsStore } from "@/store/modals"
import { useNotificationsStore } from "@/store/notifications"
-import { useCacheStore } from "@/store/cache"
+import { useSettingsStore } from "@/store/settings"
+const cacheStore = useCacheStore()
const modalsStore = useModalsStore()
const notificationsStore = useNotificationsStore()
-const cacheStore = useCacheStore()
+const settingsStore = useSettingsStore()
const route = useRoute()
const router = useRouter()
@@ -40,7 +41,10 @@ if (!series.value.page) {
router.push("/stats")
}
-const metricName = computed(() => capitalizeAndReplaceUnderscore(series.value?.page))
+const metricName = computed(() => {
+ if (series.value.page === "tvs") return series.value.title
+ return capitalizeAndReplaceUnderscore(series.value?.page)
+})
// defineOgImage({
// title: "Rollup",
@@ -96,12 +100,50 @@ useHead({
const periods = ref(STATS_PERIODS)
const selectedPeriod = ref(periods.value[2])
+const selectedTimeframe = ref(STATS_TIMEFRAMES.find(tf => tf.timeframe === (series.value.name === "tvs" ? "day" : selectedPeriod.value.timeframe)))
+const timeframes = computed(() => {
+ let res = []
+
+ for (const tf of STATS_TIMEFRAMES) {
+ const pointCount = Math.floor(DateTime.fromSeconds(filters.to).diff(DateTime.fromSeconds(filters.from), `${tf.timeframe}s`)[`${tf.timeframe}s`]) + 1
+
+ if (pointCount > 1 && pointCount < 100) {
+ res.push(tf)
+ }
+ }
+
+ if (series.value.name === "tvs") {
+ res = res.filter(tf => tf.timeframe === "day" || tf.timeframe === "month")
+ }
+
+ return res
+})
+const timeframesStyles = computed(() => {
+ const len = timeframes.value.length
+ if (!len) return { background: "var(--op-5)" }
+
+ const segment = 100 / len
+ const index = timeframes.value.findIndex(tf => tf.timeframe === selectedTimeframe.value.timeframe)
+ const start = segment * index
+ const end = start + segment
+
+ return { background: `linear-gradient(to right, transparent ${start}%, var(--op-5) ${start}%, var(--op-5) ${end}%, transparent ${end}%)` }
+})
+
const currentData = ref([])
const prevData = ref([])
-const chartView = ref('line')
+const chartView = ref("line")
const loadPrevData = ref(true)
const loadLastValue = ref(true)
+const updateUserSettings = () => {
+ settingsStore.chart = {
+ ...settingsStore.chart,
+ view: chartView.value,
+ loadPrevData: loadPrevData.value,
+ loadLastValue: loadLastValue.value,
+ }
+}
const filters = reactive({})
@@ -109,9 +151,9 @@ const setDefaultFilters = () => {
filters.timeframe = selectedPeriod.value.timeframe
filters.periodValue = selectedPeriod.value.value
filters.from = parseInt(DateTime.now().startOf('day').minus({
- days: selectedPeriod.value.timeframe === "day" ? selectedPeriod.value.value - 1 : 0, // ??
- hours: selectedPeriod.value.timeframe === "hour" ? selectedPeriod.value.value : 0,
- }).ts / 1_000)
+ days: selectedPeriod.value.timeframe === "day" ? selectedPeriod.value.value - 1 : 0,
+ hours: selectedPeriod.value.timeframe === "hour" ? selectedPeriod.value.value : 0,
+ }).ts / 1_000)
filters.to = parseInt(DateTime.now().endOf('day').ts / 1_000)
}
@@ -126,42 +168,108 @@ const handleChangeChartView = () => {
}
const isLoading = ref(false)
-const getData = async () => {
- isLoading.value = true
-
+const fetchData = async (from, to) => {
let data = []
-
- if (series.value.aggregate !== 'cumulative') {
+ if (series.value.name === "tvs") {
+ data = (await fetchTVS({
+ period: selectedTimeframe.value.timeframe,
+ from: from
+ ? from
+ : loadPrevData.value
+ ? parseInt(DateTime.fromSeconds(filters.from).minus({
+ hours: filters.timeframe === "hour" ? filters.periodValue : 0,
+ days: filters.timeframe === "day" ? filters.periodValue : 0,
+ weeks: filters.timeframe === "week" ? filters.periodValue : 0,
+ months: filters.timeframe === "month" ? filters.periodValue : 0,
+ }).ts / 1_000)
+ : filters.from,
+ to: to ? to : filters.to
+ })).map(v => { return { time: v.time, value: v.close } })
+ } else if (series.value.aggregate !== 'cumulative') {
data = (await fetchSeries({
table: series.value.name,
- period: filters.timeframe,
- from: loadPrevData.value ? parseInt(DateTime.fromSeconds(filters.from).minus({
- hours: filters.timeframe === "hour" ? filters.periodValue : 0,
- days: filters.timeframe === "day" ? filters.periodValue : 0,
- weeks: filters.timeframe === "week" ? filters.periodValue : 0,
- }).ts / 1_000) : filters.from,
- to: filters.to
- })).reverse()
+ period: selectedTimeframe.value.timeframe,
+ from: from
+ ? from
+ : loadPrevData.value
+ ? parseInt(DateTime.fromSeconds(filters.from).minus({
+ hours: filters.timeframe === "hour" ? filters.periodValue : 0,
+ days: filters.timeframe === "day" ? filters.periodValue : 0,
+ weeks: filters.timeframe === "week" ? filters.periodValue : 0,
+ }).ts / 1_000)
+ : filters.from,
+ to: to ? to : filters.to
+ }))
} else {
- data = await fetchSeriesCumulative({
+ data = (await fetchSeriesCumulative({
name: series.value.name,
- period: filters.timeframe,
+ period: selectedTimeframe.value.timeframe,
from: loadPrevData.value ? parseInt(DateTime.fromSeconds(filters.from).minus({
hours: filters.timeframe === "hour" ? filters.periodValue : 0,
days: filters.timeframe === "day" ? filters.periodValue : 0,
weeks: filters.timeframe === "week" ? filters.periodValue : 0,
}).ts / 1_000) : filters.from,
to: filters.to
- })
+ })).reverse()
}
+ return data
+}
+const getData = async () => {
+ isLoading.value = true
+
+ let data = await fetchData()
if (data.length) {
if (loadPrevData.value) {
- prevData.value = data.slice(0, filters.periodValue).map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) }))
- currentData.value = data.slice(filters.periodValue, data.length).map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) }))
+ if (selectedTimeframe.value.timeframe !== filters.timeframe) {
+ if (data.length % 2 > 0) {
+ const from = parseInt(DateTime.fromISO(data[data.length - 1].time)
+ .minus({
+ hours: selectedTimeframe.value.timeframe === "hour" ? 1 : 0,
+ days: selectedTimeframe.value.timeframe === "day" ? 1 : 0,
+ weeks: selectedTimeframe.value.timeframe === "week" ? 1 : 0,
+ months: selectedTimeframe.value.timeframe === "month" ? 1 : 0
+ }).ts / 1_000
+ )
+ const to = parseInt(DateTime.fromISO(data[data.length - 1].time).ts / 1_000)
+ let addData = await fetchData(from, to)
+ if (!addData.length) {
+ addData = [{
+ date: DateTime.fromISO(data[data.length - 1].time)
+ .minus({
+ hours: filters.timeframe === "hour" ? 1 : 0,
+ days: filters.timeframe === "day" ? 1 : 0,
+ weeks: filters.timeframe === "week" ? 1 : 0,
+ })
+ .toJSDate(),
+ value: 0,
+ }]
+ }
+ data.push(addData[0])
+ }
+
+ currentData.value = data.slice(0, data.length / 2).map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) })).reverse()
+ prevData.value = data.slice(data.length / 2, data.length).map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) })).reverse()
+ } else {
+ currentData.value = data.slice(0, filters.periodValue).map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) })).reverse()
+ prevData.value = data.slice(filters.periodValue, data.length).map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) })).reverse()
+ while (prevData.value.length < currentData.value.length) {
+ prevData.value.unshift({
+ date: DateTime.fromJSDate(prevData.value[0]?.date)
+ .minus({
+ hours: filters.timeframe === "hour" ? 1 : 0,
+ days: filters.timeframe === "day" ? 1 : 0,
+ weeks: filters.timeframe === "week" ? 1 : 0,
+ })
+ .toJSDate(),
+ value: 0
+ })
+ }
+ }
} else {
+ // currentData.value = data.slice(0, filters.periodValue).map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) })).reverse()
+ currentData.value = data.map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) })).reverse()
prevData.value = []
- currentData.value = data.slice(0, filters.periodValue).map((s) => ({ date: DateTime.fromISO(s.time).toJSDate(), value: parseFloat(s.value) }))
}
}
@@ -185,21 +293,48 @@ const handleClose = () => {
}
const handleUpdateDate = async (event) => {
+ isLoading.value = true
+
if (event.from && event.to) {
- let daysDiff = Math.round(DateTime.fromSeconds(event.to).diff(DateTime.fromSeconds(event.from), 'days').days)
- if (daysDiff < 7) {
- filters.timeframe = 'hour'
- filters.periodValue = Math.round(DateTime.fromSeconds(event.to).diff(DateTime.fromSeconds(event.from), 'hours').hours)
- } else if (daysDiff < 50) {
- filters.timeframe = 'day'
- filters.periodValue = daysDiff
+ let from = event.from
+ let to = event.to
+
+ let daysDiff = Math.round(DateTime.fromSeconds(to).diff(DateTime.fromSeconds(from), 'days').days)
+ if (series.value.name === "tvs") {
+ if (daysDiff < 50) {
+ filters.timeframe = 'day'
+ filters.periodValue = daysDiff
+ } else {
+ filters.timeframe = 'month'
+ filters.periodValue = Math.ceil(daysDiff / 30)
+ }
} else {
- filters.timeframe = 'week'
- filters.periodValue = Math.round(daysDiff / 7)
+ if (daysDiff < 7) {
+ filters.timeframe = 'hour'
+ filters.periodValue = Math.round(DateTime.fromSeconds(to).diff(DateTime.fromSeconds(from), 'hours').hours)
+ } else if (daysDiff < 50) {
+ filters.timeframe = 'day'
+ filters.periodValue = daysDiff
+ } else {
+ filters.timeframe = 'week'
+ filters.periodValue = Math.ceil(daysDiff / 7)
+ }
+ }
+
+ if (filters.timeframe === 'hour') {
+ const hoursDiff = Math.round(DateTime.fromSeconds(Math.min(to, DateTime.now().ts / 1_000)).diff(DateTime.fromSeconds(from), 'hours').hours)
+ if (hoursDiff < filters.periodValue) {
+ from = parseInt(
+ DateTime.fromSeconds(Math.min(to, DateTime.now().ts / 1_000))
+ .minus({ hours: filters.periodValue })
+ .ts / 1_000
+ )
+ }
}
- filters.from = event.from
- filters.to = event.to
+ filters.from = from
+ filters.to = to
+ selectedTimeframe.value = timeframes.value.find(tf => tf.timeframe === filters.timeframe)
await getData()
} else if (event.clear) {
@@ -209,6 +344,10 @@ const handleUpdateDate = async (event) => {
}
}
+const handleTimeframeUpdate = (tf) => {
+ selectedTimeframe.value = tf
+}
+
const handleCSVDownload = async () => {
let data = [...series.value.currentData, ...series.value.prevData]
let csvHeaders = 'timestamp,value\n'
@@ -278,6 +417,29 @@ watch(
}
},
)
+
+watch(
+ () => selectedTimeframe.value,
+ async () => {
+ if (!isLoading.value) {
+ await getData()
+ }
+ }
+)
+
+watch(
+ () => [chartView.value, loadLastValue.value, loadPrevData.value],
+ () => {
+ updateUserSettings()
+ }
+)
+
+onBeforeMount(() => {
+ const settings = JSON.parse(localStorage.getItem("settings"))
+ chartView.value = settings?.chart?.view || "line"
+ loadPrevData.value = settings?.chart?.loadPrevData
+ loadLastValue.value = settings?.chart?.loadLastValue
+})
@@ -349,6 +511,28 @@ watch(
Show last value
+
+
+ Group by
+
+
+
+ {{ tf.shortTitle }}
+
+
+
@@ -401,6 +585,18 @@ watch(
transition: all 1s ease-in-out;
}
+.groupping_selector {
+ padding: 4px 6px 4px 6px;
+ box-shadow: inset 0 0 0 1px var(--op-10);
+ border-radius: 5px;
+ cursor: pointer;
+ transition: all 1s ease-in-out;
+
+ & .item {
+ padding: 2px;
+ }
+}
+
.disabled {
opacity: 0.3;
pointer-events: none;
diff --git a/pages/txs.vue b/pages/txs.vue
index dfb65072..bc95fa64 100644
--- a/pages/txs.vue
+++ b/pages/txs.vue
@@ -111,19 +111,22 @@ const handleClearAllFilters = () => {
const searchTerm = ref("")
/** Parse route query */
-Object.keys(route.query).forEach((key) => {
- if (key === "page") return
-
- if (key === "from" || key === "to") {
- filters[key] = route.query[key]
- } else if (route.query[key].split(",").length) {
- route.query[key].split(",").forEach((item) => {
- filters[key][item] = true
- })
- } else {
- filters[key][route.query[key]] = true
- }
-})
+const parseRouteQuery = () => {
+ Object.keys(route.query).forEach((key) => {
+ if (key === "page") return
+
+ if (key === "from" || key === "to") {
+ filters[key] = route.query[key]
+ } else if (route.query[key].split(",").length) {
+ route.query[key].split(",").forEach((item) => {
+ filters[key][item] = true
+ })
+ } else {
+ filters[key][route.query[key]] = true
+ }
+ })
+}
+parseRouteQuery()
const updateRouteQuery = () => {
router.replace({
@@ -140,6 +143,7 @@ const updateRouteQuery = () => {
.join(","),
...(filters.from ? { from: filters.from } : {}),
...(filters.to ? { to: filters.to } : {}),
+ page: page.value,
},
})
}
@@ -223,10 +227,15 @@ const handleUpdateDateFilter = (event) => {
const resetFilters = (target, refetch) => {
if (target === "from" || target === "to") {
filters[target] = ""
- } else {
+ } else if (target) {
Object.keys(filters[target]).forEach((f) => {
filters[target][f] = false
})
+ } else {
+ resetFilters("from")
+ resetFilters("to")
+ resetFilters("status")
+ resetFilters("message_type")
}
if (refetch) {
@@ -274,8 +283,9 @@ const sort = reactive({
dir: "desc",
})
-const page = ref(route.query.page ? parseInt(route.query.page) : 1)
-const pages = computed(() => Math.ceil(count.value / 20))
+const page = ref(route.query.page ? parseInt(route.query.page) < 1 ? 1 : parseInt(route.query.page) : 1)
+const limit = ref(20)
+const handleNextCondition = ref(false)
const findPFB = ref(false)
@@ -283,7 +293,7 @@ const getTransactions = async () => {
isRefetching.value = true
const { data } = await fetchTransactions({
- limit: 20,
+ limit: limit.value,
offset: (page.value - 1) * 20,
sort: sort.dir,
sort_by: sort.by,
@@ -302,11 +312,12 @@ const getTransactions = async () => {
})
transactions.value = data.value
+ handleNextCondition.value = transactions.value.length < limit.value
isLoaded.value = true
isRefetching.value = false
}
-getTransactions()
+await getTransactions()
onBeforeMount(() => {
if (localStorage.getItem("page:transactions:config:columns")) {
@@ -315,18 +326,33 @@ onBeforeMount(() => {
})
/** Refetch transactions */
+watch(
+ () => route.query,
+ async () => {
+ if (!isRefetching.value) {
+ if (Object.keys(route.query).length) {
+ parseRouteQuery()
+ } else {
+ resetFilters()
+ }
+
+ await getTransactions()
+ }
+ },
+)
+
watch(
() => page.value,
async () => {
- getTransactions()
- router.replace({ query: { page: page.value } })
+ await getTransactions()
+ updateRouteQuery()
},
)
watch(
() => findPFB.value,
- () => {
- getTransactions()
+ async () => {
+ await getTransactions()
},
)
@@ -337,7 +363,7 @@ watch(
}
)
-const handleSort = (by) => {
+const handleSort = async (by) => {
/** temp. only for time */
if (!["time"].includes(by)) return
@@ -358,7 +384,7 @@ const handleSort = (by) => {
page.value = 1
}
- getTransactions()
+ await getTransactions()
}
const handlePrev = () => {
@@ -401,7 +427,7 @@ const handleNext = () => {
Page {{ comma(page) }}
- | |