mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
restored "Publishers with most sales" section (#1540)
* updated subgraph query * rework AccountList * fix missing 'a' elem in dom (<Link> is not rendering correct markup) * restore PublishersWithMostSales section * removed unnecessary params in AssetList * use Blockies for all profile.image * changed logic on getTopAssetsPublishers * added aggregations op to getTopAssetsPublishers method * fix build issues * improve logic & added correct total sales on profile * removed complex logic to unify query * typography & markup cleanup Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
2d2ad78f89
commit
42956a3441
1
src/@types/aquarius/BaseQueryParams.d.ts
vendored
1
src/@types/aquarius/BaseQueryParams.d.ts
vendored
@ -9,6 +9,7 @@ interface BaseQueryParams {
|
|||||||
nestedQuery?: any
|
nestedQuery?: any
|
||||||
esPaginationOptions?: EsPaginationOptions
|
esPaginationOptions?: EsPaginationOptions
|
||||||
sortOptions?: SortOptions
|
sortOptions?: SortOptions
|
||||||
|
aggs?: any
|
||||||
filters?: FilterTerm[]
|
filters?: FilterTerm[]
|
||||||
ignorePurgatory?: boolean
|
ignorePurgatory?: boolean
|
||||||
}
|
}
|
||||||
|
1
src/@types/aquarius/PagedAssets.d.ts
vendored
1
src/@types/aquarius/PagedAssets.d.ts
vendored
@ -3,4 +3,5 @@ interface PagedAssets {
|
|||||||
page: number
|
page: number
|
||||||
totalPages: number
|
totalPages: number
|
||||||
totalResults: number
|
totalResults: number
|
||||||
|
aggregations: any
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@ export enum SortDirectionOptions {
|
|||||||
|
|
||||||
export enum SortTermOptions {
|
export enum SortTermOptions {
|
||||||
Created = 'metadata.created',
|
Created = 'metadata.created',
|
||||||
Relevance = '_score'
|
Relevance = '_score',
|
||||||
|
Stats = 'stats.orders'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: could not figure out how to get `enum` to be ambiant
|
// Note: could not figure out how to get `enum` to be ambiant
|
||||||
@ -43,5 +44,6 @@ declare global {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
query: any
|
query: any
|
||||||
sort?: { [jsonPath: string]: SortDirectionOptions }
|
sort?: { [jsonPath: string]: SortDirectionOptions }
|
||||||
|
aggs?: any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,10 @@ export function generateBaseQuery(
|
|||||||
}
|
}
|
||||||
} as SearchQuery
|
} as SearchQuery
|
||||||
|
|
||||||
|
if (baseQueryParams.aggs !== undefined) {
|
||||||
|
generatedQuery.aggs = baseQueryParams.aggs
|
||||||
|
}
|
||||||
|
|
||||||
if (baseQueryParams.sortOptions !== undefined)
|
if (baseQueryParams.sortOptions !== undefined)
|
||||||
generatedQuery.sort = {
|
generatedQuery.sort = {
|
||||||
[baseQueryParams.sortOptions.sortBy]:
|
[baseQueryParams.sortOptions.sortBy]:
|
||||||
@ -74,12 +78,15 @@ export function transformQueryResult(
|
|||||||
results: [],
|
results: [],
|
||||||
page: 0,
|
page: 0,
|
||||||
totalPages: 0,
|
totalPages: 0,
|
||||||
totalResults: 0
|
totalResults: 0,
|
||||||
|
aggregations: []
|
||||||
}
|
}
|
||||||
|
|
||||||
result.results = (queryResult.hits.hits || []).map(
|
result.results = (queryResult.hits.hits || []).map(
|
||||||
(hit) => hit._source as Asset
|
(hit) => hit._source as Asset
|
||||||
)
|
)
|
||||||
|
|
||||||
|
result.aggregations = queryResult.aggregations
|
||||||
result.totalResults = queryResult.hits.total.value
|
result.totalResults = queryResult.hits.total.value
|
||||||
result.totalPages =
|
result.totalPages =
|
||||||
result.totalResults / size < 1
|
result.totalResults / size < 1
|
||||||
@ -307,6 +314,13 @@ export async function getPublishedAssets(
|
|||||||
sortBy: SortTermOptions.Created,
|
sortBy: SortTermOptions.Created,
|
||||||
sortDirection: SortDirectionOptions.Descending
|
sortDirection: SortDirectionOptions.Descending
|
||||||
},
|
},
|
||||||
|
aggs: {
|
||||||
|
totalOrders: {
|
||||||
|
sum: {
|
||||||
|
field: SortTermOptions.Stats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
esPaginationOptions: {
|
esPaginationOptions: {
|
||||||
from: (Number(page) - 1 || 0) * 9,
|
from: (Number(page) - 1 || 0) * 9,
|
||||||
size: 9
|
size: 9
|
||||||
@ -314,6 +328,62 @@ export async function getPublishedAssets(
|
|||||||
} as BaseQueryParams
|
} as BaseQueryParams
|
||||||
|
|
||||||
const query = generateBaseQuery(baseQueryParams)
|
const query = generateBaseQuery(baseQueryParams)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await queryMetadata(query, cancelToken)
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isCancel(error)) {
|
||||||
|
LoggerInstance.log(error.message)
|
||||||
|
} else {
|
||||||
|
LoggerInstance.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTopPublishers(
|
||||||
|
chainIds: number[],
|
||||||
|
cancelToken: CancelToken,
|
||||||
|
page?: number,
|
||||||
|
type?: string,
|
||||||
|
accesType?: string
|
||||||
|
): Promise<PagedAssets> {
|
||||||
|
const filters: FilterTerm[] = []
|
||||||
|
|
||||||
|
accesType !== undefined &&
|
||||||
|
filters.push(getFilterTerm('services.type', accesType))
|
||||||
|
type !== undefined && filters.push(getFilterTerm('metadata.type', type))
|
||||||
|
|
||||||
|
const baseQueryParams = {
|
||||||
|
chainIds,
|
||||||
|
filters,
|
||||||
|
sortOptions: {
|
||||||
|
sortBy: SortTermOptions.Created,
|
||||||
|
sortDirection: SortDirectionOptions.Descending
|
||||||
|
},
|
||||||
|
aggs: {
|
||||||
|
topPublishers: {
|
||||||
|
terms: {
|
||||||
|
field: 'nft.owner.keyword',
|
||||||
|
order: { totalSales: 'desc' }
|
||||||
|
},
|
||||||
|
aggs: {
|
||||||
|
totalSales: {
|
||||||
|
sum: {
|
||||||
|
field: SortTermOptions.Stats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
esPaginationOptions: {
|
||||||
|
from: (Number(page) - 1 || 0) * 9,
|
||||||
|
size: 9
|
||||||
|
}
|
||||||
|
} as BaseQueryParams
|
||||||
|
|
||||||
|
const query = generateBaseQuery(baseQueryParams)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await queryMetadata(query, cancelToken)
|
const result = await queryMetadata(query, cancelToken)
|
||||||
return result
|
return result
|
||||||
|
@ -18,7 +18,7 @@ import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery'
|
|||||||
import { calcSingleOutGivenPoolIn } from './pool'
|
import { calcSingleOutGivenPoolIn } from './pool'
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
import { MAX_DECIMALS } from './constants'
|
import { MAX_DECIMALS } from './constants'
|
||||||
|
import { getPublishedAssets, getTopPublishers } from '@utils/aquarius'
|
||||||
export interface UserLiquidity {
|
export interface UserLiquidity {
|
||||||
price: string
|
price: string
|
||||||
oceanBalance: string
|
oceanBalance: string
|
||||||
@ -172,19 +172,11 @@ const UserSalesQuery = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// TODO: figure out some way to get this
|
|
||||||
const TopSalesQuery = gql`
|
const TopSalesQuery = gql`
|
||||||
query TopSalesQuery {
|
query TopSalesQuery {
|
||||||
users(
|
users(first: 20, orderBy: totalSales, orderDirection: desc) {
|
||||||
first: 20
|
|
||||||
orderBy: sharesOwned
|
|
||||||
orderDirection: desc
|
|
||||||
where: { tokenBalancesOwned_not: "0" }
|
|
||||||
) {
|
|
||||||
id
|
id
|
||||||
tokenBalancesOwned {
|
totalSales
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -419,20 +411,10 @@ export async function getUserSales(
|
|||||||
accountId: string,
|
accountId: string,
|
||||||
chainIds: number[]
|
chainIds: number[]
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
const variables = { user: accountId?.toLowerCase() }
|
|
||||||
try {
|
try {
|
||||||
const userSales = await fetchDataForMultipleChains(
|
const result = await getPublishedAssets(accountId, chainIds, null)
|
||||||
UserSalesQuery,
|
const { totalOrders } = result.aggregations
|
||||||
variables,
|
return totalOrders.value
|
||||||
chainIds
|
|
||||||
)
|
|
||||||
let salesSum = 0
|
|
||||||
for (let i = 0; i < userSales.length; i++) {
|
|
||||||
if (userSales[i].users.length > 0) {
|
|
||||||
salesSum += parseInt(userSales[i].users[0].totalSales)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return salesSum
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
LoggerInstance.error('Error getUserSales', error.message)
|
LoggerInstance.error('Error getUserSales', error.message)
|
||||||
}
|
}
|
||||||
@ -442,33 +424,19 @@ export async function getTopAssetsPublishers(
|
|||||||
chainIds: number[],
|
chainIds: number[],
|
||||||
nrItems = 9
|
nrItems = 9
|
||||||
): Promise<AccountTeaserVM[]> {
|
): Promise<AccountTeaserVM[]> {
|
||||||
const publisherSales: AccountTeaserVM[] = []
|
const publishers: AccountTeaserVM[] = []
|
||||||
|
|
||||||
for (const chain of chainIds) {
|
const result = await getTopPublishers(chainIds, null)
|
||||||
const queryContext = getQueryContext(Number(chain))
|
const { topPublishers } = result.aggregations
|
||||||
const fetchedUsers: OperationResult<UsersSalesList> = await fetchData(
|
|
||||||
TopSalesQuery,
|
for (let i = 0; i < topPublishers.buckets.length; i++) {
|
||||||
null,
|
publishers.push({
|
||||||
queryContext
|
address: topPublishers.buckets[i].key,
|
||||||
)
|
nrSales: parseInt(topPublishers.buckets[i].totalSales.value)
|
||||||
for (let i = 0; i < fetchedUsers.data.users.length; i++) {
|
})
|
||||||
const publishersIndex = publisherSales.findIndex(
|
|
||||||
(user) => fetchedUsers.data.users[i].id === user.address
|
|
||||||
)
|
|
||||||
if (publishersIndex === -1) {
|
|
||||||
const publisher: AccountTeaserVM = {
|
|
||||||
address: fetchedUsers.data.users[i].id,
|
|
||||||
nrSales: fetchedUsers.data.users[i].totalSales
|
|
||||||
}
|
|
||||||
publisherSales.push(publisher)
|
|
||||||
} else {
|
|
||||||
publisherSales[publishersIndex].nrSales +=
|
|
||||||
publisherSales[publishersIndex].nrSales
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publisherSales.sort((a, b) => b.nrSales - a.nrSales)
|
publishers.sort((a, b) => b.nrSales - a.nrSales)
|
||||||
|
|
||||||
return publisherSales.slice(0, nrItems)
|
return publishers.slice(0, nrItems)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './AssetList.module.css'
|
import styles from './index.module.css'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import Loader from '../atoms/Loader'
|
import Loader from '../atoms/Loader'
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
@ -29,7 +29,7 @@ export default function AccountList({
|
|||||||
const { chainIds } = useUserPreferences()
|
const { chainIds } = useUserPreferences()
|
||||||
|
|
||||||
const styleClasses = cx({
|
const styleClasses = cx({
|
||||||
assetList: true,
|
accountList: true,
|
||||||
[className]: className
|
[className]: className
|
||||||
})
|
})
|
||||||
|
|
||||||
|
24
src/components/@shared/AccountList/index.module.css
Normal file
24
src/components/@shared/AccountList/index.module.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
.accountList {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 25rem) {
|
||||||
|
.accountList {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
|
||||||
|
gap: var(--spacer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loaderWrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
@ -1,46 +1,39 @@
|
|||||||
.blockies {
|
.blockies {
|
||||||
aspect-ratio: 1/1;
|
aspect-ratio: 1/1;
|
||||||
width: 13%;
|
width: calc(var(--font-size-large) * 2) !important;
|
||||||
height: 13%;
|
height: calc(var(--font-size-large) * 2) !important;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: calc(var(--spacer) / 4);
|
margin-right: calc(var(--spacer) / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.teaser {
|
.teaser {
|
||||||
max-width: 40rem;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
composes: box from '../atoms/Box.module.css';
|
composes: box from '../atoms/Box.module.css';
|
||||||
padding: calc(var(--spacer) / 2) !important;
|
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 2);
|
||||||
font-size: var(--font-size-mini);
|
|
||||||
height: 90%;
|
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link span {
|
|
||||||
font-size: var(--font-size-large);
|
|
||||||
margin-right: calc(var(--spacer) / 3);
|
|
||||||
}
|
|
||||||
.name {
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-size: var(--font-size-base) !important;
|
|
||||||
padding-top: calc(var(--spacer) / 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sales {
|
.place {
|
||||||
font-size: small;
|
font-size: var(--font-size-large);
|
||||||
margin-top: -5px !important;
|
margin-right: calc(var(--spacer) / 2);
|
||||||
margin-bottom: clac(var(--spacer) / 2);
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
padding-top: calc(var(--spacer) / 5);
|
||||||
|
color: var(--font-color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales span {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
color: var(--font-color-text);
|
||||||
}
|
}
|
||||||
|
@ -33,34 +33,26 @@ export default function AccountTeaser({
|
|||||||
}, [accountTeaserVM, newCancelToken])
|
}, [accountTeaserVM, newCancelToken])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={styles.teaser}>
|
|
||||||
<Link href={`/profile/${accountTeaserVM.address}`}>
|
<Link href={`/profile/${accountTeaserVM.address}`}>
|
||||||
<header className={styles.header}>
|
<a className={styles.teaser}>
|
||||||
{place && <span>{place}</span>}
|
{place && <span className={styles.place}>{place}</span>}
|
||||||
{profile?.image ? (
|
|
||||||
<img src={profile.image} className={styles.blockies} />
|
|
||||||
) : (
|
|
||||||
<Blockies
|
<Blockies
|
||||||
accountId={accountTeaserVM.address}
|
accountId={accountTeaserVM.address}
|
||||||
className={styles.blockies}
|
className={styles.blockies}
|
||||||
|
image={profile?.image}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<div>
|
<div>
|
||||||
<Dotdotdot clamp={3}>
|
<Dotdotdot tagName="h4" clamp={2} className={styles.name}>
|
||||||
<h3 className={styles.name}>
|
|
||||||
{profile?.name
|
{profile?.name
|
||||||
? profile?.name
|
? profile?.name
|
||||||
: accountTruncate(accountTeaserVM.address)}
|
: accountTruncate(accountTeaserVM.address)}
|
||||||
</h3>
|
|
||||||
</Dotdotdot>
|
</Dotdotdot>
|
||||||
<p className={styles.sales}>
|
<p className={styles.sales}>
|
||||||
{`${accountTeaserVM.nrSales} ${
|
<span>{accountTeaserVM.nrSales}</span>
|
||||||
accountTeaserVM.nrSales === 1 ? 'sale' : 'sales'
|
{`${accountTeaserVM.nrSales === 1 ? ' sale' : ' sales'}`}
|
||||||
}`}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</article>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,13 @@ import styles from './index.module.css'
|
|||||||
export interface BlockiesProps {
|
export interface BlockiesProps {
|
||||||
accountId: string
|
accountId: string
|
||||||
className?: string
|
className?: string
|
||||||
|
image?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Blockies({
|
export default function Blockies({
|
||||||
accountId,
|
accountId,
|
||||||
className
|
className,
|
||||||
|
image
|
||||||
}: BlockiesProps): ReactElement {
|
}: BlockiesProps): ReactElement {
|
||||||
if (!accountId) return null
|
if (!accountId) return null
|
||||||
|
|
||||||
@ -18,7 +20,7 @@ export default function Blockies({
|
|||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
className={`${className || ''} ${styles.blockies} `}
|
className={`${className || ''} ${styles.blockies} `}
|
||||||
src={blockies}
|
src={image || blockies}
|
||||||
alt="Blockies"
|
alt="Blockies"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { useUserPreferences } from '@context/UserPreferences'
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
|
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||||
import AccountList from '@shared/AccountList/AccountList'
|
import AccountList from '@shared/AccountList/AccountList'
|
||||||
import { getTopAssetsPublishers } from '@utils/subgraph'
|
import { getTopAssetsPublishers } from '@utils/subgraph'
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import styles from './Home.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
export default function PublishersWithMostSales({
|
export default function PublishersWithMostSales({
|
||||||
title,
|
title,
|
||||||
@ -17,18 +18,19 @@ export default function PublishersWithMostSales({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function init() {
|
async function init() {
|
||||||
|
setLoading(true)
|
||||||
if (chainIds.length === 0) {
|
if (chainIds.length === 0) {
|
||||||
const result: AccountTeaserVM[] = []
|
const result: AccountTeaserVM[] = []
|
||||||
setResult(result)
|
setResult(result)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
|
||||||
const publishers = await getTopAssetsPublishers(chainIds)
|
const publishers = await getTopAssetsPublishers(chainIds)
|
||||||
setResult(publishers)
|
setResult(publishers)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Logger.error(error.message)
|
LoggerInstance.error(error.message)
|
||||||
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import styles from './index.module.css'
|
|||||||
import { useIsMounted } from '@hooks/useIsMounted'
|
import { useIsMounted } from '@hooks/useIsMounted'
|
||||||
import { useCancelToken } from '@hooks/useCancelToken'
|
import { useCancelToken } from '@hooks/useCancelToken'
|
||||||
import { SortTermOptions } from '../../@types/aquarius/SearchQuery'
|
import { SortTermOptions } from '../../@types/aquarius/SearchQuery'
|
||||||
|
import PublishersWithMostSales from './PublishersWithMostSales'
|
||||||
|
|
||||||
async function getQueryHighest(
|
async function getQueryHighest(
|
||||||
chainIds: number[]
|
chainIds: number[]
|
||||||
@ -66,7 +67,8 @@ function SectionQueryResult({
|
|||||||
results: [],
|
results: [],
|
||||||
page: 0,
|
page: 0,
|
||||||
totalPages: 0,
|
totalPages: 0,
|
||||||
totalResults: 0
|
totalResults: 0,
|
||||||
|
aggregations: undefined
|
||||||
}
|
}
|
||||||
setResult(result)
|
setResult(result)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@ -153,6 +155,8 @@ export default function HomePage(): ReactElement {
|
|||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<PublishersWithMostSales title="Publishers With Most Sales" />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user