Skip to content

Commit

Permalink
feat: Transactions trend chart (#503)
Browse files Browse the repository at this point in the history
  • Loading branch information
janmichek committed Oct 10, 2023
1 parent ce6a432 commit 1fe86ec
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 3 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"axios": "^1.2.1",
"bignumber.js": "9.1.1",
"camelcase-keys-deep": "^0.1.0",
"chart.js": "^4.4.0",
"compression": "^1.7.4",
"core-js": "^3.26.1",
"express": "^4.18.2",
Expand All @@ -44,6 +45,7 @@
"pinia": "^2.0.32",
"swiper": "^8.4.5",
"vue": "^3.3.4",
"vue-chartjs": "^5.2.0",
"vue-multiselect": "^3.0.0-beta.1",
"vue-router": "^4.1.6"
},
Expand Down
1 change: 1 addition & 0 deletions src/components/AppTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ function selectTab(tabIndex) {
border-radius: 4px;
margin-bottom: 3px;
letter-spacing: 0.003em;
transition: none;
@media (--desktop) {
font-size: 16px;
Expand Down
50 changes: 50 additions & 0 deletions src/components/TransactionsChartControls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<template>
<div class="transactions-chart-controls">
<app-chip
v-for="(button, index) in buttons"
:key="index"
class="transactions-chart-controls__button"
:variant="selectedIndex === index ? 'error' : 'secondary'"
@click="select(index)">
{{ button.label }}
</app-chip>
</div>
</template>

<script setup>
const buttons = [
{ interval: 'day', limit: '7', label: '1W' },
{ interval: 'day', limit: '30', label: '1M' },
{ interval: 'day', limit: '90', label: '3M' },
{ interval: 'month', limit: '12', label: '1Y' },
{ interval: 'month', limit: '100', label: 'ALL' },
]
const selectedIndex = ref(0)
function select(value) {
selectedIndex.value = value
emit('selected', buttons[value])
}
const emit = defineEmits(['selected'])
</script>

<style scoped>
.transactions-chart-controls {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
column-gap: 8px;
@media (--desktop) {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
}
&__button {
display: inline-flex;
justify-content: center;
cursor: pointer;
}
}
</style>
167 changes: 167 additions & 0 deletions src/components/TransactionsChartPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<template>
<app-panel>
<template #heading>
TOTAL TRANSACTIONS
</template>
<template #header>
<transactions-chart-controls
class="transactions-chart-panel__controls"
@selected="loadTransactionsStatistics"/>
</template>

<div class="transactions-chart-panel__container">
<Line
:options="chartOptions"
:data="chartData"/>
</div>

<transactions-chart-controls
class="transactions-chart-panel__controls--condensed"
@selected="loadTransactionsStatistics"/>
</app-panel>
</template>

<script setup>
import { Line } from 'vue-chartjs'
import {
CategoryScale,
Chart as ChartJS,
Legend,
LinearScale,
LineElement,
PointElement,
Title,
Tooltip,
} from 'chart.js'
import { storeToRefs } from 'pinia'
import { DateTime } from 'luxon'
import { useTransactionsStore } from '@/stores/transactions'
import TransactionsChartControls from '@/components/TransactionsChartControls'
const transactionsStore = useTransactionsStore()
const {
transactionsStatistics,
} = storeToRefs(transactionsStore)
const { fetchTransactionsStatistics } = transactionsStore
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
)
ChartJS.defaults.font.family = 'Roboto Mono'
const chartData = computed(() => {
return {
labels: labels.value,
datasets: [{
data: stats.value,
label: null,
cubicInterpolationMode: 'monotone',
tension: 0.4,
borderColor: '#f5274e',
backgroundColor: '#f5274e',
pointRadius: 3,
pointHitRadius: 20,
}],
}
})
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
},
tooltip: {
tooltip: {
position: 'top',
},
callbacks: {
title: function(context) {
return context.label
},
},
},
},
scales: {
y: {
border: {
display: false,
},
},
x: {
grid: {
color: function() {
return 'transparent'
},
},
},
},
}
const selectedInterval = ref('')
await useAsyncData(async() => {
await fetchTransactionsStatistics()
return true
})
const stats = computed(() => {
return transactionsStatistics.value?.map(stat => {
return stat.count
})
})
const labels = computed(() => {
return transactionsStatistics.value?.map(stat => {
return formatLabel(stat.startDate)
})
})
function formatLabel(label) {
const date = DateTime.fromISO(label)
if (selectedInterval.value === 'month') {
return date.toFormat('yyyy-MM')
}
return date.toFormat('MM-dd')
}
async function loadTransactionsStatistics({ interval, limit }) {
selectedInterval.value = interval
await fetchTransactionsStatistics(`?limit=${parseInt(limit) + 1}&interval_by=${interval}`)
}
</script>
<style scoped>
.transactions-chart-panel {
&__container {
position: relative;
height: 250px;
}
&__controls {
display: none;
@media (--desktop) {
display: grid;
}
}
&__controls--condensed {
margin-top: var(--space-4);
@media (--desktop) {
display: none;
}
}
}
</style>
21 changes: 18 additions & 3 deletions src/pages/transactions/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,35 @@

