diff --git a/cypress/e2e/app/charts.cy.js b/cypress/e2e/app/charts.cy.js index 4096de467..c30c49c0d 100644 --- a/cypress/e2e/app/charts.cy.js +++ b/cypress/e2e/app/charts.cy.js @@ -3,7 +3,7 @@ describe('charts', () => { cy.visit('/charts/transactions') cy.get('.charts-navigation__link').should('be.visible') cy.get('.chart-controls__button').should('be.visible') - cy.get('.range-picker').should('be.visible') + cy.get('.scope-picker').should('be.visible') cy.get('.transactions-chart-panel__select').should('be.visible') cy.get('.line-chart canvas').should('be.visible') cy.get('.chart-controls__button').should('be.visible') diff --git a/cypress/e2e/app/transactions.cy.js b/cypress/e2e/app/transactions.cy.js index b7442d139..2466ac093 100644 --- a/cypress/e2e/app/transactions.cy.js +++ b/cypress/e2e/app/transactions.cy.js @@ -28,4 +28,61 @@ describe('transactions', () => { }) }) }) + + it('should select date', () => { + cy.visit('/transactions') + + cy.get('.paginated-content .scope-picker').click() + cy.get('.dp__today').click() + cy.get('.dp__today').parent().prev().click() + + cy.url().should('include', 'scope=') + cy.get('.paginated-content .dp__input').should('not.have.value', '') + }) + it('should select type', () => { + cy.visit('/transactions') + + cy.get('.paginated-content .multiselect').click() + cy.contains('.paginated-content .multiselect__option', 'SpendTx').click() + + cy.url().should('include', 'txType=spend') + cy.get('.paginated-content .multiselect__single').contains('SpendTx') + }) + + it('should select combined parameters', () => { + cy.visit('/transactions') + + cy.get('.paginated-content .scope-picker').click() + cy.get('.dp__today').click() + cy.get('.dp__today').parent().prev().click() + + cy.get('.paginated-content .multiselect').click() + cy.contains('.paginated-content .multiselect__option', 'NameClaimTx').click() + + cy.url().should('include', 'txType=name_claim').should('include', 'scope=') + cy.get('.paginated-content .multiselect__single').contains('NameClaimTx') + cy.get('.paginated-content .dp__input').should('not.have.value', '') + }) + + it('should filter by combining parameters from direct URL access', () => { + cy.visit('/transactions?scope=1733871600-1733958000&txType=spend') + + cy.get('.paginated-content .multiselect__single').contains('SpendTx') + cy.get('.transactions-table tbody tr').should('have.length', 10) + cy.get('.transactions-table tbody tr').contains('td', 'SpendTx').should('have.length', 1) + cy.get('.paginated-content .dp__input') + .should('have.value', '12/11/2024 - 12/12/2024') + }) + + it('should cancel filtering and clear url', () => { + cy.visit('/transactions?scope=1733871600-1733958000&txType=contract_create') + + cy.get('.paginated-content .multiselect').click() + cy.contains('.paginated-content .multiselect__option', 'All Types').click() + cy.get('.dp--clear-btn').click() + + cy.url().should('eq', Cypress.config().baseUrl + '/transactions') + cy.get('.paginated-content .multiselect__single').contains('All Types') + cy.get('.paginated-content .dp__input').should('have.value', '') + }) }) diff --git a/package.json b/package.json index 457443e17..7ef7a7759 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "reinstall": "rm -rf node_modules/ && yarn cache clean && yarn install", "reset": "yarn reinstall && yarn dev", "e2e:run": "cypress run", - "e2e:open": "cypress open" + "e2e": "cypress open" }, "dependencies": { "@aeternity/aepp-sdk": "14.0.0", diff --git a/src/components/AccountsChartPanel.vue b/src/components/AccountsChartPanel.vue index c9af5186f..2f25cb619 100644 --- a/src/components/AccountsChartPanel.vue +++ b/src/components/AccountsChartPanel.vue @@ -14,7 +14,7 @@ + :interval-by="range.intervalBy"/> { - await loadHashrateStatistics() + await loadAccountStatistics() return true }) if (process.client) { watch([range], async() => { - await loadHashrateStatistics() + await loadAccountStatistics() }) } -async function loadHashrateStatistics() { +async function loadAccountStatistics() { await fetchAccountsStatistics( - range.value.interval, + range.value.intervalBy, range.value.limit, - range.value.customInterval) + range.value.scope) } diff --git a/src/components/AppSelect.vue b/src/components/AppSelect.vue index 4a22e1ece..0687a3104 100644 --- a/src/components/AppSelect.vue +++ b/src/components/AppSelect.vue @@ -10,9 +10,7 @@ :searchable="searchable" :hide-selected="hideSelected" :preselect-first="preselectFirst" - :class="[ - size ? `multiselect--${size}` : null, - ]"> + :class="[ size ? `multiselect--${size}` : null]"> @@ -23,32 +24,30 @@ const emit = defineEmits(['update:modelValue']) const props = defineProps({ modelValue: { - type: Number, - default: 0, + type: Object, + default: null, }, }) -const selectedRange = useVModel(props, 'modelValue', emit) +const selectedScope = useVModel(props, 'modelValue', emit) -const isCustomIntervalSelected = computed(() => - Object.keys(selectedRange.value).includes('customInterval')) +const isCustomScopeSelected = computed(() => Object.keys(selectedScope.value).includes('scope')) -function isPresetSelected(option) { - return selectedRange.value.label === option.label +function selectCustomScope(scope) { + selectedScope.value = { + scope: { + minStart: scope[0], + maxStart: scope[1], + }, + } } -function selectPreset(option) { - selectedRange.value = option +function isPresetSelected(option) { + return selectedScope.value.label === option.label } -function selectCustomInterval(dateCustomInterval) { - const customInterval = { - customInterval: { - minStart: dateCustomInterval[0].toISOString().split('T')[0], - maxStart: dateCustomInterval[1].toISOString().split('T')[0], - }, - } - selectedRange.value = customInterval +function selectPreset(option) { + selectedScope.value = option } diff --git a/src/components/ContractsChartPanel.vue b/src/components/ContractsChartPanel.vue index f134faa1e..f06b4a7dd 100644 --- a/src/components/ContractsChartPanel.vue +++ b/src/components/ContractsChartPanel.vue @@ -11,7 +11,7 @@ + :interval-by="range.intervalBy"/> diff --git a/src/components/KeyblocksChartPanel.vue b/src/components/KeyblocksChartPanel.vue index 527552df6..a44bac5be 100644 --- a/src/components/KeyblocksChartPanel.vue +++ b/src/components/KeyblocksChartPanel.vue @@ -11,7 +11,7 @@ + :interval-by="range.intervalBy"/> import { storeToRefs } from 'pinia' import { useChartsStore } from '@/stores/charts' -import { CHART_INTERVALS_PRESETS_OPTIONS } from '@/utils/constants' +import { CHART_SCOPE_PRESETS_OPTIONS } from '@/utils/constants' const chartsStore = useChartsStore() const { keyblocksStatistics } = storeToRefs(chartsStore) const { fetchKeyblocksStatistics } = chartsStore -const range = ref(CHART_INTERVALS_PRESETS_OPTIONS[4]) +const range = ref(CHART_SCOPE_PRESETS_OPTIONS[4]) await useAsyncData(async() => { await loadKeyblockStatistics() @@ -43,9 +43,9 @@ if (process.client) { async function loadKeyblockStatistics() { await fetchKeyblocksStatistics( - range.value.interval, + range.value.intervalBy, range.value.limit, - range.value.customInterval) + range.value.scope) } diff --git a/src/components/LineChart.vue b/src/components/LineChart.vue index a997c8a46..3ad715796 100644 --- a/src/components/LineChart.vue +++ b/src/components/LineChart.vue @@ -35,7 +35,7 @@ const props = defineProps({ type: Array, default: null, }, - interval: { + intervalBy: { type: String, required: true, }, @@ -55,7 +55,7 @@ const labels = computed(() => { function formatDate(label) { const date = DateTime.fromISO(label) - if (props.interval === 'month') { + if (props.intervalBy === 'month') { return date.toFormat('yyyy/MM') } diff --git a/src/components/NamesChartPanel.vue b/src/components/NamesChartPanel.vue index 191397519..7bb3731a7 100644 --- a/src/components/NamesChartPanel.vue +++ b/src/components/NamesChartPanel.vue @@ -11,7 +11,7 @@ + :interval-by="range.intervalBy"/> import { storeToRefs } from 'pinia' import { useChartsStore } from '@/stores/charts' -import { CHART_INTERVALS_PRESETS_OPTIONS } from '@/utils/constants' +import { CHART_SCOPE_PRESETS_OPTIONS } from '@/utils/constants' const chartsStore = useChartsStore() const { namesStatistics } = storeToRefs(chartsStore) @@ -35,7 +35,7 @@ const props = defineProps({ }, preselectedRange: { type: Object, - default: CHART_INTERVALS_PRESETS_OPTIONS[4], + default: CHART_SCOPE_PRESETS_OPTIONS[4], }, }) @@ -54,9 +54,9 @@ if (process.client) { async function loadNamesStatistics() { await fetchNamesStatistics( - range.value.interval, + range.value.intervalBy, range.value.limit, - range.value.customInterval) + range.value.scope) } diff --git a/src/components/RangePicker.vue b/src/components/ScopePicker.vue similarity index 58% rename from src/components/RangePicker.vue rename to src/components/ScopePicker.vue index 399dd0ac9..df2164d8f 100644 --- a/src/components/RangePicker.vue +++ b/src/components/ScopePicker.vue @@ -1,20 +1,22 @@ @@ -24,35 +26,65 @@ import VueDatePicker from '@vuepic/vue-datepicker' import '@vuepic/vue-datepicker/dist/main.css' import { STATISTICS_DATA_BEGINNING } from '@/utils/constants' -const date = ref() -const datepicker = ref(null) const props = defineProps({ - isRangeSelected: { + selectedScope: { + type: Array, + default: null, + }, + isScopeSelected: { type: Boolean, required: true, }, + placeholder: { + type: String, + default: 'CUSTOM', + }, + clearable: { + type: Boolean, + default: false, + }, + type: { + type: String, + default: 'yyyy-MM-dd', + }, }) +const datepicker = ref(null) +const scope = ref(props.selectedScope) +const emit = defineEmits(['updated']) const today = DateTime.now().toFormat('yyyy-MM-dd') -watch(() => props.isRangeSelected, (newVal, oldVal) => { - if (!newVal && oldVal) { - closeDatepicker() - } -}) +watch( + () => props.isScopeSelected, + (newVal, oldVal) => { + if (!newVal && oldVal) { + close() + } + if (!newVal) { + clear() + } + }, +) -function closeDatepicker() { - if (datepicker) { +function clear() { + scope.value = null +} + +function close() { + if (datepicker.value) { datepicker.value.closeMenu() } - date.value = null } -defineEmits(['updated']) +const inputClassNames = computed(() => ({ + input: `scope-picker__input + ${props.isScopeSelected ? 'scope-picker__input--active' : ''} + ${props.clearable ? 'scope-picker__input--clearable' : ''}`, +})) diff --git a/src/components/TransactionsChartPanel.vue b/src/components/TransactionsChartPanel.vue index 82a59e66d..75fa2aa58 100644 --- a/src/components/TransactionsChartPanel.vue +++ b/src/components/TransactionsChartPanel.vue @@ -19,7 +19,7 @@ + :interval-by="range.intervalBy"/> import { storeToRefs } from 'pinia' import { useChartsStore } from '@/stores/charts' -import { CHART_INTERVALS_PRESETS_OPTIONS } from '~/utils/constants' +import { CHART_SCOPE_PRESETS_OPTIONS } from '~/utils/constants' const chartsStore = useChartsStore() const { transactionsStatistics } = storeToRefs(chartsStore) @@ -43,12 +43,12 @@ const { fetchTransactionsStatistics } = chartsStore const props = defineProps({ hasSelect: { - required: true, + default: true, type: Boolean, }, preselectedRange: { type: Object, - default: CHART_INTERVALS_PRESETS_OPTIONS[4], + default: CHART_SCOPE_PRESETS_OPTIONS[4], }, }) @@ -68,9 +68,9 @@ if (process.client) { async function loadTransactionStatistics() { await fetchTransactionsStatistics( - range.value.interval, + range.value.intervalBy, range.value.limit, - range.value.customInterval, + range.value.scope, props.hasSelect ? type.value.typeQuery : null) } diff --git a/src/components/TransactionsPanel.vue b/src/components/TransactionsPanel.vue index c35dbe575..7b159bd89 100644 --- a/src/components/TransactionsPanel.vue +++ b/src/components/TransactionsPanel.vue @@ -9,7 +9,13 @@ @prev-clicked="loadPrevTransactions" @next-clicked="loadNextTransactions"> import { computed } from 'vue' import { storeToRefs } from 'pinia' -import { useRoute, useRouter } from 'nuxt/app' +import { useRoute } from 'nuxt/app' import { useTransactionsStore } from '@/stores/transactions' -import { TX_TYPES_OPTIONS } from '@/utils/constants' import { isDesktop } from '@/utils/screen' const transactionsStore = useTransactionsStore() -const { transactions, transactionsCount, isHydrated, pageIndex, selectedTxType } = storeToRefs(transactionsStore) -const { fetchTransactions, fetchTransactionsCount, setPageIndex, setSelectedTxType } = transactionsStore + +const { + transactions, + transactionsCount, + isHydrated, + pageIndex, + selectedTxType, + selectedScope, +} = storeToRefs(transactionsStore) + +const { + loadTransactions, + changeRoute, + setPageLimit, +} = transactionsStore + const route = useRoute() -const { push } = useRouter() const limit = computed(() => process.client && isDesktop() ? 10 : 3) +if (process.client) { + if (!isHydrated?.value) { + setPageLimit(limit) + await loadTransactions() + } + + watch([selectedTxType, selectedScope], () => { + changeRoute() + }) + + watch(() => route.fullPath, async() => { + await loadTransactions() + }) +} + async function loadPrevTransactions() { - await fetchTransactions(transactions.value.prev) + await loadTransactions(transactions.value.prev) } async function loadNextTransactions() { - await fetchTransactions(transactions.value.next) + await loadTransactions(transactions.value.next) } -async function loadTransactions() { - const { txType } = route.query - const txTypeOption = TX_TYPES_OPTIONS.find(option => option.typeQuery === txType) - setSelectedTxType(txTypeOption || TX_TYPES_OPTIONS[0]) - await fetchTransactions(`/v3/transactions?limit=${limit.value}${selectedTxType.value.typeQuery ? '&type=' + selectedTxType.value.typeQuery : ''}`) - await fetchTransactionsCount(selectedTxType.value.typeQuery) - setPageIndex(1) -} + -if (process.client) { - watch(() => route.fullPath, () => { - loadTransactions() - }) + diff --git a/src/components/TransactionsScopePicker.vue b/src/components/TransactionsScopePicker.vue new file mode 100644 index 000000000..22708b1e9 --- /dev/null +++ b/src/components/TransactionsScopePicker.vue @@ -0,0 +1,24 @@ + + + diff --git a/src/pages/accounts/index.vue b/src/pages/accounts/index.vue index be6e09581..311c68196 100644 --- a/src/pages/accounts/index.vue +++ b/src/pages/accounts/index.vue @@ -13,7 +13,7 @@ + :preselected-range="CHART_SCOPE_PRESETS_OPTIONS[0]"/> @@ -21,7 +21,7 @@ diff --git a/src/pages/charts/hashrate.vue b/src/pages/charts/hashrate.vue index 47d7c67ac..0128c1891 100644 --- a/src/pages/charts/hashrate.vue +++ b/src/pages/charts/hashrate.vue @@ -22,5 +22,5 @@ diff --git a/src/pages/charts/transactions.vue b/src/pages/charts/transactions.vue index 645469be9..ce26ab8b4 100644 --- a/src/pages/charts/transactions.vue +++ b/src/pages/charts/transactions.vue @@ -15,12 +15,12 @@ diff --git a/src/pages/contracts/index.vue b/src/pages/contracts/index.vue index d7cb016e0..db3573f67 100644 --- a/src/pages/contracts/index.vue +++ b/src/pages/contracts/index.vue @@ -18,7 +18,7 @@ @@ -28,7 +28,7 @@ import ContractsPanel from '@/components/ContractsPanel' import PageHeader from '@/components/PageHeader' import { contractsHints } from '@/utils/hints/contractsHints' -import { CHART_INTERVALS_PRESETS_OPTIONS } from '@/utils/constants' +import { CHART_SCOPE_PRESETS_OPTIONS } from '@/utils/constants' const { isLoading } = useLoading() diff --git a/src/pages/names/index.vue b/src/pages/names/index.vue index d3b4ca5ad..a23fbf415 100644 --- a/src/pages/names/index.vue +++ b/src/pages/names/index.vue @@ -17,7 +17,7 @@