mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Merge branch 'main' into feature/issue-1244-edit-ui-links-validation
This commit is contained in:
commit
16d7077c9f
@ -14,7 +14,7 @@
|
|||||||
"link": "/profile"
|
"link": "/profile"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"announcement": "Explore [OceanONDA V4](https://blog.oceanprotocol.com/how-to-publish-a-data-nft-f58ad2a622a9).",
|
"announcement": "[Lock your OCEAN](https://df.oceandao.org/) to get veOCEAN, earn rewards and curate data.",
|
||||||
"warning": {
|
"warning": {
|
||||||
"ctd": "Compute-to-Data is still in a testing phase, please use it only on test networks."
|
"ctd": "Compute-to-Data is still in a testing phase, please use it only on test networks."
|
||||||
}
|
}
|
||||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -13,7 +13,7 @@
|
|||||||
"@coingecko/cryptoformat": "^0.5.4",
|
"@coingecko/cryptoformat": "^0.5.4",
|
||||||
"@loadable/component": "^5.15.2",
|
"@loadable/component": "^5.15.2",
|
||||||
"@oceanprotocol/art": "^3.2.0",
|
"@oceanprotocol/art": "^3.2.0",
|
||||||
"@oceanprotocol/lib": "^2.2.1",
|
"@oceanprotocol/lib": "^2.2.3",
|
||||||
"@oceanprotocol/typographies": "^0.1.0",
|
"@oceanprotocol/typographies": "^0.1.0",
|
||||||
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
@ -4539,9 +4539,9 @@
|
|||||||
"integrity": "sha512-Oe+oBRiu1dlco9PQ7eUYcTYi2Nua69S3TiSw62H46AIpwnFK8ORuO0Ny20No++KisBA9F+84b5lDn6kQy5Lt/Q=="
|
"integrity": "sha512-Oe+oBRiu1dlco9PQ7eUYcTYi2Nua69S3TiSw62H46AIpwnFK8ORuO0Ny20No++KisBA9F+84b5lDn6kQy5Lt/Q=="
|
||||||
},
|
},
|
||||||
"node_modules/@oceanprotocol/lib": {
|
"node_modules/@oceanprotocol/lib": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-2.2.3.tgz",
|
||||||
"integrity": "sha512-HNmT3DJJeyvFRwCbmgJucGpte90epIhgSy+68PSc83TLKRW2CF4N1mioMkoGxMwnK3rJzj6tEy4R9NKKLbdT5w==",
|
"integrity": "sha512-jBD6bD1dPd7MHiiMA0V2hBntHM0vhmYpzeuv2dNj/2i1FNvTzpQO3v4a5V+Ahxs6L6YirBBfzT3HHtGKOghc5w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oceanprotocol/contracts": "^1.1.7",
|
"@oceanprotocol/contracts": "^1.1.7",
|
||||||
"bignumber.js": "^9.1.0",
|
"bignumber.js": "^9.1.0",
|
||||||
@ -44803,9 +44803,9 @@
|
|||||||
"integrity": "sha512-Oe+oBRiu1dlco9PQ7eUYcTYi2Nua69S3TiSw62H46AIpwnFK8ORuO0Ny20No++KisBA9F+84b5lDn6kQy5Lt/Q=="
|
"integrity": "sha512-Oe+oBRiu1dlco9PQ7eUYcTYi2Nua69S3TiSw62H46AIpwnFK8ORuO0Ny20No++KisBA9F+84b5lDn6kQy5Lt/Q=="
|
||||||
},
|
},
|
||||||
"@oceanprotocol/lib": {
|
"@oceanprotocol/lib": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-2.2.3.tgz",
|
||||||
"integrity": "sha512-HNmT3DJJeyvFRwCbmgJucGpte90epIhgSy+68PSc83TLKRW2CF4N1mioMkoGxMwnK3rJzj6tEy4R9NKKLbdT5w==",
|
"integrity": "sha512-jBD6bD1dPd7MHiiMA0V2hBntHM0vhmYpzeuv2dNj/2i1FNvTzpQO3v4a5V+Ahxs6L6YirBBfzT3HHtGKOghc5w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@oceanprotocol/contracts": "^1.1.7",
|
"@oceanprotocol/contracts": "^1.1.7",
|
||||||
"bignumber.js": "^9.1.0",
|
"bignumber.js": "^9.1.0",
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
"@coingecko/cryptoformat": "^0.5.4",
|
"@coingecko/cryptoformat": "^0.5.4",
|
||||||
"@loadable/component": "^5.15.2",
|
"@loadable/component": "^5.15.2",
|
||||||
"@oceanprotocol/art": "^3.2.0",
|
"@oceanprotocol/art": "^3.2.0",
|
||||||
"@oceanprotocol/lib": "^2.2.1",
|
"@oceanprotocol/lib": "^2.2.3",
|
||||||
"@oceanprotocol/typographies": "^0.1.0",
|
"@oceanprotocol/typographies": "^0.1.0",
|
||||||
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
|
@ -32,22 +32,25 @@ export function getNetworkDisplayName(
|
|||||||
displayName = 'Polygon'
|
displayName = 'Polygon'
|
||||||
break
|
break
|
||||||
case 1287:
|
case 1287:
|
||||||
displayName = 'Moonbase Alpha'
|
displayName = 'Moonbase'
|
||||||
break
|
break
|
||||||
case 1285:
|
case 1285:
|
||||||
displayName = 'Moonriver'
|
displayName = 'Moonriver'
|
||||||
break
|
break
|
||||||
case 80001:
|
case 80001:
|
||||||
displayName = 'Polygon Mumbai'
|
displayName = 'Mumbai'
|
||||||
break
|
break
|
||||||
case 8996:
|
case 8996:
|
||||||
displayName = 'Development'
|
displayName = 'Development'
|
||||||
break
|
break
|
||||||
case 3:
|
case 3:
|
||||||
displayName = 'ETH Ropsten'
|
displayName = 'Ropsten'
|
||||||
|
break
|
||||||
|
case 5:
|
||||||
|
displayName = 'Görli'
|
||||||
break
|
break
|
||||||
case 2021000:
|
case 2021000:
|
||||||
displayName = 'GAIA-X Testnet'
|
displayName = 'GAIA-X'
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
displayName = data
|
displayName = data
|
||||||
|
@ -7,7 +7,8 @@ export enum SortTermOptions {
|
|||||||
Created = 'nft.created',
|
Created = 'nft.created',
|
||||||
Relevance = '_score',
|
Relevance = '_score',
|
||||||
Orders = 'stats.orders',
|
Orders = 'stats.orders',
|
||||||
Allocated = 'stats.allocated'
|
Allocated = 'stats.allocated',
|
||||||
|
Price = 'stats.price.value'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: could not figure out how to get `enum` to be ambiant
|
// Note: could not figure out how to get `enum` to be ambiant
|
||||||
|
@ -215,6 +215,28 @@ export async function getAssetsFromDtList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getAssetsFromNftList(
|
||||||
|
nftList: string[],
|
||||||
|
chainIds: number[],
|
||||||
|
cancelToken: CancelToken
|
||||||
|
): Promise<Asset[]> {
|
||||||
|
try {
|
||||||
|
if (!(nftList.length > 0)) return
|
||||||
|
|
||||||
|
const baseParams = {
|
||||||
|
chainIds,
|
||||||
|
filters: [getFilterTerm('nftAddress', nftList)],
|
||||||
|
ignorePurgatory: true
|
||||||
|
} as BaseQueryParams
|
||||||
|
const query = generateBaseQuery(baseParams)
|
||||||
|
|
||||||
|
const queryResult = await queryMetadata(query, cancelToken)
|
||||||
|
return queryResult?.results
|
||||||
|
} catch (error) {
|
||||||
|
LoggerInstance.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function retrieveDDOListByDIDs(
|
export async function retrieveDDOListByDIDs(
|
||||||
didList: string[],
|
didList: string[],
|
||||||
chainIds: number[],
|
chainIds: number[],
|
||||||
|
@ -1,7 +1,19 @@
|
|||||||
import { AllLocked } from 'src/@types/subgraph/AllLocked'
|
import { AllLocked } from 'src/@types/subgraph/AllLocked'
|
||||||
|
import { OwnAllocations } from 'src/@types/subgraph/OwnAllocations'
|
||||||
|
import { NftOwnAllocation } from 'src/@types/subgraph/NftOwnAllocation'
|
||||||
|
import { OceanLocked } from 'src/@types/subgraph/OceanLocked'
|
||||||
import { gql, OperationResult } from 'urql'
|
import { gql, OperationResult } from 'urql'
|
||||||
import { fetchData, getQueryContext } from './subgraph'
|
import { fetchData, getQueryContext } from './subgraph'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import networkdata from '../../content/networks-metadata.json'
|
||||||
|
import {
|
||||||
|
getNetworkDataById,
|
||||||
|
getNetworkType,
|
||||||
|
NetworkType
|
||||||
|
} from '@hooks/useNetworkMetadata'
|
||||||
|
import { getAssetsFromNftList } from './aquarius'
|
||||||
|
import { chainIdsSupported } from 'app.config'
|
||||||
|
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
const AllLocked = gql`
|
const AllLocked = gql`
|
||||||
query AllLocked {
|
query AllLocked {
|
||||||
@ -11,10 +23,82 @@ const AllLocked = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
interface TotalVe {
|
const OwnAllocations = gql`
|
||||||
|
query OwnAllocations($address: String) {
|
||||||
|
veAllocations(where: { allocationUser: $address }) {
|
||||||
|
id
|
||||||
|
nftAddress
|
||||||
|
allocated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const NftOwnAllocation = gql`
|
||||||
|
query NftOwnAllocation($address: String, $nftAddress: String) {
|
||||||
|
veAllocations(
|
||||||
|
where: { allocationUser: $address, nftAddress: $nftAddress }
|
||||||
|
) {
|
||||||
|
allocated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const OceanLocked = gql`
|
||||||
|
query OceanLocked($address: String) {
|
||||||
|
veOCEAN(id: $address) {
|
||||||
|
id
|
||||||
|
lockedAmount
|
||||||
|
unlockTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export interface TotalVe {
|
||||||
totalLocked: number
|
totalLocked: number
|
||||||
totalAllocated: number
|
totalAllocated: number
|
||||||
}
|
}
|
||||||
|
export interface Allocation {
|
||||||
|
nftAddress: string
|
||||||
|
allocation: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetWithOwnAllocation {
|
||||||
|
asset: AssetExtended
|
||||||
|
allocation: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getVeChainNetworkId(assetNetworkId: number): number {
|
||||||
|
const networkData = getNetworkDataById(networkdata, assetNetworkId)
|
||||||
|
const networkType = getNetworkType(networkData)
|
||||||
|
if (networkType === NetworkType.Mainnet) return 1
|
||||||
|
else return 5
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getVeChainNetworkIds(assetNetworkIds: number[]): number[] {
|
||||||
|
const veNetworkIds: number[] = []
|
||||||
|
assetNetworkIds.forEach((x) => {
|
||||||
|
const id = getVeChainNetworkId(x)
|
||||||
|
veNetworkIds.indexOf(id) === -1 && veNetworkIds.push(id)
|
||||||
|
})
|
||||||
|
return veNetworkIds
|
||||||
|
}
|
||||||
|
export async function getNftOwnAllocation(
|
||||||
|
userAddress: string,
|
||||||
|
nftAddress: string,
|
||||||
|
networkId: number
|
||||||
|
): Promise<number> {
|
||||||
|
const veNetworkId = getVeChainNetworkId(networkId)
|
||||||
|
const queryContext = getQueryContext(veNetworkId)
|
||||||
|
const fetchedAllocation: OperationResult<NftOwnAllocation, any> =
|
||||||
|
await fetchData(
|
||||||
|
NftOwnAllocation,
|
||||||
|
{
|
||||||
|
address: userAddress.toLowerCase(),
|
||||||
|
nftAddress: nftAddress.toLowerCase()
|
||||||
|
},
|
||||||
|
queryContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return fetchedAllocation.data?.veAllocations[0]?.allocated
|
||||||
|
}
|
||||||
|
|
||||||
export async function getTotalAllocatedAndLocked(): Promise<TotalVe> {
|
export async function getTotalAllocatedAndLocked(): Promise<TotalVe> {
|
||||||
const totals = {
|
const totals = {
|
||||||
@ -26,7 +110,7 @@ export async function getTotalAllocatedAndLocked(): Promise<TotalVe> {
|
|||||||
|
|
||||||
const response = await axios.post(`https://df-sql.oceandao.org/nftinfo`)
|
const response = await axios.post(`https://df-sql.oceandao.org/nftinfo`)
|
||||||
totals.totalAllocated = response.data?.reduce(
|
totals.totalAllocated = response.data?.reduce(
|
||||||
(previousValue: number, currentValue: { ve_allocated: any }) =>
|
(previousValue: number, currentValue: { ve_allocated: string }) =>
|
||||||
previousValue + Number(currentValue.ve_allocated),
|
previousValue + Number(currentValue.ve_allocated),
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
@ -43,3 +127,66 @@ export async function getTotalAllocatedAndLocked(): Promise<TotalVe> {
|
|||||||
)
|
)
|
||||||
return totals
|
return totals
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getLocked(
|
||||||
|
userAddress: string,
|
||||||
|
networkIds: number[]
|
||||||
|
): Promise<number> {
|
||||||
|
let total = 0
|
||||||
|
const veNetworkIds = getVeChainNetworkIds(networkIds)
|
||||||
|
for (let i = 0; i < veNetworkIds.length; i++) {
|
||||||
|
const queryContext = getQueryContext(veNetworkIds[i])
|
||||||
|
const fetchedLocked: OperationResult<OceanLocked, any> = await fetchData(
|
||||||
|
OceanLocked,
|
||||||
|
{ address: userAddress.toLowerCase() },
|
||||||
|
queryContext
|
||||||
|
)
|
||||||
|
|
||||||
|
fetchedLocked.data?.veOCEAN?.lockedAmount &&
|
||||||
|
(total += Number(fetchedLocked.data?.veOCEAN?.lockedAmount))
|
||||||
|
}
|
||||||
|
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getOwnAllocations(
|
||||||
|
networkIds: number[],
|
||||||
|
userAddress: string
|
||||||
|
): Promise<Allocation[]> {
|
||||||
|
const allocations: Allocation[] = []
|
||||||
|
const veNetworkIds = getVeChainNetworkIds(networkIds)
|
||||||
|
for (let i = 0; i < veNetworkIds.length; i++) {
|
||||||
|
const queryContext = getQueryContext(veNetworkIds[i])
|
||||||
|
const fetchedAllocations: OperationResult<OwnAllocations, any> =
|
||||||
|
await fetchData(
|
||||||
|
OwnAllocations,
|
||||||
|
{ address: userAddress.toLowerCase() },
|
||||||
|
queryContext
|
||||||
|
)
|
||||||
|
|
||||||
|
fetchedAllocations.data?.veAllocations.forEach(
|
||||||
|
(x) =>
|
||||||
|
x.allocated !== '0' &&
|
||||||
|
allocations.push({
|
||||||
|
nftAddress: x.nftAddress,
|
||||||
|
allocation: x.allocated / 100
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allocations
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getOwnAssetsWithAllocation(
|
||||||
|
networkIds: number[],
|
||||||
|
userAddress: string
|
||||||
|
): Promise<Asset[]> {
|
||||||
|
const allocations = await getOwnAllocations(networkIds, userAddress)
|
||||||
|
const assets = await getAssetsFromNftList(
|
||||||
|
allocations.map((x) => x.nftAddress),
|
||||||
|
chainIdsSupported,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
return assets
|
||||||
|
}
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-left: 1px solid var(--border-color);
|
border-left: 1px solid var(--border-color);
|
||||||
padding-left: calc(var(--spacer) / 3.5);
|
padding-left: calc(var(--spacer) / 4);
|
||||||
margin-left: calc(var(--spacer) / 4);
|
margin-left: calc(var(--spacer) / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,9 @@ export default function AssetTeaser({
|
|||||||
type={type}
|
type={type}
|
||||||
accessType={accessType}
|
accessType={accessType}
|
||||||
/>
|
/>
|
||||||
<span className={styles.typeLabel}>{datatokens[0]?.symbol}</span>
|
<span className={styles.typeLabel}>
|
||||||
|
{datatokens[0]?.symbol.substring(0, 9)}
|
||||||
|
</span>
|
||||||
<NetworkName
|
<NetworkName
|
||||||
networkId={asset.chainId}
|
networkId={asset.chainId}
|
||||||
className={styles.typeLabel}
|
className={styles.typeLabel}
|
||||||
|
@ -9,7 +9,7 @@ import styles from './index.module.css'
|
|||||||
export function NetworkIcon({ name }: { name: string }): ReactElement {
|
export function NetworkIcon({ name }: { name: string }): ReactElement {
|
||||||
const IconMapped = name.includes('ETH')
|
const IconMapped = name.includes('ETH')
|
||||||
? EthIcon
|
? EthIcon
|
||||||
: name.includes('Polygon')
|
: name.includes('Polygon') || name.includes('Mumbai')
|
||||||
? PolygonIcon
|
? PolygonIcon
|
||||||
: name.includes('Moon')
|
: name.includes('Moon')
|
||||||
? MoonbeamIcon
|
? MoonbeamIcon
|
||||||
|
@ -18,7 +18,7 @@ export default function Conversion({
|
|||||||
const { prices } = usePrices()
|
const { prices } = usePrices()
|
||||||
const { currency, locale } = useUserPreferences()
|
const { currency, locale } = useUserPreferences()
|
||||||
|
|
||||||
const [priceConverted, setPriceConverted] = useState('0.00')
|
const [priceConverted, setPriceConverted] = useState('0')
|
||||||
// detect fiat, only have those kick in full @coingecko/cryptoformat formatting
|
// detect fiat, only have those kick in full @coingecko/cryptoformat formatting
|
||||||
const isFiat = !isCrypto(currency)
|
const isFiat = !isCrypto(currency)
|
||||||
// isCrypto() only checks for BTC & ETH & unknown but seems sufficient for now
|
// isCrypto() only checks for BTC & ETH & unknown but seems sufficient for now
|
||||||
@ -28,7 +28,7 @@ export default function Conversion({
|
|||||||
const priceTokenId = getCoingeckoTokenId(symbol)
|
const priceTokenId = getCoingeckoTokenId(symbol)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!prices || !price || !priceTokenId || !prices[priceTokenId]) {
|
if (!prices || !priceTokenId || !prices[priceTokenId]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export default function Conversion({
|
|||||||
isFiat ? currency : '',
|
isFiat ? currency : '',
|
||||||
locale,
|
locale,
|
||||||
false,
|
false,
|
||||||
{ decimalPlaces: 2 }
|
{ decimalPlaces: price === 0 ? 0 : 2 }
|
||||||
)
|
)
|
||||||
// It's a hack! Wrap everything in the string which is not a number or `.` or `,`
|
// It's a hack! Wrap everything in the string which is not a number or `.` or `,`
|
||||||
// with a span for consistent visual symbol formatting.
|
// with a span for consistent visual symbol formatting.
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price > div:firt-child {
|
.price > div:first-child {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
|
import { markdownToHtml } from '@utils/markdown'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Empty.module.css'
|
import styles from './Empty.module.css'
|
||||||
|
|
||||||
export default function Empty({ message }: { message?: string }): ReactElement {
|
export default function Empty({ message }: { message?: string }): ReactElement {
|
||||||
return <div className={styles.empty}>{message || 'No results found'}</div>
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.empty}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: markdownToHtml(message) || 'No results found'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,61 @@
|
|||||||
import { useAsset } from '@context/Asset'
|
import { useAsset } from '@context/Asset'
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import Tooltip from '@shared/atoms/Tooltip'
|
||||||
import { formatPrice } from '@shared/Price/PriceUnit'
|
import { formatPrice } from '@shared/Price/PriceUnit'
|
||||||
|
import { getNftOwnAllocation } from '@utils/veAllocation'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
export default function AssetStats() {
|
export default function AssetStats() {
|
||||||
const { locale } = useUserPreferences()
|
const { locale } = useUserPreferences()
|
||||||
const { asset } = useAsset()
|
const { asset } = useAsset()
|
||||||
const [orders, setOrders] = useState(0)
|
const { accountId } = useWeb3()
|
||||||
const [allocated, setAllocated] = useState(0)
|
|
||||||
|
const [ownAllocation, setOwnAllocation] = useState(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!asset) return
|
if (!asset || !accountId) return
|
||||||
|
|
||||||
const { orders, allocated } = asset.stats
|
async function init() {
|
||||||
|
const allocation = await getNftOwnAllocation(
|
||||||
setOrders(orders)
|
accountId,
|
||||||
setAllocated(allocated)
|
asset.nftAddress,
|
||||||
}, [asset])
|
asset.chainId
|
||||||
|
)
|
||||||
|
setOwnAllocation(allocation / 100)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
}, [accountId, asset])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className={styles.stats}>
|
<footer className={styles.stats}>
|
||||||
{allocated && allocated > 0 ? (
|
{asset?.stats?.allocated && asset?.stats?.allocated > 0 ? (
|
||||||
<span className={styles.stat}>
|
<span className={styles.stat}>
|
||||||
<span className={styles.number}>
|
<span className={styles.number}>
|
||||||
{formatPrice(allocated, locale)}
|
{formatPrice(asset.stats.allocated, locale)}
|
||||||
</span>
|
</span>
|
||||||
veOCEAN
|
veOCEAN
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{!asset || !asset?.stats || orders < 0 ? (
|
{!asset?.stats || asset?.stats?.orders < 0 ? (
|
||||||
'N/A'
|
'N/A'
|
||||||
) : orders === 0 ? (
|
) : asset?.stats?.orders === 0 ? (
|
||||||
'No sales yet'
|
'No sales yet'
|
||||||
) : (
|
) : (
|
||||||
<span className={styles.stat}>
|
<span className={styles.stat}>
|
||||||
<span className={styles.number}>{orders}</span> sale
|
<span className={styles.number}>{asset.stats.orders}</span> sale
|
||||||
{orders === 1 ? '' : 's'}
|
{asset.stats.orders === 1 ? '' : 's'}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{ownAllocation && ownAllocation > 0 ? (
|
||||||
|
<span className={styles.stat}>
|
||||||
|
<span className={styles.number}>{ownAllocation}</span>% allocated
|
||||||
|
<Tooltip
|
||||||
|
content={`You have ${ownAllocation}% of your total veOCEAN allocated to this asset.`}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,7 @@ export default function MarketStatsTotal({
|
|||||||
<>
|
<>
|
||||||
<PriceUnit price={total.orders} size="small" /> orders across{' '}
|
<PriceUnit price={total.orders} size="small" /> orders across{' '}
|
||||||
<PriceUnit price={total.nfts} size="small" /> assets with{' '}
|
<PriceUnit price={total.nfts} size="small" /> assets with{' '}
|
||||||
<PriceUnit price={total.datatokens} size="small" /> different datatokens.{' '}
|
<PriceUnit price={total.datatokens} size="small" /> different datatokens.
|
||||||
<PriceUnit price={total.veAllocated} symbol="veOCEAN" size="small" />{' '}
|
|
||||||
allocated.{' '}
|
|
||||||
<PriceUnit price={total.veLocked} symbol="OCEAN" size="small" /> locked.
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import Tooltip from '@shared/atoms/Tooltip'
|
|||||||
import Markdown from '@shared/Markdown'
|
import Markdown from '@shared/Markdown'
|
||||||
import content from '../../../../content/footer.json'
|
import content from '../../../../content/footer.json'
|
||||||
import { getTotalAllocatedAndLocked } from '@utils/veAllocation'
|
import { getTotalAllocatedAndLocked } from '@utils/veAllocation'
|
||||||
|
import PriceUnit from '@shared/Price/PriceUnit'
|
||||||
|
|
||||||
const initialTotal: StatsTotal = {
|
const initialTotal: StatsTotal = {
|
||||||
nfts: 0,
|
nfts: 0,
|
||||||
@ -113,7 +114,7 @@ export default function MarketStats(): ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.stats}>
|
<div className={styles.stats}>
|
||||||
<>
|
<div>
|
||||||
<MarketStatsTotal total={total} />{' '}
|
<MarketStatsTotal total={total} />{' '}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
className={styles.info}
|
className={styles.info}
|
||||||
@ -121,7 +122,12 @@ export default function MarketStats(): ReactElement {
|
|||||||
<Markdown className={styles.note} text={content.stats.note} />
|
<Markdown className={styles.note} text={content.stats.note} />
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
</div>
|
||||||
|
<div>
|
||||||
|
<PriceUnit price={total.veLocked} symbol="OCEAN" size="small" /> locked.{' '}
|
||||||
|
<PriceUnit price={total.veAllocated} symbol="veOCEAN" size="small" />{' '}
|
||||||
|
allocated.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -58,9 +58,6 @@
|
|||||||
width: auto;
|
width: auto;
|
||||||
left: auto;
|
left: auto;
|
||||||
background: none;
|
background: none;
|
||||||
}
|
|
||||||
|
|
||||||
.input:focus + .button {
|
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
47
src/components/Home/Allocations/AssetListTable.tsx
Normal file
47
src/components/Home/Allocations/AssetListTable.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Table, { TableOceanColumn } from '@shared/atoms/Table'
|
||||||
|
import AssetTitle from '@shared/AssetList/AssetListTitle'
|
||||||
|
import { AssetWithOwnAllocation } from '@utils/veAllocation'
|
||||||
|
|
||||||
|
const columns: TableOceanColumn<AssetWithOwnAllocation>[] = [
|
||||||
|
{
|
||||||
|
name: 'Dataset',
|
||||||
|
selector: (row) => {
|
||||||
|
const { metadata } = row.asset
|
||||||
|
return <AssetTitle title={metadata.name} asset={row.asset} />
|
||||||
|
},
|
||||||
|
maxWidth: '45rem',
|
||||||
|
grow: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Datatoken Symbol',
|
||||||
|
selector: (row) => row.asset.datatokens[0].symbol,
|
||||||
|
maxWidth: '10rem'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Allocated',
|
||||||
|
selector: (row) => row.allocation,
|
||||||
|
right: true,
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function AssetListTable({
|
||||||
|
data,
|
||||||
|
isLoading
|
||||||
|
}: {
|
||||||
|
data: AssetWithOwnAllocation[]
|
||||||
|
isLoading: boolean
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
data={data}
|
||||||
|
defaultSortFieldId={3}
|
||||||
|
sortAsc={false}
|
||||||
|
isLoading={isLoading}
|
||||||
|
emptyMessage={`Your allocated assets will appear here. [Lock your OCEAN](https://df.oceandao.org) to get started.`}
|
||||||
|
noTableHead
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
3
src/components/Home/Allocations/index.module.css
Normal file
3
src/components/Home/Allocations/index.module.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.section {
|
||||||
|
composes: section from '../index.module.css';
|
||||||
|
}
|
93
src/components/Home/Allocations/index.tsx
Normal file
93
src/components/Home/Allocations/index.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import { AssetWithOwnAllocation, getOwnAllocations } from '@utils/veAllocation'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import {
|
||||||
|
getFilterTerm,
|
||||||
|
generateBaseQuery,
|
||||||
|
queryMetadata
|
||||||
|
} from '@utils/aquarius'
|
||||||
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
|
import { useCancelToken } from '@hooks/useCancelToken'
|
||||||
|
import { useIsMounted } from '@hooks/useIsMounted'
|
||||||
|
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||||
|
import AssetListTable from './AssetListTable'
|
||||||
|
|
||||||
|
export default function Allocations(): ReactElement {
|
||||||
|
const { accountId } = useWeb3()
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
const isMounted = useIsMounted()
|
||||||
|
const newCancelToken = useCancelToken()
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState<boolean>()
|
||||||
|
const [data, setData] = useState<AssetWithOwnAllocation[]>()
|
||||||
|
const [hasAllocations, setHasAllocations] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountId) return
|
||||||
|
|
||||||
|
async function checkAllocations() {
|
||||||
|
try {
|
||||||
|
const allocations = await getOwnAllocations(chainIds, accountId)
|
||||||
|
setHasAllocations(allocations && allocations.length > 0)
|
||||||
|
} catch (error) {
|
||||||
|
LoggerInstance.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkAllocations()
|
||||||
|
}, [accountId, chainIds])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getAllocationAssets() {
|
||||||
|
if (!hasAllocations) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
const allocations = await getOwnAllocations(chainIds, accountId)
|
||||||
|
setHasAllocations(allocations && allocations.length > 0)
|
||||||
|
|
||||||
|
const baseParams = {
|
||||||
|
chainIds,
|
||||||
|
filters: [
|
||||||
|
getFilterTerm(
|
||||||
|
'nftAddress',
|
||||||
|
allocations.map((x) => x.nftAddress)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
ignorePurgatory: true
|
||||||
|
} as BaseQueryParams
|
||||||
|
|
||||||
|
const query = generateBaseQuery(baseParams)
|
||||||
|
|
||||||
|
const result = await queryMetadata(query, newCancelToken())
|
||||||
|
|
||||||
|
const assetsWithAllocation: AssetWithOwnAllocation[] = []
|
||||||
|
|
||||||
|
result?.results.forEach((asset) => {
|
||||||
|
const allocation = allocations.find(
|
||||||
|
(x) => x.nftAddress.toLowerCase() === asset.nftAddress.toLowerCase()
|
||||||
|
)
|
||||||
|
assetsWithAllocation.push({
|
||||||
|
asset,
|
||||||
|
allocation: `${allocation.allocation} %`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!isMounted()) return
|
||||||
|
setData(assetsWithAllocation)
|
||||||
|
setLoading(false)
|
||||||
|
} catch (error) {
|
||||||
|
LoggerInstance.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getAllocationAssets()
|
||||||
|
}, [hasAllocations, accountId, chainIds, isMounted, newCancelToken])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.section}>
|
||||||
|
<h3>Your Allocated Assets</h3>
|
||||||
|
<AssetListTable data={data} isLoading={loading} />
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
@ -8,12 +8,14 @@ import TopSales from './TopSales'
|
|||||||
import TopTags from './TopTags'
|
import TopTags from './TopTags'
|
||||||
import SectionQueryResult from './SectionQueryResult'
|
import SectionQueryResult from './SectionQueryResult'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
|
import Allocations from './Allocations'
|
||||||
|
|
||||||
export default function HomePage(): ReactElement {
|
export default function HomePage(): ReactElement {
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
|
||||||
const [queryLatest, setQueryLatest] = useState<SearchQuery>()
|
const [queryLatest, setQueryLatest] = useState<SearchQuery>()
|
||||||
const [queryMostSales, setQueryMostSales] = useState<SearchQuery>()
|
const [queryMostSales, setQueryMostSales] = useState<SearchQuery>()
|
||||||
const [queryMostAllocation, setQueryMostAllocation] = useState<SearchQuery>()
|
const [queryMostAllocation, setQueryMostAllocation] = useState<SearchQuery>()
|
||||||
const { chainIds } = useUserPreferences()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const baseParams = {
|
const baseParams = {
|
||||||
@ -52,10 +54,12 @@ export default function HomePage(): ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className={styles.section}>
|
<section className={styles.section}>
|
||||||
<h3>Bookmarks</h3>
|
<h3>Your Bookmarks</h3>
|
||||||
<Bookmarks />
|
<Bookmarks />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<Allocations />
|
||||||
|
|
||||||
<SectionQueryResult
|
<SectionQueryResult
|
||||||
title="Highest veOCEAN Allocations"
|
title="Highest veOCEAN Allocations"
|
||||||
query={queryMostAllocation}
|
query={queryMostAllocation}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
.number {
|
.number {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: baseline;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import Tooltip from '@shared/atoms/Tooltip'
|
|||||||
import styles from './NumberUnit.module.css'
|
import styles from './NumberUnit.module.css'
|
||||||
|
|
||||||
interface NumberUnitProps {
|
interface NumberUnitProps {
|
||||||
label: string
|
label: string | ReactElement
|
||||||
value: number | string | Element | ReactElement
|
value: number | string | Element | ReactElement
|
||||||
small?: boolean
|
small?: boolean
|
||||||
icon?: Element | ReactElement
|
icon?: Element | ReactElement
|
||||||
|
@ -4,3 +4,24 @@
|
|||||||
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stats [class^='PriceUnit_symbol'] {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats .link,
|
||||||
|
.stats .link:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats [class^='PriceUnit_price'] {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
@ -6,16 +6,31 @@ import NumberUnit from './NumberUnit'
|
|||||||
import styles from './Stats.module.css'
|
import styles from './Stats.module.css'
|
||||||
import { useProfile } from '@context/Profile'
|
import { useProfile } from '@context/Profile'
|
||||||
import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing'
|
import { getAccessDetailsForAssets } from '@utils/accessDetailsAndPricing'
|
||||||
|
import { getLocked } from '@utils/veAllocation'
|
||||||
|
import PriceUnit from '@shared/Price/PriceUnit'
|
||||||
|
import Button from '@shared/atoms/Button'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
|
||||||
export default function Stats({
|
export default function Stats({
|
||||||
accountId
|
accountId
|
||||||
}: {
|
}: {
|
||||||
accountId: string
|
accountId: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const web3 = useWeb3()
|
||||||
const { chainIds } = useUserPreferences()
|
const { chainIds } = useUserPreferences()
|
||||||
const { assets, assetsTotal, sales } = useProfile()
|
const { assets, assetsTotal, sales } = useProfile()
|
||||||
|
|
||||||
const [totalSales, setTotalSales] = useState(0)
|
const [totalSales, setTotalSales] = useState(0)
|
||||||
|
const [lockedOcean, setLockedOcean] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getLockedOcean() {
|
||||||
|
if (!accountId) return
|
||||||
|
const locked = await getLocked(accountId, chainIds)
|
||||||
|
setLockedOcean(locked)
|
||||||
|
}
|
||||||
|
getLockedOcean()
|
||||||
|
}, [accountId, chainIds])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!assets || !accountId || !chainIds) return
|
if (!assets || !accountId || !chainIds) return
|
||||||
@ -59,6 +74,30 @@ export default function Stats({
|
|||||||
value={sales < 0 ? 0 : sales}
|
value={sales < 0 ? 0 : sales}
|
||||||
/>
|
/>
|
||||||
<NumberUnit label="Published" value={assetsTotal} />
|
<NumberUnit label="Published" value={assetsTotal} />
|
||||||
|
<NumberUnit
|
||||||
|
label={
|
||||||
|
lockedOcean === 0 && accountId === web3.accountId ? (
|
||||||
|
<Button
|
||||||
|
className={styles.link}
|
||||||
|
style="text"
|
||||||
|
href="https://df.oceandao.org"
|
||||||
|
>
|
||||||
|
Lock OCEAN
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<PriceUnit price={lockedOcean} symbol="OCEAN" /> locked
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value={
|
||||||
|
<Conversion
|
||||||
|
price={lockedOcean > 0 ? lockedOcean : 0}
|
||||||
|
symbol="OCEAN"
|
||||||
|
hideApproximateSymbol
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,11 @@ div.filterList {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
display: flex;
|
|
||||||
gap: calc(var(--spacer) / 4) calc(var(--spacer) / 2);
|
gap: calc(var(--spacer) / 4) calc(var(--spacer) / 2);
|
||||||
flex-direction: column;
|
|
||||||
align-items: baseline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter {
|
.filter,
|
||||||
|
.filterList > div {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
.row {
|
.row {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
white-space: nowrap;
|
|
||||||
margin-bottom: calc(var(--spacer) / 2);
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,15 +8,9 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 55rem) {
|
|
||||||
.sortList {
|
|
||||||
align-self: flex-end;
|
|
||||||
overflow-y: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sortLabel {
|
.sortLabel {
|
||||||
composes: label from '@shared/FormInput/Label.module.css';
|
composes: label from '@shared/FormInput/Label.module.css';
|
||||||
|
padding: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-left: calc(var(--spacer) / 2);
|
margin-left: calc(var(--spacer) / 2);
|
||||||
margin-right: calc(var(--spacer) / 1.5);
|
margin-right: calc(var(--spacer) / 1.5);
|
||||||
@ -26,7 +20,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sorted {
|
.sorted {
|
||||||
display: flex;
|
display: inline-block;
|
||||||
padding: calc(var(--spacer) / 6) calc(var(--spacer) / 2);
|
padding: calc(var(--spacer) / 6) calc(var(--spacer) / 2);
|
||||||
margin-right: calc(var(--spacer) / 4);
|
margin-right: calc(var(--spacer) / 4);
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
@ -35,6 +29,7 @@
|
|||||||
font-weight: var(--font-weight-base);
|
font-weight: var(--font-weight-base);
|
||||||
background: var(--background-content);
|
background: var(--background-content);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sorted:hover,
|
.sorted:hover,
|
||||||
@ -47,7 +42,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.direction {
|
.direction {
|
||||||
display: flex;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
@ -13,7 +13,10 @@ const cx = classNames.bind(styles)
|
|||||||
|
|
||||||
const sortItems = [
|
const sortItems = [
|
||||||
{ display: 'Relevance', value: SortTermOptions.Relevance },
|
{ display: 'Relevance', value: SortTermOptions.Relevance },
|
||||||
{ display: 'Published', value: SortTermOptions.Created }
|
{ display: 'Published', value: SortTermOptions.Created },
|
||||||
|
{ display: 'Sales', value: SortTermOptions.Orders },
|
||||||
|
{ display: 'Total allocation', value: SortTermOptions.Allocated },
|
||||||
|
{ display: 'Price', value: SortTermOptions.Price }
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function Sort({
|
export default function Sort({
|
||||||
|
Loading…
Reference in New Issue
Block a user