Skip to content

Commit

Permalink
feat: Transactions chart page (#719)
Browse files Browse the repository at this point in the history
  • Loading branch information
janmichek authored May 6, 2024
1 parent b650cdf commit ba1f59e
Show file tree
Hide file tree
Showing 19 changed files with 321 additions and 146 deletions.
56 changes: 35 additions & 21 deletions src/components/AppSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
:disabled="disabled"
:searchable="searchable"
:hide-selected="hideSelected"
:preselect-first="preselectFirst">
:preselect-first="preselectFirst"
:class="[
size ? `multiselect--${size}` : null,
]">
<template
v-for="(_, slot) of $slots"
#[slot]="scope">
Expand Down Expand Up @@ -63,6 +66,11 @@ const props = defineProps({
type: Boolean,
default: false,
},
size: {
type: String,
default: null,
validator: val => ['sm'].includes(val),
},
modelValue: undefined,
})
const emit = defineEmits(['update:modelValue'])
Expand All @@ -89,13 +97,9 @@ const selectedValue = useVModel(props, 'modelValue', emit)
&__single {
color: var(--color-midnight);
font-size: 16px;
font-size: 14px;
line-height: 20px;
padding-left: var(--space-0);
@media (--desktop) {
font-size: 14px;
}
}
&__content-wrapper {
Expand All @@ -110,19 +114,14 @@ const selectedValue = useVModel(props, 'modelValue', emit)
}
&__option {
height: 36px;
display: flex;
align-items: center;
border: solid var(--color-midnight-35);
border-width: 1px 0;
color: solid var(--color-midnight);
font-size: 12px;
font-size: 14px;
line-height: 20px;
@media (--desktop) {
font-size: 14px;
}
&:last-child {
border-width: 1px 0 0;
}
Expand All @@ -142,15 +141,12 @@ const selectedValue = useVModel(props, 'modelValue', emit)
}
&__input {
font-size: 16px;
font-size: 14px;
line-height: 20px;
@media (--desktop) {
font-size: 14px;
}
}
&__select {
height: 36px;
display: flex;
justify-content: center;
align-items: center;
Expand Down Expand Up @@ -178,13 +174,31 @@ const selectedValue = useVModel(props, 'modelValue', emit)
&__placeholder {
color: var(--color-midnight-35);
font-size: 16px;
font-size: 14px;
line-height: 20px;
padding-top: 0;
padding-left: var(--space-0);
padding-top: var(--space-0);
margin-bottom: var(--space-0);
}
&--sm {
.multiselect__tags {
min-height: 28px;
padding: 0 40px 0 8px;
}
@media (--desktop) {
font-size: 14px;
.multiselect__single {
padding-top: var(--space-0);
margin-bottom: var(--space-0);
}
.multiselect__input {
padding-top: var(--space-0);
margin-bottom: var(--space-0);
}
.multiselect__select {
height: 29px;
}
}
}
Expand Down
62 changes: 31 additions & 31 deletions src/components/ChartControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,55 @@
<div>
<div class="chart-controls__container">
<app-chip
v-for="(button, index) in buttons"
:key="index"
v-for="option in CHART_INTERVALS_OPTIONS"
:key="option.label"
class="chart-controls__button"
:variant="selectedIndex === index ? 'error' : 'secondary'"
@click="selectInterval(index)">
{{ button.label }}
:variant="isIntervalSelected(option) ? 'error' : 'secondary'"
@click="selectInterval(option)">
{{ option.label }}
</app-chip>
<range-picker
:is-active="selectedIndex === 'custom'"
:is-range-set="hasCustomDate"
@updated="selectRange"/>
:is-range-selected="isCustomIntervalSelected"
@updated="selectCustomInterval"/>
</div>
</div>
</template>

<script setup>
import RangePicker from '@/components/RangePicker'
import { useVModel } from '@vueuse/core'
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 emit = defineEmits(['update:modelValue'])
const selectedIndex = ref(0)
const hasCustomDate = computed(() => {
return selectedIndex.value === 'custom'
const props = defineProps({
modelValue: {
type: Number,
default: 0,
},
})
function selectInterval(index) {
selectedIndex.value = index
emit('selected', buttons[index])
const selectedRange = useVModel(props, 'modelValue', emit)
const isCustomIntervalSelected = computed(() =>
Object.keys(selectedRange.value).includes('customInterval'))
function isIntervalSelected(option) {
return selectedRange.value.label === option.label
}
function selectInterval(option) {
selectedRange.value = option
}
function selectRange(dateRange) {
selectedIndex.value = 'custom'
const range = {
range: {
minStart: dateRange[0].toISOString().split('T')[0],
maxStart: dateRange[1].toISOString().split('T')[0],
function selectCustomInterval(dateCustomInterval) {
const customInterval = {
customInterval: {
minStart: dateCustomInterval[0].toISOString().split('T')[0],
maxStart: dateCustomInterval[1].toISOString().split('T')[0],
},
}
emit('selected', range)
selectedRange.value = customInterval
}
const emit = defineEmits(['selected'])
</script>

<style scoped>
Expand Down
30 changes: 18 additions & 12 deletions src/components/ContractsChartPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,48 @@
</template>
<template #header>
<chart-controls
class="u-hidden-mobile"
@selected="loadContractsStatistics"/>
v-model="selectedRange"
class="u-hidden-mobile"/>
</template>

<div class="contracts-chart-panel__container">
<line-chart
v-if="contractsStatistics"
:statistics="contractsStatistics"
:selected-interval="selectedInterval"/>
:data="contractsStatistics"
:interval="selectedRange.interval"/>
</div>

<chart-controls
class="contracts-chart-panel__controls u-hidden-desktop"
@selected="loadContractsStatistics"/>
v-model="selectedRange"
class="contracts-chart-panel__controls u-hidden-desktop"/>
</app-panel>
</template>

<script setup>
import { storeToRefs } from 'pinia'
import { useContractsStore } from '@/stores/contracts'
import LineChart from '@/components/LineChart'
import { CHART_INTERVALS_OPTIONS } from '@/utils/constants'
const contractsStore = useContractsStore()
const { contractsStatistics } = storeToRefs(contractsStore)
const { fetchContractsStatistics } = contractsStore
const selectedInterval = ref('')
const selectedRange = ref(CHART_INTERVALS_OPTIONS[0])
await useAsyncData(async() => {
await fetchContractsStatistics()
await loadContractsStatistics()
return true
})
async function loadContractsStatistics({ interval, limit, range }) {
selectedInterval.value = interval
await fetchContractsStatistics(interval, limit, range)
watch(selectedRange, async() => {
await loadContractsStatistics()
})
async function loadContractsStatistics() {
await fetchContractsStatistics(
selectedRange.value.interval,
selectedRange.value.limit,
selectedRange.value.customInterval)
}
</script>
Expand Down
45 changes: 35 additions & 10 deletions src/components/LineChart.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
<template>
<Line
:options="chartOptions"
:data="chartData"/>
<div class="line-chart">
<Line
v-if="hasChart"
:options="chartOptions"
:data="chartData"/>
<blank-state
v-if="isEmpty"
class="line-chart__blank-state"/>
<loader-indicator v-if="isLoading"/>
</div>
</template>

<script setup>
Expand All @@ -19,33 +26,36 @@ import {
import { DateTime } from 'luxon'
import { Line } from 'vue-chartjs'
const hasChart = computed(() => props.data?.length > 0)
const isEmpty = computed(() => props.data?.length === 0)
const isLoading = computed(() => props.data === null)
const props = defineProps({
statistics: {
data: {
type: Array,
required: true,
default: null,
},
selectedInterval: {
interval: {
type: String,
required: true,
},
})
const stats = computed(() => {
return props.statistics.map(stat => {
return props.data.map(stat => {
return stat.count
})
})
const labels = computed(() => {
return props.statistics.map(stat => {
return props.data.map(stat => {
return formatDate(stat.startDate)
})
})
function formatDate(label) {
const date = DateTime.fromISO(label)
if (props.selectedInterval === 'month') {
if (props.interval === 'month') {
return date.toFormat('yyyy/MM')
}
Expand Down Expand Up @@ -128,3 +138,18 @@ ChartJS.register(
ChartJS.defaults.font.family = 'Roboto Mono'
</script>
<style scoped>
.line-chart {
height: 250px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
&__blank-state {
width: 100%;
}
}
</style>
Loading

0 comments on commit ba1f59e

Please sign in to comment.