<page-header>
Transactions

<template #tooltip>
{{ transactionsHints.transaction }}
</template>
</page-header>

<transactions-panel v-if="!isLoading"/>
<template v-if="!isLoading">
<transactions-chart-panel class="transactions-panel"/>
<transactions-panel class="transactions-panel"/>
</template>
<loader-panel v-else/>
</template>

<script setup>
import TransactionsPanel from '@/components/TransactionsPanel'
import PageHeader from '@/components/PageHeader'
import { transactionsHints } from '@/utils/hints/transactionsHints'
import TransactionsChartPanel from '@/components/TransactionsChartPanel'
const { isLoading } = useLoading()
</script>

<style scoped>
.transactions-panel {
margin-bottom: var(--space-4);
@media (--desktop) {
margin-bottom: var(--space-6);
}
&:last-child {
margin-bottom: 0;
}
}
</style>
11 changes: 11 additions & 0 deletions src/stores/transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,40 @@ export const useTransactionsStore = defineStore('transactions', () => {

const rawTransactions = ref(null)
const transactionsCount = ref(null)
const transactionsStatistics = ref(null)

const transactions = computed(() =>
rawTransactions.value
? adaptTransactions(rawTransactions.value)
: null,
)

async function fetchTransactions(queryParameters = null) {
rawTransactions.value = null
const { data } = await axios.get(`${MIDDLEWARE_URL}${queryParameters || '/v2/txs?limit=10'}`)
rawTransactions.value = data
}

async function fetchTransactionsCount(txType = null) {
transactionsCount.value = null
const url = txType ? `${MIDDLEWARE_URL}/v2/txs/count?tx_type=${txType}` : `${MIDDLEWARE_URL}/v2/txs/count`
const { data } = await axios.get(url)
transactionsCount.value = data
}

async function fetchTransactionsStatistics(slug) {
transactionsStatistics.value = null
const { data } = await axios.get(`${MIDDLEWARE_URL}/v3/statistics/transactions${slug || '?limit=8&interval_by=day'}`)
transactionsStatistics.value = data.data.slice(1).reverse()
}

return {
rawTransactions,
transactionsCount,
transactions,
fetchTransactions,
fetchTransactionsCount,
transactionsStatistics,
fetchTransactionsStatistics,
}
})
17 changes: 17 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,11 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"

"@kurkle/color@^0.3.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f"
integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==

"@ledgerhq/devices@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.0.0.tgz#8fe9f9e442e28b7a20bcdf4c2eed06ce7b8f76ae"
Expand Down Expand Up @@ -2582,6 +2587,13 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==

chart.js@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.0.tgz#df843fdd9ec6bd88d7f07e2b95348d221bd2698c"
integrity sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==
dependencies:
"@kurkle/color" "^0.3.0"

chokidar@^3.5.1, chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
Expand Down Expand Up @@ -8126,6 +8138,11 @@ vue-bundle-renderer@^1.0.3:
dependencies:
ufo "^1.1.1"

vue-chartjs@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/vue-chartjs/-/vue-chartjs-5.2.0.tgz#3d0076ccf8016d1bf8fab5ccd837e7fb81005ded"
integrity sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==

vue-demi@*:
version "0.13.11"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99"
Expand Down

0 comments on commit 1fe86ec

Please sign in to comment.