From be8a80be4c9aba41d7d6c345796b8855a5c15826 Mon Sep 17 00:00:00 2001 From: minaxolone Date: Wed, 31 Jul 2024 19:10:08 +0100 Subject: [PATCH] cleanup --- .../community-wallet.model.ts | 48 ++++- .../community-wallets.processor.ts | 18 +- .../community-wallets.service.ts | 54 ++---- api/src/stats/stats.service.ts | 74 +------- web-app/src/config.ts | 16 +- web-app/src/modules/core/router.tsx | 18 ++ .../Accounts/components/TopAccountsTable.tsx | 100 +++------- .../components/TopLiquidAccountRow.tsx | 2 +- .../TopLiquidAccountRowSkeleton.tsx | 2 +- .../components/TopLiquidAccountsTable.tsx | 15 +- .../CommunityWallets/CommunityWallets.tsx | 86 +++++---- .../components/CommunityWalletsTable.tsx | 126 ------------- .../components/DetailsTable.tsx | 174 ------------------ .../components/PaymentsTable.tsx | 85 ++------- .../components/Transactions.tsx | 60 ------ .../CommunityWallets/components/types.ts | 5 + .../core/routes/CommunityWallets/queries.ts | 55 ------ .../routes/CommunityWallets.tsx | 94 ++++++++++ .../CommunityWallets/routes/Details.tsx | 128 +++++++++++++ .../CommunityWallets/routes/Transactions.tsx | 94 ++++++++++ web-app/src/modules/core/routes/Home/Home.tsx | 9 +- .../modules/core/routes/Home/Stats/Stats.tsx | 1 - .../modules/core/routes/Home/Stats/index.ts | 1 + .../src/modules/core/routes/Stats/Stats.tsx | 1 - .../src/modules/ui/StatsCard/StatsCard.tsx | 5 +- web-app/src/modules/ui/Table/SortableTh.tsx | 13 +- web-app/src/modules/ui/Table/index.ts | 3 + web-app/src/modules/ui/Table/types.ts | 4 + 28 files changed, 539 insertions(+), 752 deletions(-) delete mode 100644 web-app/src/modules/core/routes/CommunityWallets/components/CommunityWalletsTable.tsx delete mode 100644 web-app/src/modules/core/routes/CommunityWallets/components/DetailsTable.tsx delete mode 100644 web-app/src/modules/core/routes/CommunityWallets/components/Transactions.tsx create mode 100644 web-app/src/modules/core/routes/CommunityWallets/components/types.ts create mode 100644 web-app/src/modules/core/routes/CommunityWallets/routes/CommunityWallets.tsx create mode 100644 web-app/src/modules/core/routes/CommunityWallets/routes/Details.tsx create mode 100644 web-app/src/modules/core/routes/CommunityWallets/routes/Transactions.tsx create mode 100644 web-app/src/modules/core/routes/Home/Stats/index.ts create mode 100644 web-app/src/modules/ui/Table/index.ts create mode 100644 web-app/src/modules/ui/Table/types.ts diff --git a/api/src/ol/community-wallets/community-wallet.model.ts b/api/src/ol/community-wallets/community-wallet.model.ts index d217ece..aa72c2c 100644 --- a/api/src/ol/community-wallets/community-wallet.model.ts +++ b/api/src/ol/community-wallets/community-wallet.model.ts @@ -1,5 +1,13 @@ import { Field, ObjectType } from '@nestjs/graphql'; +interface CommunityWalletInput { + rank: number; + address: string; + name?: string; + description?: string; + balance: number; +} + @ObjectType() export class CommunityWallet { @Field() @@ -17,11 +25,22 @@ export class CommunityWallet { @Field() balance: number; - constructor(partial: Partial) { - Object.assign(this, partial); + public constructor(input: CommunityWalletInput) { + this.rank = input.rank; + this.address = input.address; + this.name = input.name; + this.description = input.description; + this.balance = input.balance; } } +interface CommunityWalletStatsInput { + totalBalance: number; + totalPaid: number; + totalPending: number; + totalVetoed: number; +} + @ObjectType() export class CommunityWalletStats { @Field() @@ -36,11 +55,22 @@ export class CommunityWalletStats { @Field() totalVetoed: number; - constructor(partial: Partial) { - Object.assign(this, partial); + public constructor(input: CommunityWalletStatsInput) { + this.totalBalance = input.totalBalance; + this.totalPaid = input.totalPaid; + this.totalPending = input.totalPending; + this.totalVetoed = input.totalVetoed; } } +interface PaymentInput { + deadline: string; + payee: string; + value: number; + description: string; + status: string; +} + @ObjectType() export class Payment { @Field() @@ -58,8 +88,12 @@ export class Payment { @Field() status: string; - constructor(partial: Partial) { - Object.assign(this, partial); + public constructor(input: PaymentInput) { + this.deadline = input.deadline; + this.payee = input.payee; + this.value = input.value; + this.description = input.description; + this.status = input.status; } } @@ -92,8 +126,6 @@ export class CommunityWalletDetails { @Field() isMultiAction: boolean; - // also nullable - // @Field(() => [Number]) @Field(() => [Number], { nullable: true }) threshold?: number[]; diff --git a/api/src/ol/community-wallets/community-wallets.processor.ts b/api/src/ol/community-wallets/community-wallets.processor.ts index 3b4cb5b..31b95ff 100644 --- a/api/src/ol/community-wallets/community-wallets.processor.ts +++ b/api/src/ol/community-wallets/community-wallets.processor.ts @@ -2,12 +2,10 @@ import _ from 'lodash'; import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; import { Inject, OnModuleInit } from '@nestjs/common'; import { Job, Queue } from 'bullmq'; + import { ICommunityWalletsService } from './interfaces.js'; -import { redisClient } from '../../redis/redis.service.js'; import { Types } from '../../types.js'; -import { COMMUNITY_WALLETS_CACHE_KEY } from '../constants.js'; - @Processor('community-wallets') export class CommunityWalletsProcessor extends WorkerHost implements OnModuleInit { public constructor( @@ -21,11 +19,15 @@ export class CommunityWalletsProcessor extends WorkerHost implements OnModuleIni } public async onModuleInit() { - await this.communityWalletsQueue.add('updateCommunityWalletsCaches', undefined, { - repeat: { - every: 60 * 60 * 1_000, // 1 hour - }, - }); + await this.communityWalletsQueue.add( + 'updateCommunityWalletsCaches', + undefined, + { + repeat: { + every: 60 * 60 * 1_000, // 1 hour + }, + } + ); // Execute the job immediately on startup await this.updateCommunityWalletsCaches(); diff --git a/api/src/ol/community-wallets/community-wallets.service.ts b/api/src/ol/community-wallets/community-wallets.service.ts index 27ca5c4..85442f3 100644 --- a/api/src/ol/community-wallets/community-wallets.service.ts +++ b/api/src/ol/community-wallets/community-wallets.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@nestjs/common'; import _ from 'lodash'; +import { ConfigService } from '@nestjs/config'; + import { redisClient } from '../../redis/redis.service.js'; import { OlService } from '../ol.service.js'; import { @@ -8,7 +10,7 @@ import { CommunityWalletPayments, CommunityWalletDetails, } from './community-wallet.model.js'; -import { parseAddress } from '../../utils.js'; +import { parseAddress, parseHexString } from '../../utils.js'; import { communityWallets } from './community-wallets.js'; import { ICommunityWalletsService } from './interfaces.js'; import { @@ -18,32 +20,31 @@ import { COMMUNITY_WALLETS_DETAILS_CACHE_KEY, } from '../constants.js'; +function formatCoin(value: number): number { + return Math.floor(value / 1e6); +} + @Injectable() export class CommunityWalletsService implements ICommunityWalletsService { private readonly cacheEnabled: boolean; - public constructor(private readonly olService: OlService) { - this.cacheEnabled = process.env.CACHE_ENABLED === 'true'; + public constructor( + private readonly olService: OlService, + config: ConfigService, + ) { + this.cacheEnabled = config.get('cacheEnabled')!; } private async getFromCache(key: string): Promise { - try { - const cachedData = await redisClient.get(key); - if (cachedData) { - return JSON.parse(cachedData) as T; - } - } catch (error) { - console.error(`Error getting data from cache for key ${key}:`, error); + const cachedData = await redisClient.get(key); + if (cachedData) { + return JSON.parse(cachedData) as T; } return null; } private async setCache(key: string, data: T): Promise { - try { - await redisClient.set(key, JSON.stringify(data)); - } catch (error) { - console.error(`Error setting data to cache for key ${key}:`, error); - } + await redisClient.set(key, JSON.stringify(data)); } public async getCommunityWallets(): Promise { @@ -211,10 +212,10 @@ export class CommunityWalletsService implements ICommunityWalletsService { return payments .filter((payment) => status !== 'pending' || payment.deadline > currentEpoch) .map((payment) => ({ - deadline: String(payment.deadline), - payee: String(payment.tx.payee), + deadline: payment.deadline, + payee: payment.tx.payee, value: formatCoin(payment.tx.value), - description: hexToAscii(payment.tx.description), + description: Buffer.from(parseHexString(payment.tx.description)).toString('utf-8'), status, })); }; @@ -333,20 +334,3 @@ export class CommunityWalletsService implements ICommunityWalletsService { await this.setCache(COMMUNITY_WALLETS_DETAILS_CACHE_KEY, details); } } - -function hexToAscii(hex: string): string { - // Remove the "0x" prefix if it exists - hex = hex.startsWith('0x') ? hex.slice(2) : hex; - - // Split the hex string into pairs of characters - const hexPairs = hex.match(/.{1,2}/g) || []; - - // Convert each pair of hex characters to an ASCII character - const asciiStr = hexPairs.map((hexPair) => String.fromCharCode(parseInt(hexPair, 16))).join(''); - - return asciiStr; -} - -function formatCoin(value: number): number { - return Math.floor(value / 1000000); -} diff --git a/api/src/stats/stats.service.ts b/api/src/stats/stats.service.ts index 944bd65..c003882 100644 --- a/api/src/stats/stats.service.ts +++ b/api/src/stats/stats.service.ts @@ -41,8 +41,7 @@ export class StatsService { config: ConfigService, ) { this.dataApiHost = config.get('dataApiHost')!; - - this.cacheEnabled = process.env.CACHE_ENABLED === 'true'; + this.cacheEnabled = config.get('cacheEnabled')!; } private async setCache(key: string, data: T): Promise { @@ -63,49 +62,17 @@ export class StatsService { } public async getStats(): Promise { - console.time('getSupplyStats'); const supplyStats = await this.olService.getSupplyStats(); - console.timeEnd('getSupplyStats'); - - console.time('getTotalSupply'); const totalSupply: number = supplyStats.totalSupply; - console.timeEnd('getTotalSupply'); - - console.time('getSlowWalletsCountOverTime'); const slowWalletsCountOverTime = await this.getSlowWalletsCountOverTime(); - console.timeEnd('getSlowWalletsCountOverTime'); - - console.time('getBurnsOverTime'); const burnOverTime = await this.getBurnsOverTime(); - console.timeEnd('getBurnsOverTime'); - - console.time('getAccountsOnChainOverTime'); const accountsOnChainOverTime = await this.getAccountsOnChainOverTime(); - console.timeEnd('getAccountsOnChainOverTime'); - - console.time('getSupplyAndCapital'); const supplyAndCapital = await this.getSupplyAndCapital(supplyStats); - console.timeEnd('getSupplyAndCapital'); - - console.time('getCommunityWalletsBalanceBreakdown'); const communityWalletsBalanceBreakdown = await this.getCommunityWalletsBalanceBreakdown(); - console.timeEnd('getCommunityWalletsBalanceBreakdown'); - - console.time('getLastEpochTotalUnlockedAmount'); const lastEpochTotalUnlockedAmount = await this.getLastEpochTotalUnlockedAmount(); - console.timeEnd('getLastEpochTotalUnlockedAmount'); - - console.time('getPOFValues'); const pofValues = await this.getPOFValues(); // Empty table? - console.timeEnd('getPOFValues'); - - console.time('getLiquidSupplyConcentration'); const liquidSupplyConcentration = await this.getLiquidSupplyConcentration(); - console.timeEnd('getLiquidSupplyConcentration'); - - console.time('calculateLiquidityConcentrationLocked'); const lockedSupplyConcentration = await this.calculateLiquidityConcentrationLocked(); - console.timeEnd('calculateLiquidityConcentrationLocked'); // calculate KPIS // circulating @@ -452,42 +419,6 @@ export class StatsService { } } - private async getSlowWalletsUnlockedAmount(): Promise { - try { - const query = ` - SELECT - hex(SW.address) AS address, - (latest_balance - max(SW.unlocked)) / 1e6 AS locked_balance - FROM - slow_wallet SW - JOIN - (SELECT - address, - argMax(balance, timestamp) as latest_balance - FROM coin_balance - WHERE coin_module = 'libra_coin' - GROUP BY address) AS CB - ON SW.address = CB.address - GROUP BY SW.address, latest_balance - `; - - const resultSet = await this.clickhouseService.client.query({ - query: query, - format: 'JSONEachRow', - }); - - const rows = await resultSet.json<{ locked_balance: number }>(); - - // Sum the locked Amount - const totalLockedAmount = rows.reduce((acc, row) => acc + row.locked_balance, 0); - - return totalLockedAmount; - } catch (error) { - console.error('Error in getSlowWalletsUnlockedAmount:', error); - throw error; - } - } - private async getLastEpochTotalUnlockedAmount(): Promise { try { // Query the slow_wallet table to get the addresses and unlocked balances @@ -551,7 +482,6 @@ export class StatsService { const latest_balance = balanceMap.get(row.address) ?? 0; const unlocked_balance = row.unlocked_balance; const locked_balance = latest_balance - unlocked_balance; - // console.log(`Address: ${row.address}, Latest Balance: ${latest_balance}, Unlocked Balance: ${unlocked_balance}, Locked Balance: ${locked_balance}`); return { address: row.address, locked_balance, @@ -1344,7 +1274,6 @@ export class StatsService { // Query to get the top unlocked balance accounts private async queryTopLiquidAccounts(): Promise { - console.time('queryTopLiquidAccounts'); const limit = 100; const circulatingSupply = await this.getCirculatingSupply(); @@ -1444,7 +1373,6 @@ export class StatsService { liquidShare: item.percentOfCirculating, }), ); - console.timeEnd('queryTopLiquidAccounts'); return ret; } catch (error) { console.error('Error in getTopUnlockedBalanceWallets:', error); diff --git a/web-app/src/config.ts b/web-app/src/config.ts index c282b3b..d929898 100644 --- a/web-app/src/config.ts +++ b/web-app/src/config.ts @@ -1,11 +1,16 @@ const API_HOST: string = import.meta.env.VITE_API_HOST; -const VITE_DATA_API_HOST: string = import.meta.env.VITE_DATA_API_HOST; +const DATA_API_HOST: string = import.meta.env.VITE_DATA_API_HOST; export interface Config { apiHost: string; dataApiHost: string; } +const localhost = { + apiHost: API_HOST, + dataApiHost: DATA_API_HOST, +}; + const configMap = new Map([ [ '0l.fyi', @@ -23,10 +28,11 @@ const configMap = new Map([ ], [ '127.0.0.1', - { - apiHost: API_HOST, - dataApiHost: VITE_DATA_API_HOST, - }, + localhost + ], + [ + 'localhost', + localhost ], ]); diff --git a/web-app/src/modules/core/router.tsx b/web-app/src/modules/core/router.tsx index 8fe1a8d..7c70e32 100644 --- a/web-app/src/modules/core/router.tsx +++ b/web-app/src/modules/core/router.tsx @@ -16,6 +16,10 @@ import Module from './routes/Account/Modules/Module'; import Stats from './routes/Stats'; import Postero from './routes/Postero'; +import CommunityWalletsIndex from './routes/CommunityWallets/routes/CommunityWallets'; +import CommunityWalletsTransactions from './routes/CommunityWallets/routes/Transactions'; +import CommunityWalletsDetails from './routes/CommunityWallets/routes/Details'; + const router = createBrowserRouter([ { path: '/', @@ -76,6 +80,20 @@ const router = createBrowserRouter([ { path: '/community-wallets', element: , + children: [ + { + index: true, + element: , + }, + { + path: 'details', + element: , + }, + { + path: 'transactions', + element: , + }, + ], }, { path: '/stats', diff --git a/web-app/src/modules/core/routes/Accounts/components/TopAccountsTable.tsx b/web-app/src/modules/core/routes/Accounts/components/TopAccountsTable.tsx index d112451..dfe8efd 100644 --- a/web-app/src/modules/core/routes/Accounts/components/TopAccountsTable.tsx +++ b/web-app/src/modules/core/routes/Accounts/components/TopAccountsTable.tsx @@ -1,7 +1,9 @@ -import { FC, useState } from 'react'; +import { FC, useState, useEffect } from 'react'; +import _ from 'lodash'; import { gql, useQuery } from '@apollo/client'; + import { ITopAccount } from '../../../../interface/TopAccount.interface'; -import SortableTh from '../../../../ui/Table/SortableTh'; +import { SortableTh, SortOrder } from '../../../../ui/Table'; import TopAccountRow from './TopAccountRow'; import TopAccountRowSkeleton from './TopAccountRowSkeleton'; @@ -9,8 +11,6 @@ interface TopAccountsTableProps { accounts?: ITopAccount[]; } -type SortOrder = 'asc' | 'desc'; - const GET_TOP_ACCOUNTS = gql` query Accounts { getTopAccounts { @@ -26,83 +26,39 @@ const GET_TOP_ACCOUNTS = gql` } `; +const columns = [ + { key: 'rank', label: 'Rank', className: 'text-center' }, + { key: 'address', label: 'Address', className: '' }, + { key: 'publicName', label: 'Public Name', className: 'text-left' }, + { key: 'balance', label: 'Balance', className: 'text-right' }, + { key: 'cumulativeShare', label: 'Cumulative Share (%)', className: 'text-right' }, +]; + const TopAccountsTable: FC = () => { - const [sortColumn, setSortColumn] = useState('rank'); - const [sortOrder, setSortOrder] = useState('desc'); + const [sortColumn, setSortColumn] = useState('rank'); + const [sortOrder, setSortOrder] = useState(SortOrder.Desc); + const [sortedAccounts, setSortedAccounts] = useState([]); const { data } = useQuery(GET_TOP_ACCOUNTS); const accounts: ITopAccount[] = data ? data.getTopAccounts : null; + useEffect(() => { + const sortedAccounts = _.sortBy(accounts, sortColumn); + if (sortOrder === SortOrder.Desc) { + sortedAccounts.reverse(); + } + setSortedAccounts(sortedAccounts); + }, [accounts, sortColumn, sortOrder]); + const handleSort = (column: string) => { if (sortColumn === column) { - setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); + setSortOrder(sortOrder === SortOrder.Asc ? SortOrder.Desc : SortOrder.Asc); } else { setSortColumn(column); - setSortOrder('asc'); - } - }; - - const getSortedAccounts = (accounts: ITopAccount[]) => { - const sortedAccounts = [...accounts].sort((a, b) => { - const [aValue, bValue] = getValue(a, b, sortColumn); - - if (aValue === bValue) { - return a.address.localeCompare(b.address); - } - - return aValue < bValue ? -1 : 1; - }); - - if (sortOrder === 'asc') { - sortedAccounts.reverse(); - } - - return sortedAccounts; - }; - - const getValue = (a: ITopAccount, b: ITopAccount, column: string): [any, any] => { - let value1: any; - let value2: any; - - switch (column) { - case 'rank': - value1 = a.rank; - value2 = b.rank; - break; - case 'address': - value1 = a.address; - value2 = b.address; - break; - case 'publicName': - value1 = a.publicName; - value2 = b.publicName; - break; - case 'balance': - value1 = a.balance; - value2 = b.balance; - break; - default: - value1 = a.rank; - value2 = b.rank; + setSortOrder(SortOrder.Desc); } - - return [value1, value2]; }; - let sortedAccounts; - - if (accounts) { - sortedAccounts = getSortedAccounts(accounts); - } - - const columns = [ - { key: 'rank', label: 'Rank', className: 'text-center' }, - { key: 'address', label: 'Address', className: '' }, - { key: 'publicName', label: 'Public Name', className: 'text-left' }, - { key: 'balance', label: 'Balance', className: 'text-right' }, - { key: 'cumulativeShare', label: 'Cumulative Share (%)', className: 'text-right' }, - ]; - return (
@@ -126,13 +82,11 @@ const TopAccountsTable: FC = () => { - {sortedAccounts + {sortedAccounts.length ? sortedAccounts.map((account) => ( )) - : Array.from({ length: 10 }).map((_, index) => ( - - ))} + : new Array(10).fill(0).map((_, index) => )}
diff --git a/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountRow.tsx b/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountRow.tsx index c9dcce1..258d6fe 100644 --- a/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountRow.tsx +++ b/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountRow.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import { FC } from 'react'; import { ITopLiquidAccount } from '../../../../interface/TopLiquidAccount.interface'; import AccountAddress from '../../../../ui/AccountAddress'; import Money from '../../../../ui/Money'; diff --git a/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountRowSkeleton.tsx b/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountRowSkeleton.tsx index 7ce47af..e136127 100644 --- a/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountRowSkeleton.tsx +++ b/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountRowSkeleton.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import { FC } from 'react'; const TopLiquidAccountRowSkeleton: FC = () => { return ( diff --git a/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountsTable.tsx b/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountsTable.tsx index ff0a45a..e273236 100644 --- a/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountsTable.tsx +++ b/web-app/src/modules/core/routes/Accounts/components/TopLiquidAccountsTable.tsx @@ -1,15 +1,14 @@ import React, { useState } from 'react'; import { gql, useQuery } from '@apollo/client'; -import SortableTh from '../../../../ui/Table/SortableTh'; + +import { SortableTh, SortOrder } from '../../../../ui/Table'; import TopLiquidAccountRow from './TopLiquidAccountRow'; import TopLiquidAccountRowSkeleton from './TopLiquidAccountRowSkeleton'; import { ITopLiquidAccount } from '../../../../interface/TopLiquidAccount.interface'; interface TopLiquidAccountsTableProps { accounts?: ITopLiquidAccount[]; -} - -type SortOrder = 'asc' | 'desc'; +} const GET_TOP_LIQUID_ACCOUNTS = gql` query TopLiquidAccounts { @@ -25,17 +24,17 @@ const GET_TOP_LIQUID_ACCOUNTS = gql` const TopLiquidAccountsTable: React.FC = () => { const [sortColumn, setSortColumn] = useState('rank'); - const [sortOrder, setSortOrder] = useState('desc'); + const [sortOrder, setSortOrder] = useState(SortOrder.Desc); const { data } = useQuery(GET_TOP_LIQUID_ACCOUNTS); const accounts: ITopLiquidAccount[] = data ? data.getTopLiquidAccounts : null; const handleSort = (column: string) => { if (sortColumn === column) { - setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); + setSortOrder(sortOrder === SortOrder.Asc ? SortOrder.Desc : SortOrder.Asc); } else { setSortColumn(column); - setSortOrder('asc'); + setSortOrder(SortOrder.Asc); } }; @@ -50,7 +49,7 @@ const TopLiquidAccountsTable: React.FC = () => { return aValue < bValue ? -1 : 1; }); - if (sortOrder === 'asc') { + if (sortOrder === SortOrder.Asc) { sortedAccounts.reverse(); } diff --git a/web-app/src/modules/core/routes/CommunityWallets/CommunityWallets.tsx b/web-app/src/modules/core/routes/CommunityWallets/CommunityWallets.tsx index 24cfe2f..a73d4eb 100644 --- a/web-app/src/modules/core/routes/CommunityWallets/CommunityWallets.tsx +++ b/web-app/src/modules/core/routes/CommunityWallets/CommunityWallets.tsx @@ -1,33 +1,11 @@ -import { FC, useState } from 'react'; +import { FC } from 'react'; +import { NavLink, Outlet } from 'react-router-dom'; +import clsx from 'clsx'; + import Page from '../../../ui/Page'; -import ToggleButton from '../../../ui/ToggleButton'; import CommunityWalletsStats from './components/CommunityWalletsStats'; -import CommunityWalletsTable from './components/CommunityWalletsTable'; -import DetailsTable from './components/DetailsTable'; -import Transactions from './components/Transactions'; -import { useQuery } from '@apollo/client'; -import { - GET_COMMUNITY_WALLETS, - GET_COMMUNITY_WALLETS_PAYMENTS, - GET_COMMUNITY_WALLETS_DETAILS, -} from './queries'; const CommunityWallets: FC = () => { - const [activeView, setActiveView] = useState('wallets'); - const { data: communityWalletsRes } = useQuery(GET_COMMUNITY_WALLETS); - const { data: paymentsRes } = useQuery(GET_COMMUNITY_WALLETS_PAYMENTS); - const { data: detailsRes } = useQuery(GET_COMMUNITY_WALLETS_DETAILS); - - const communityWallets = communityWalletsRes?.getCommunityWallets || []; - const payments = paymentsRes?.getCommunityWalletsPayments || []; - const details = detailsRes?.getCommunityWalletsDetails || []; - - const toggleOptions = [ - { label: 'Wallets', value: 'wallets' }, - { label: 'Details', value: 'details' }, - { label: 'Transactions', value: 'transactions' }, - ]; - return (

@@ -36,18 +14,50 @@ const CommunityWallets: FC = () => {
- - <> -
- -
-
- -
-
- -
- +
    +
  • + + clsx( + 'block px-4 py-2', + isActive ? 'bg-[var(--Colors-Background-bg-active,#FAFAFA)]' : 'bg-white', + ) + } + > + Wallets + +
  • +
  • + + clsx( + 'block px-4 py-2', + isActive ? 'bg-[var(--Colors-Background-bg-active,#FAFAFA)]' : 'bg-white', + ) + } + > + Details + +
  • +
  • + + clsx( + 'block px-4 py-2', + isActive ? 'bg-[var(--Colors-Background-bg-active,#FAFAFA)]' : 'bg-white', + ) + } + > + Transactions + +
  • +
+ +
diff --git a/web-app/src/modules/core/routes/CommunityWallets/components/CommunityWalletsTable.tsx b/web-app/src/modules/core/routes/CommunityWallets/components/CommunityWalletsTable.tsx deleted file mode 100644 index 50fb755..0000000 --- a/web-app/src/modules/core/routes/CommunityWallets/components/CommunityWalletsTable.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { FC, useState, useEffect } from 'react'; -import SortableTh from '../../../../ui/Table/SortableTh'; -import CommunityWalletRow from './CommunityWalletRow'; -import CommunityWalletRowSkeleton from './CommunityWalletRowSkeleton'; -import { ICommunityWallet } from '../../../../interface/CommunityWallets.interface'; - -type SortOrder = 'asc' | 'desc'; - -const CommunityWalletsTable: FC<{ wallets: ICommunityWallet[] }> = ({ wallets }) => { - const [sortColumn, setSortColumn] = useState('rank'); - const [sortOrder, setSortOrder] = useState('desc'); - const [sortedWallets, setSortedWallets] = useState([]); - - useEffect(() => { - setSortedWallets(getSortedWallets(wallets)); - }, [wallets, sortColumn, sortOrder]); - - const handleSort = (column: string) => { - if (sortColumn === column) { - setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); - } else { - setSortColumn(column); - setSortOrder('asc'); - } - setSortedWallets(getSortedWallets(wallets)); - }; - - const getSortedWallets = (wallets: ICommunityWallet[]) => { - const sortableWallets = [...wallets].sort((a, b) => { - const [aValue, bValue] = getValue(a, b, sortColumn); - - if (aValue === bValue) { - return a.address.localeCompare(b.address); - } - - return aValue < bValue ? -1 : 1; - }); - - if (sortOrder === 'asc') { - sortableWallets.reverse(); - } - - return sortableWallets; - }; - - const getValue = (a: ICommunityWallet, b: ICommunityWallet, column: string): [any, any] => { - let value1: any; - let value2: any; - - switch (column) { - case 'rank': - value1 = a.rank; - value2 = b.rank; - break; - case 'address': - value1 = a.address; - value2 = b.address; - break; - case 'name': - value1 = a.name; - value2 = b.name; - break; - case 'balance': - value1 = a.balance; - value2 = b.balance; - break; - case 'description': - value1 = a.description; - value2 = b.description; - break; - default: - value1 = a.rank; - value2 = b.rank; - } - - return [value1, value2]; - }; - - const columns = [ - { key: 'rank', label: 'Rank', className: 'text-center' }, - { key: 'address', label: 'Address', className: '' }, - { key: 'name', label: 'Name', className: 'text-left' }, - { key: 'description', label: 'Description', className: 'text-left' }, - { key: 'balance', label: 'Balance', className: 'text-right' }, - ]; - - return ( -
-
-
-
- - - - {columns.map((col) => ( - - {col.label} - - ))} - - - - {sortedWallets.length > 0 - ? sortedWallets.map((wallet) => ( - - )) - : Array.from({ length: 10 }).map((_, index) => ( - - ))} - -
-
-
-
-
- ); -}; - -export default CommunityWalletsTable; diff --git a/web-app/src/modules/core/routes/CommunityWallets/components/DetailsTable.tsx b/web-app/src/modules/core/routes/CommunityWallets/components/DetailsTable.tsx deleted file mode 100644 index a8355f6..0000000 --- a/web-app/src/modules/core/routes/CommunityWallets/components/DetailsTable.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { CheckIcon, XMarkIcon } from '@heroicons/react/20/solid'; -import SortableTh from '../../../../ui/Table/SortableTh'; -import { ICommunityWalletDetails } from '../../../../interface/CommunityWallets.interface'; -import AccountAddress from '../../../../ui/AccountAddress'; - -type SortOrder = 'asc' | 'desc'; - -const DetailsTable: React.FC<{ details: ICommunityWalletDetails[] }> = ({ details }) => { - const [sortColumn, setSortColumn] = useState('isMultiAction'); - const [sortOrder, setSortOrder] = useState('asc'); - const [sortedDetails, setSortedDetails] = useState([]); - - useEffect(() => { - setSortedDetails(getSortedDetails(details)); - }, [details, sortColumn, sortOrder]); - - const handleSort = (column: string) => { - if (sortColumn === column) { - setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); - } else { - setSortColumn(column); - setSortOrder('asc'); - } - setSortedDetails(getSortedDetails(details)); - }; - - const getSortedDetails = (details: ICommunityWalletDetails[]) => { - const sortableDetails = [...details].sort((a, b) => { - const [aValue, bValue] = getValue(a, b, sortColumn); - - if (aValue === bValue) { - return a.address.localeCompare(b.address); - } - - return aValue < bValue ? -1 : 1; - }); - - if (sortOrder === 'asc') { - sortableDetails.reverse(); - } - - return sortableDetails; - }; - - const getValue = ( - a: ICommunityWalletDetails, - b: ICommunityWalletDetails, - column: string, - ): [any, any] => { - let value1: any; - let value2: any; - - switch (column) { - case 'address': - value1 = a.address; - value2 = b.address; - break; - case 'name': - value1 = a.name; - value2 = b.name; - break; - case 'isMultiAction': - value1 = a.isMultiAction; - value2 = b.isMultiAction; - break; - case 'threshold': - value1 = a.threshold; - value2 = b.threshold; - break; - case 'payees': - value1 = a.payees; - value2 = b.payees; - break; - case 'totalPaid': - value1 = a.totalPaid; - value2 = b.totalPaid; - break; - case 'balance': - value1 = a.balance; - value2 = b.balance; - break; - default: - value1 = a.totalPaid; - value2 = b.totalPaid; - } - - return [value1, value2]; - }; - - const formatCoin = (value: number) => { - return value - ? value.toLocaleString('en-US', { - maximumFractionDigits: 0, - }) - : value; - }; - - const formatThreshold = (threshold: number[]) => { - if (Array.isArray(threshold) && threshold.length === 2) { - return `${threshold[0]} / ${threshold[1]}`; - } - return ''; - }; - - const columns = [ - { key: 'address', label: 'Address', className: 'text-left' }, - { key: 'name', label: 'Name', className: '' }, - { key: 'isMultiAction', label: 'Multisign', className: 'text-center' }, - { key: 'threshold', label: 'Threshold', className: 'text-center' }, - { key: 'payees', label: '#Payees', className: 'text-right' }, - { key: 'totalPaid', label: 'Payments', className: 'text-right' }, - { key: 'balance', label: 'Balance', className: 'text-right' }, - ]; - - return ( -
- - - - {columns.map((col) => ( - - {col.label} - - ))} - - - - {sortedDetails.length > 0 ? ( - sortedDetails.map((wallet, index) => ( - - - - - - - - - - )) - ) : ( - - - - )} - -
- - {wallet.name} - {wallet.isMultiAction ? ( - - ) : ( - - )} - {formatThreshold(wallet.threshold)}{wallet.payees}{formatCoin(wallet.totalPaid)}{formatCoin(wallet.balance)}
- No data available -
-
- ); -}; - -DetailsTable.propTypes = { - details: PropTypes.array.isRequired, -}; - -export default DetailsTable; diff --git a/web-app/src/modules/core/routes/CommunityWallets/components/PaymentsTable.tsx b/web-app/src/modules/core/routes/CommunityWallets/components/PaymentsTable.tsx index 2d34b0a..76b9937 100644 --- a/web-app/src/modules/core/routes/CommunityWallets/components/PaymentsTable.tsx +++ b/web-app/src/modules/core/routes/CommunityWallets/components/PaymentsTable.tsx @@ -1,12 +1,15 @@ import React, { useState, useMemo, useEffect } from 'react'; +import Decimal from 'decimal.js'; +import _ from 'lodash'; + import AccountAddress from '../../../../ui/AccountAddress'; import LibraAmount from '../../../../ui/LibraAmount'; -import Decimal from 'decimal.js'; import { ICommunityWalletPayments, IPayment, } from '../../../../interface/CommunityWallets.interface'; -import SortableTh from '../../../../ui/Table/SortableTh'; +import { SortableTh, SortOrder } from '../../../../ui/Table'; +import { PaymentStatus } from './types'; interface IFlatPayment extends IPayment { address: string; @@ -15,95 +18,43 @@ interface IFlatPayment extends IPayment { interface PaymentsTableProps { payments: ICommunityWalletPayments[]; - status: 'paid' | 'pending' | 'vetoed'; + status: PaymentStatus; } const PaymentsTable: React.FC = ({ payments, status }) => { const [sortColumn, setSortColumn] = useState('deadline'); - const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc'); + const [sortOrder, setSortOrder] = useState(SortOrder.Asc); const [sortedPayments, setSortedPayments] = useState([]); const filteredPayments = useMemo(() => { const flatPayments: IFlatPayment[] = []; - payments.forEach((wallet) => { - wallet[status].forEach((payment) => { + for (const wallet of payments) { + for (const payment of wallet[status]) { flatPayments.push({ ...payment, address: wallet.address, name: wallet.name, }); - }); - }); + } + } return flatPayments; }, [payments, status]); useEffect(() => { - setSortedPayments(getSortedPayments(filteredPayments)); + const sortedPayments = _.sortBy(filteredPayments, sortColumn); + if (sortOrder === SortOrder.Desc) { + sortedPayments.reverse(); + } + setSortedPayments(sortedPayments); }, [filteredPayments, sortColumn, sortOrder]); const handleSort = (column: string) => { if (sortColumn === column) { - setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); + setSortOrder(sortOrder === SortOrder.Asc ? SortOrder.Desc : SortOrder.Asc); } else { setSortColumn(column); - setSortOrder('asc'); + setSortOrder(SortOrder.Asc); } - setSortedPayments(getSortedPayments(filteredPayments)); - }; - - const getSortedPayments = (payments: IFlatPayment[]) => { - const sortablePayments = [...payments].sort((a, b) => { - const [aValue, bValue] = getValue(a, b, sortColumn); - - if (aValue === bValue) { - return a.address.localeCompare(b.address); - } - - return aValue < bValue ? -1 : 1; - }); - - if (sortOrder === 'asc') { - sortablePayments.reverse(); - } - - return sortablePayments; - }; - - const getValue = (a: IFlatPayment, b: IFlatPayment, column: string): [any, any] => { - let value1: any; - let value2: any; - - switch (column) { - case 'address': - value1 = a.address; - value2 = b.address; - break; - case 'name': - value1 = a.name; - value2 = b.name; - break; - case 'deadline': - value1 = a.deadline; - value2 = b.deadline; - break; - case 'payee': - value1 = a.payee; - value2 = b.payee; - break; - case 'description': - value1 = a.description; - value2 = b.description; - break; - case 'value': - value1 = a.value; - value2 = b.value; - break; - default: - value1 = a.deadline; - value2 = b.deadline; - } - - return [value1, value2]; }; if (filteredPayments.length === 0) { diff --git a/web-app/src/modules/core/routes/CommunityWallets/components/Transactions.tsx b/web-app/src/modules/core/routes/CommunityWallets/components/Transactions.tsx deleted file mode 100644 index 4fc1015..0000000 --- a/web-app/src/modules/core/routes/CommunityWallets/components/Transactions.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { useState } from 'react'; -import PaymentsTable from './PaymentsTable'; -import { ICommunityWalletPayments } from '../../../../interface/CommunityWallets.interface'; - -interface TransactionsProps { - payments: ICommunityWalletPayments[]; -} - -const Transactions: React.FC = ({ payments }) => { - const [selectedStatus, setSelectedStatus] = useState('paid'); - - return ( -
-
- - - -
- -
- -
-
- -
-
- -
-
- ); -}; - -export default Transactions; diff --git a/web-app/src/modules/core/routes/CommunityWallets/components/types.ts b/web-app/src/modules/core/routes/CommunityWallets/components/types.ts new file mode 100644 index 0000000..b822b68 --- /dev/null +++ b/web-app/src/modules/core/routes/CommunityWallets/components/types.ts @@ -0,0 +1,5 @@ +export enum PaymentStatus { + Paid = 'paid', + Pending = 'pending', + Vetoed = 'vetoed', +} diff --git a/web-app/src/modules/core/routes/CommunityWallets/queries.ts b/web-app/src/modules/core/routes/CommunityWallets/queries.ts index 2fb3e77..4465783 100644 --- a/web-app/src/modules/core/routes/CommunityWallets/queries.ts +++ b/web-app/src/modules/core/routes/CommunityWallets/queries.ts @@ -1,17 +1,5 @@ import { gql } from '@apollo/client'; -export const GET_COMMUNITY_WALLETS = gql` - query GetCommunityWallets { - getCommunityWallets { - rank - address - name - balance - description - } - } -`; - export const GET_COMMUNITY_WALLETS_STATS = gql` query GetCommunityWalletsStats { getCommunityWalletsStats { @@ -23,46 +11,3 @@ export const GET_COMMUNITY_WALLETS_STATS = gql` } `; -export const GET_COMMUNITY_WALLETS_PAYMENTS = gql` - query GetCommunityWalletsPayments { - getCommunityWalletsPayments { - address - name - paid { - deadline - payee - description - value - status - } - pending { - deadline - payee - description - value - status - } - vetoed { - deadline - payee - description - value - status - } - } - } -`; - -export const GET_COMMUNITY_WALLETS_DETAILS = gql` - query GetCommunityWalletsDetails { - getCommunityWalletsDetails { - address - name - isMultiAction - threshold - totalPaid - balance - payees - } - } -`; diff --git a/web-app/src/modules/core/routes/CommunityWallets/routes/CommunityWallets.tsx b/web-app/src/modules/core/routes/CommunityWallets/routes/CommunityWallets.tsx new file mode 100644 index 0000000..6fe2621 --- /dev/null +++ b/web-app/src/modules/core/routes/CommunityWallets/routes/CommunityWallets.tsx @@ -0,0 +1,94 @@ +import { useState, useEffect } from 'react'; +import _ from 'lodash'; +import { gql, useQuery } from '@apollo/client'; + +import { SortableTh, SortOrder } from '../../../../ui/Table'; +import CommunityWalletRow from '../components/CommunityWalletRow'; +import CommunityWalletRowSkeleton from '../components/CommunityWalletRowSkeleton'; +import { ICommunityWallet } from '../../../../interface/CommunityWallets.interface'; + +const GET_COMMUNITY_WALLETS = gql` + query GetCommunityWallets { + getCommunityWallets { + rank + address + name + balance + description + } + } +`; + +const columns = [ + { key: 'rank', label: 'Rank', className: 'text-center' }, + { key: 'address', label: 'Address', className: '' }, + { key: 'name', label: 'Name', className: 'text-left' }, + { key: 'description', label: 'Description', className: 'text-left' }, + { key: 'balance', label: 'Balance', className: 'text-right' }, +]; + +const CommunityWallets = () => { + const [sortColumn, setSortColumn] = useState('balance'); + const [sortOrder, setSortOrder] = useState(SortOrder.Desc); + const [sortedWallets, setSortedWallets] = useState([]); + + const { data } = useQuery<{ getCommunityWallets: ICommunityWallet[] }>(GET_COMMUNITY_WALLETS); + const wallets = data?.getCommunityWallets; + + useEffect(() => { + const sortedWallets = _.sortBy(wallets ?? [], sortColumn); + if (sortOrder === SortOrder.Desc) { + sortedWallets.reverse(); + } + setSortedWallets(sortedWallets); + }, [wallets, sortColumn, sortOrder]); + + const handleSort = (column: string) => { + if (sortColumn === column) { + setSortOrder(sortOrder === SortOrder.Asc ? SortOrder.Desc : SortOrder.Asc); + } else { + setSortColumn(column); + setSortOrder(SortOrder.Desc); + } + }; + + return ( +
+
+
+
+ + + + {columns.map((col) => ( + + {col.label} + + ))} + + + + {sortedWallets.length > 0 + ? sortedWallets.map((wallet) => ( + + )) + : new Array(10) + .fill(0) + .map((_, index) => )} + +
+
+
+
+
+ ); +}; + +export default CommunityWallets; diff --git a/web-app/src/modules/core/routes/CommunityWallets/routes/Details.tsx b/web-app/src/modules/core/routes/CommunityWallets/routes/Details.tsx new file mode 100644 index 0000000..41788c2 --- /dev/null +++ b/web-app/src/modules/core/routes/CommunityWallets/routes/Details.tsx @@ -0,0 +1,128 @@ +import { useState, useEffect } from 'react'; +import _ from 'lodash'; +import { gql, useQuery } from '@apollo/client'; +import { CheckIcon, XMarkIcon } from '@heroicons/react/20/solid'; + +import { SortOrder, SortableTh } from '../../../../ui/Table'; +import { ICommunityWalletDetails } from '../../../../interface/CommunityWallets.interface'; +import AccountAddress from '../../../../ui/AccountAddress'; + +const GET_COMMUNITY_WALLETS_DETAILS = gql` + query GetCommunityWalletsDetails { + getCommunityWalletsDetails { + address + name + isMultiAction + threshold + totalPaid + balance + payees + } + } +`; + +const formatCoin = (value: number) => { + return value + ? value.toLocaleString('en-US', { + maximumFractionDigits: 0, + }) + : value; +}; + +const formatThreshold = (threshold: number[]) => { + if (Array.isArray(threshold) && threshold.length === 2) { + return `${threshold[0]} / ${threshold[1]}`; + } + return ''; +}; + +const columns = [ + { key: 'address', label: 'Address', className: 'text-left' }, + { key: 'name', label: 'Name', className: '' }, + { key: 'isMultiAction', label: 'Multisign', className: 'text-center' }, + { key: 'threshold', label: 'Threshold', className: 'text-center' }, + { key: 'payees', label: '#Payees', className: 'text-right' }, + { key: 'totalPaid', label: 'Payments', className: 'text-right' }, + { key: 'balance', label: 'Balance', className: 'text-right' }, +]; + +const Details = () => { + const [sortColumn, setSortColumn] = useState('isMultiAction'); + const [sortOrder, setSortOrder] = useState(SortOrder.Asc); + const [sortedDetails, setSortedDetails] = useState([]); + const { data } = useQuery<{ + getCommunityWalletsDetails: ICommunityWalletDetails[]; + }>(GET_COMMUNITY_WALLETS_DETAILS); + const details = data?.getCommunityWalletsDetails; + + useEffect(() => { + const sortedDetails = _.sortBy(details ?? [], sortColumn); + if (sortOrder === SortOrder.Desc) { + sortedDetails.reverse(); + } + setSortedDetails(sortedDetails); + }, [details, sortColumn, sortOrder]); + + const handleSort = (column: string) => { + if (sortColumn === column) { + setSortOrder(sortOrder === SortOrder.Asc ? SortOrder.Desc : SortOrder.Asc); + } else { + setSortColumn(column); + setSortOrder(SortOrder.Desc); + } + }; + + return ( +
+ + + + {columns.map((col) => ( + + {col.label} + + ))} + + + + {sortedDetails.length > 0 ? ( + sortedDetails.map((wallet, index) => ( + + + + + + + + + + )) + ) : ( + + + + )} + +
+ + {wallet.name} + {wallet.isMultiAction ? ( + + ) : ( + + )} + {formatThreshold(wallet.threshold)}{wallet.payees}{formatCoin(wallet.totalPaid)}{formatCoin(wallet.balance)}
+ No data available +
+
+ ); +}; + +export default Details; diff --git a/web-app/src/modules/core/routes/CommunityWallets/routes/Transactions.tsx b/web-app/src/modules/core/routes/CommunityWallets/routes/Transactions.tsx new file mode 100644 index 0000000..03b1790 --- /dev/null +++ b/web-app/src/modules/core/routes/CommunityWallets/routes/Transactions.tsx @@ -0,0 +1,94 @@ +import { useState } from 'react'; +import { gql, useQuery } from '@apollo/client'; + +import PaymentsTable from '../components/PaymentsTable'; +import { PaymentStatus } from '../components/types'; +import { ICommunityWalletPayments } from '../../../../interface/CommunityWallets.interface'; + +const GET_COMMUNITY_WALLETS_PAYMENTS = gql` + query GetCommunityWalletsPayments { + getCommunityWalletsPayments { + address + name + paid { + deadline + payee + description + value + status + } + pending { + deadline + payee + description + value + status + } + vetoed { + deadline + payee + description + value + status + } + } + } +`; + +const Transactions = () => { + const [selectedStatus, setSelectedStatus] = useState(PaymentStatus.Paid); + const { data } = useQuery<{ + getCommunityWalletsPayments: ICommunityWalletPayments[]; + }>(GET_COMMUNITY_WALLETS_PAYMENTS); + + const payments = data?.getCommunityWalletsPayments ?? []; + + return ( +
+
+ + + +
+ +
+ +
+
+ +
+
+ +
+
+ ); +}; + +export default Transactions; diff --git a/web-app/src/modules/core/routes/Home/Home.tsx b/web-app/src/modules/core/routes/Home/Home.tsx index 4cf0a16..1ce86e3 100644 --- a/web-app/src/modules/core/routes/Home/Home.tsx +++ b/web-app/src/modules/core/routes/Home/Home.tsx @@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'; import Page from '../../../ui/Page/Page'; import TransactionsTable from '../../../ui/UserTransactionsTable'; -import Stats from './Stats/Stats'; +import Stats from './Stats'; const GET_USER_TRANSACTIONS = gql` query GetUserTransactions { @@ -35,13 +35,6 @@ const Transactions: FC = () => { const Home: FC = () => { return ( -
-
-
-

Open Libra Explorer

-
-
-
diff --git a/web-app/src/modules/core/routes/Home/Stats/Stats.tsx b/web-app/src/modules/core/routes/Home/Stats/Stats.tsx index c3420bf..c3892d6 100644 --- a/web-app/src/modules/core/routes/Home/Stats/Stats.tsx +++ b/web-app/src/modules/core/routes/Home/Stats/Stats.tsx @@ -68,7 +68,6 @@ const Stats: FC = () => { const blockResource = await aptos.getAccountResource('0x1', '0x1::block::BlockResource'); const epochIntervalMs = parseInt((blockResource.data as any).epoch_interval, 10) / 1_000; - console.log('epochIntervalMs', epochIntervalMs); const events = await aptos.getEventsByEventHandle( '0x1', diff --git a/web-app/src/modules/core/routes/Home/Stats/index.ts b/web-app/src/modules/core/routes/Home/Stats/index.ts new file mode 100644 index 0000000..cbde4ea --- /dev/null +++ b/web-app/src/modules/core/routes/Home/Stats/index.ts @@ -0,0 +1 @@ +export { default } from './Stats'; diff --git a/web-app/src/modules/core/routes/Stats/Stats.tsx b/web-app/src/modules/core/routes/Stats/Stats.tsx index d921390..e09e375 100644 --- a/web-app/src/modules/core/routes/Stats/Stats.tsx +++ b/web-app/src/modules/core/routes/Stats/Stats.tsx @@ -7,7 +7,6 @@ import ChartComponent from './ChartComponent'; import StatItem from './components/StatItem'; import StatsContainer from './components/StatsContainer'; import Page from '../../../ui/Page'; -import AccountAddress from '../../../ui/AccountAddress'; const getData = async () => { const res = await fetch(`${config.apiHost}/stats`); diff --git a/web-app/src/modules/ui/StatsCard/StatsCard.tsx b/web-app/src/modules/ui/StatsCard/StatsCard.tsx index d1e6b15..c7bd711 100644 --- a/web-app/src/modules/ui/StatsCard/StatsCard.tsx +++ b/web-app/src/modules/ui/StatsCard/StatsCard.tsx @@ -1,15 +1,14 @@ -// src/ui/StatsCard.tsx import { FC, ReactNode } from 'react'; interface StatsCardProps { title: string; - value: string | number | null; + value: string | number | null | undefined; children?: ReactNode; loading?: boolean; } const StatsCard: FC = ({ title, value, children, loading = false }) => { - const isLoading = loading || value === null; + const isLoading = loading || value === null || value === undefined; return (
diff --git a/web-app/src/modules/ui/Table/SortableTh.tsx b/web-app/src/modules/ui/Table/SortableTh.tsx index b27d81a..c58d58c 100644 --- a/web-app/src/modules/ui/Table/SortableTh.tsx +++ b/web-app/src/modules/ui/Table/SortableTh.tsx @@ -1,16 +1,17 @@ -import { FC } from 'react'; +import { FC, ReactNode } from 'react'; import { ArrowUpIcon, ArrowDownIcon } from '@heroicons/react/20/solid'; +import { SortOrder } from './types'; interface SortableThProps { column: string; sortColumn: string; - sortOrder: 'asc' | 'desc'; + sortOrder: SortOrder; onSort: (column: string) => void; className?: string; - children: React.ReactNode; + children: ReactNode; } -const SortableTh: FC = ({ +export const SortableTh: FC = ({ column, sortColumn, sortOrder, @@ -26,7 +27,7 @@ const SortableTh: FC = ({ > {children} {sortColumn === column && - (sortOrder === 'asc' ? ( + (sortOrder === SortOrder.Asc ? ( ) : ( @@ -34,5 +35,3 @@ const SortableTh: FC = ({ ); }; - -export default SortableTh; diff --git a/web-app/src/modules/ui/Table/index.ts b/web-app/src/modules/ui/Table/index.ts new file mode 100644 index 0000000..b3c8cc1 --- /dev/null +++ b/web-app/src/modules/ui/Table/index.ts @@ -0,0 +1,3 @@ + +export { SortOrder } from './types'; +export { SortableTh } from './SortableTh'; diff --git a/web-app/src/modules/ui/Table/types.ts b/web-app/src/modules/ui/Table/types.ts new file mode 100644 index 0000000..346ae73 --- /dev/null +++ b/web-app/src/modules/ui/Table/types.ts @@ -0,0 +1,4 @@ +export enum SortOrder { + Asc, + Desc, +}