Dashboard: Delete Marketplace v2 listing page (#3975)
- [x] Marketplace v2 is very outdated
- [x] Marketplace v3 is out for over 2 years
- [x] After the _incident_, people should be on v3 now
- [x] This also helps remove more sdk v4 code
<!-- start pr-codex -->
---
## PR-Codex overview
The focus of this PR is to refactor and clean up the contract UI listings components.
### Detailed summary
- Removed unused components from listings page
- Updated route configuration for listings
- Refactored ListingStats component
- Updated imports and dependencies
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
<!-- end pr-codex -->
This commit is contained in:
@@ -38,11 +38,6 @@ const LazyContractEnglishAuctionsPage = dynamic(() =>
|
||||
({ ContractEnglishAuctionsPage }) => ContractEnglishAuctionsPage,
|
||||
),
|
||||
);
|
||||
const LazyContractListingsPage = dynamic(() =>
|
||||
import("../tabs/listings/page").then(
|
||||
({ ContractListingsPage }) => ContractListingsPage,
|
||||
),
|
||||
);
|
||||
const LazyContractSplitPage = dynamic(() =>
|
||||
import("../tabs/split/page").then(
|
||||
({ ContractSplitPage }) => ContractSplitPage,
|
||||
@@ -196,16 +191,6 @@ export function useContractRouteConfig(
|
||||
}),
|
||||
component: LazyContractEnglishAuctionsPage,
|
||||
},
|
||||
{
|
||||
title: "Listings",
|
||||
path: "listings",
|
||||
isEnabled: contractTypeQuery.isLoading
|
||||
? "loading"
|
||||
: contractTypeQuery.data === "marketplace"
|
||||
? "enabled"
|
||||
: "disabled",
|
||||
component: LazyContractListingsPage,
|
||||
},
|
||||
{
|
||||
title: "Balances",
|
||||
path: "split",
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import { useEVMContractInfo } from "@3rdweb-sdk/react";
|
||||
import { Stack } from "@chakra-ui/react";
|
||||
import { useCancelListing } from "@thirdweb-dev/react";
|
||||
import type { ListingType, Marketplace } from "@thirdweb-dev/sdk";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import { useTrack } from "hooks/analytics/useTrack";
|
||||
import { useTxNotifications } from "hooks/useTxNotifications";
|
||||
|
||||
interface CancelTabProps {
|
||||
contract: Marketplace;
|
||||
listingId: string;
|
||||
listingType: ListingType;
|
||||
}
|
||||
|
||||
export const CancelTab: React.FC<CancelTabProps> = ({
|
||||
contract,
|
||||
listingId,
|
||||
listingType,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
const network = useEVMContractInfo()?.chain;
|
||||
|
||||
const cancelListing = useCancelListing(contract);
|
||||
|
||||
const { onSuccess, onError } = useTxNotifications(
|
||||
"Listing cancelled",
|
||||
"Error cancelling listing",
|
||||
);
|
||||
return (
|
||||
<Stack pt={3} gap={3}>
|
||||
{/* maybe some text? */}
|
||||
<TransactionButton
|
||||
transactionCount={1}
|
||||
isLoading={cancelListing.isLoading}
|
||||
onClick={() => {
|
||||
trackEvent({
|
||||
category: "marketplace",
|
||||
action: "cancel-listing",
|
||||
label: "attempt",
|
||||
});
|
||||
cancelListing.mutate(
|
||||
{
|
||||
id: listingId,
|
||||
type: listingType,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
trackEvent({
|
||||
category: "marketplace",
|
||||
action: "cancel-listing",
|
||||
label: "success",
|
||||
network,
|
||||
});
|
||||
onSuccess();
|
||||
},
|
||||
onError: (error) => {
|
||||
trackEvent({
|
||||
category: "marketplace",
|
||||
action: "cancel-listing",
|
||||
label: "error",
|
||||
network,
|
||||
error,
|
||||
});
|
||||
onError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
}}
|
||||
colorScheme="primary"
|
||||
alignSelf="flex-end"
|
||||
>
|
||||
Cancel Listing
|
||||
</TransactionButton>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -354,20 +354,6 @@ export const CreateListingsForm: React.FC<NFTMintForm> = ({
|
||||
</Stack>
|
||||
) : null}
|
||||
</FormControl>
|
||||
{/* Just don't support Marketplace v2 anymore */}
|
||||
{/* <FormControl isDisabled={noNfts}>
|
||||
<Heading as={FormLabel} size="label.lg">
|
||||
Listing Type
|
||||
</Heading>
|
||||
<Select {...form.register("listingType")}>
|
||||
<option value="direct">Direct</option>
|
||||
<option value="auction">Auction</option>
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
The type of listing you want to create, either an auction or direct
|
||||
listing.
|
||||
</FormHelperText>
|
||||
</FormControl> */}
|
||||
<FormControl isRequired isDisabled={noNfts}>
|
||||
<Heading as={FormLabel} size="label.lg">
|
||||
Listing Currency
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
import {
|
||||
Flex,
|
||||
GridItem,
|
||||
SimpleGrid,
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Tabs,
|
||||
usePrevious,
|
||||
} from "@chakra-ui/react";
|
||||
import type {
|
||||
AuctionListing,
|
||||
DirectListing,
|
||||
Marketplace,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { BigNumber } from "ethers";
|
||||
import { useMemo } from "react";
|
||||
import { useActiveAccount } from "thirdweb/react";
|
||||
import { Badge, Card, CodeBlock, Drawer, Heading, Text } from "tw-components";
|
||||
import { AddressCopyButton } from "tw-components/AddressCopyButton";
|
||||
import { NFTMediaWithEmptyState } from "tw-components/nft-media";
|
||||
import { CancelTab } from "./cancel-tab";
|
||||
|
||||
interface NFTDrawerProps {
|
||||
contract: Marketplace;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
data: AuctionListing | DirectListing | null;
|
||||
}
|
||||
export const ListingDrawer: React.FC<NFTDrawerProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
data,
|
||||
contract,
|
||||
}) => {
|
||||
const address = useActiveAccount()?.address;
|
||||
const prevData = usePrevious(data);
|
||||
|
||||
const renderData = data || prevData;
|
||||
const isOwner = address === renderData?.sellerAddress;
|
||||
|
||||
const tokenId = renderData?.asset.id.toString() || "";
|
||||
|
||||
const tabs = useMemo(() => {
|
||||
if (!renderData) {
|
||||
return [];
|
||||
}
|
||||
const t = [
|
||||
{
|
||||
title: "Details",
|
||||
isDisabled: false,
|
||||
children: () => (
|
||||
<Flex flexDir="column" gap={4}>
|
||||
<Card as={Flex} flexDir="column" gap={3}>
|
||||
<SimpleGrid rowGap={3} columns={12} placeItems="center left">
|
||||
<GridItem colSpan={3}>
|
||||
<Heading size="label.md">Token ID</Heading>
|
||||
</GridItem>
|
||||
<GridItem colSpan={9}>
|
||||
<AddressCopyButton
|
||||
size="xs"
|
||||
address={tokenId}
|
||||
title="Token ID"
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem colSpan={3}>
|
||||
<Heading size="label.md">Seller</Heading>
|
||||
</GridItem>
|
||||
<GridItem colSpan={9}>
|
||||
<AddressCopyButton
|
||||
size="xs"
|
||||
address={renderData.sellerAddress}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem colSpan={3}>
|
||||
<Heading size="label.md">Listing Type</Heading>
|
||||
</GridItem>
|
||||
<GridItem colSpan={9}>
|
||||
<Badge
|
||||
size="label.sm"
|
||||
variant="subtle"
|
||||
textTransform="capitalize"
|
||||
>
|
||||
{renderData?.type === 0 ? "Direct Listing" : "Auction"}
|
||||
</Badge>
|
||||
</GridItem>
|
||||
<GridItem colSpan={3}>
|
||||
<Heading size="label.md">Quantity</Heading>
|
||||
</GridItem>
|
||||
<GridItem colSpan={9}>
|
||||
<Text fontFamily="mono" size="body.md">
|
||||
{BigNumber.from(renderData.quantity || "0").toString()}
|
||||
</Text>
|
||||
</GridItem>
|
||||
</SimpleGrid>
|
||||
</Card>
|
||||
{data?.asset.attributes || data?.asset.properties ? (
|
||||
<Card as={Flex} flexDir="column" gap={4}>
|
||||
<Heading size="label.md">Attributes</Heading>
|
||||
<CodeBlock
|
||||
code={
|
||||
JSON.stringify(
|
||||
data?.asset.attributes || data?.asset.properties,
|
||||
null,
|
||||
2,
|
||||
) || ""
|
||||
}
|
||||
language="json"
|
||||
canCopy={false}
|
||||
wrap={false}
|
||||
overflow="auto"
|
||||
/>
|
||||
</Card>
|
||||
) : null}
|
||||
</Flex>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Cancel Listing",
|
||||
isDisabled: !isOwner,
|
||||
children: () => (
|
||||
<CancelTab
|
||||
contract={contract}
|
||||
listingId={renderData.id}
|
||||
listingType={renderData.type}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return t;
|
||||
}, [contract, isOwner, renderData, tokenId, data?.asset]);
|
||||
|
||||
if (!renderData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
allowPinchZoom
|
||||
preserveScrollBarGap
|
||||
size="xl"
|
||||
onClose={onClose}
|
||||
isOpen={isOpen}
|
||||
>
|
||||
<Flex py={6} px={2} flexDir="column" gap={6}>
|
||||
<Flex gap={6}>
|
||||
<NFTMediaWithEmptyState
|
||||
metadata={renderData.asset}
|
||||
requireInteraction
|
||||
width="150px"
|
||||
height="150px"
|
||||
/>
|
||||
<Flex flexDir="column" gap={2} w="70%">
|
||||
<Heading size="title.lg">{renderData.asset.name}</Heading>
|
||||
<Text size="label.md" noOfLines={6}>
|
||||
{renderData.asset.description}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Tabs isLazy lazyBehavior="keepMounted" colorScheme="gray">
|
||||
<TabList
|
||||
px={0}
|
||||
borderBottomColor="borderColor"
|
||||
borderBottomWidth="1px"
|
||||
overflowX={{ base: "auto", md: "inherit" }}
|
||||
>
|
||||
{tabs.map((tab) => (
|
||||
<Tab key={tab.title} gap={2} isDisabled={tab.isDisabled}>
|
||||
{tab.title}
|
||||
</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
<TabPanels px={0} py={2}>
|
||||
{tabs.map((tab) => {
|
||||
return (
|
||||
<TabPanel key={tab.title} px={0}>
|
||||
{tab.children()}
|
||||
</TabPanel>
|
||||
);
|
||||
})}
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Flex>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
@@ -1,42 +1,13 @@
|
||||
import {
|
||||
SimpleGrid,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Stat,
|
||||
StatLabel,
|
||||
StatNumber,
|
||||
} from "@chakra-ui/react";
|
||||
import { Skeleton, Stack, Stat, StatLabel, StatNumber } from "@chakra-ui/react";
|
||||
import {
|
||||
useDirectListingsCount,
|
||||
useEnglishAuctionsCount,
|
||||
useListingsCount,
|
||||
} from "@thirdweb-dev/react";
|
||||
import type { Marketplace, MarketplaceV3 } from "@thirdweb-dev/sdk";
|
||||
import type { MarketplaceV3 } from "@thirdweb-dev/sdk";
|
||||
import { BigNumber } from "ethers";
|
||||
import { useMemo } from "react";
|
||||
import { Card } from "tw-components";
|
||||
|
||||
interface ListingStatsProps {
|
||||
contract?: Marketplace;
|
||||
}
|
||||
|
||||
export const ListingStats: React.FC<ListingStatsProps> = ({ contract }) => {
|
||||
const listingsQuery = useListingsCount(contract);
|
||||
|
||||
return (
|
||||
<SimpleGrid columns={3} spacing={{ base: 3, md: 6 }}>
|
||||
<Card as={Stat}>
|
||||
<StatLabel mb={{ base: 1, md: 0 }}>Total Listings</StatLabel>
|
||||
<Skeleton isLoaded={!contract || listingsQuery.isSuccess}>
|
||||
<StatNumber>
|
||||
{BigNumber.from(listingsQuery?.data || 0).toString()}
|
||||
</StatNumber>
|
||||
</Skeleton>
|
||||
</Card>
|
||||
</SimpleGrid>
|
||||
);
|
||||
};
|
||||
|
||||
interface ListingStatsV3Props {
|
||||
contract?: MarketplaceV3;
|
||||
features: string[];
|
||||
|
||||
@@ -1,297 +0,0 @@
|
||||
import {
|
||||
ButtonGroup,
|
||||
Center,
|
||||
Flex,
|
||||
Icon,
|
||||
IconButton,
|
||||
Select,
|
||||
Skeleton,
|
||||
Spinner,
|
||||
Table,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
usePrevious,
|
||||
} from "@chakra-ui/react";
|
||||
import {
|
||||
useActiveListings,
|
||||
useListings,
|
||||
useListingsCount,
|
||||
} from "@thirdweb-dev/react";
|
||||
import type {
|
||||
AuctionListing,
|
||||
DirectListing,
|
||||
Marketplace,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { MediaCell } from "components/contract-pages/table/table-columns/cells/media-cell";
|
||||
import { BigNumber } from "ethers";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
MdFirstPage,
|
||||
MdLastPage,
|
||||
MdNavigateBefore,
|
||||
MdNavigateNext,
|
||||
} from "react-icons/md";
|
||||
import { type Cell, type Column, usePagination, useTable } from "react-table";
|
||||
import { Button, Text } from "tw-components";
|
||||
import { AddressCopyButton } from "tw-components/AddressCopyButton";
|
||||
import { ListingDrawer } from "./listing-drawer";
|
||||
|
||||
type ListingMetadata = AuctionListing | DirectListing;
|
||||
|
||||
const tableColumns: Column<ListingMetadata>[] = [
|
||||
{
|
||||
Header: "Listing Id",
|
||||
accessor: (row) => row.id.toString(),
|
||||
},
|
||||
{
|
||||
Header: "Media",
|
||||
accessor: (row) => row.asset,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
Cell: (cell: any) => <MediaCell cell={cell} />,
|
||||
},
|
||||
{
|
||||
Header: "Name",
|
||||
accessor: (row) => row.asset?.name,
|
||||
},
|
||||
{
|
||||
Header: "Seller",
|
||||
accessor: (row) => row.sellerAddress,
|
||||
Cell: ({ cell }: { cell: Cell<ListingMetadata, string> }) => (
|
||||
<AddressCopyButton variant="outline" address={cell.value} />
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: "Price",
|
||||
accessor: (row) => row.buyoutCurrencyValuePerToken,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
Cell: ({ cell }: { cell: Cell<ListingMetadata, any> }) => {
|
||||
return (
|
||||
<Text size="label.md" whiteSpace="nowrap">
|
||||
{cell.value.displayValue} {cell.value.symbol}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Type",
|
||||
// 0 = Direct, 1 = Auction
|
||||
accessor: (row) => (row.type === 0 ? "Direct Listing" : "Auction"),
|
||||
},
|
||||
];
|
||||
|
||||
interface ListingsTableProps {
|
||||
contract: Marketplace;
|
||||
}
|
||||
|
||||
const DEFAULT_QUERY_STATE = { count: 50, start: 0 };
|
||||
|
||||
export const ListingsTable: React.FC<ListingsTableProps> = ({ contract }) => {
|
||||
const [queryParams, setQueryParams] = useState(DEFAULT_QUERY_STATE);
|
||||
const getAllQueryResult = useListings(contract, queryParams);
|
||||
const getActiveQueryResult = useActiveListings(contract, queryParams);
|
||||
const totalCountQuery = useListingsCount(contract);
|
||||
|
||||
const [listingsToShow, setListingsToShow_] = useState<"all" | "active">(
|
||||
"all",
|
||||
);
|
||||
|
||||
const setListingsToShow = (value: "all" | "active") => {
|
||||
setQueryParams(DEFAULT_QUERY_STATE);
|
||||
setListingsToShow_(value);
|
||||
};
|
||||
|
||||
const prevData = usePrevious(
|
||||
listingsToShow === "all"
|
||||
? getAllQueryResult?.data
|
||||
: getActiveQueryResult?.data,
|
||||
);
|
||||
|
||||
const renderData = useMemo(() => {
|
||||
if (listingsToShow === "all") {
|
||||
return getAllQueryResult?.data || prevData;
|
||||
}
|
||||
return getActiveQueryResult?.data || prevData;
|
||||
}, [getAllQueryResult, getActiveQueryResult, listingsToShow, prevData]);
|
||||
|
||||
const {
|
||||
getTableProps,
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
prepareRow,
|
||||
page,
|
||||
canPreviousPage,
|
||||
canNextPage,
|
||||
pageCount,
|
||||
gotoPage,
|
||||
nextPage,
|
||||
previousPage,
|
||||
setPageSize,
|
||||
state: { pageIndex, pageSize },
|
||||
} = useTable(
|
||||
{
|
||||
columns: tableColumns,
|
||||
data: renderData || [],
|
||||
initialState: {
|
||||
pageSize: queryParams.count,
|
||||
pageIndex: 0,
|
||||
},
|
||||
manualPagination: true,
|
||||
pageCount: Math.max(
|
||||
Math.ceil(
|
||||
BigNumber.from(totalCountQuery.data || 0).toNumber() /
|
||||
queryParams.count,
|
||||
),
|
||||
1,
|
||||
),
|
||||
},
|
||||
// FIXME: re-work tables and pagination with @tanstack/table@latest - which (I believe) does not need this workaround anymore
|
||||
// eslint-disable-next-line react-compiler/react-compiler
|
||||
usePagination,
|
||||
);
|
||||
|
||||
// FIXME: re-work tables and pagination with @tanstack/table@latest - which (I believe) does not need this workaround anymore
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
useEffect(() => {
|
||||
setQueryParams({ start: pageIndex * pageSize, count: pageSize });
|
||||
}, [pageIndex, pageSize]);
|
||||
|
||||
const [tokenRow, setTokenRow] = useState<ListingMetadata | null>(null);
|
||||
|
||||
return (
|
||||
<Flex gap={4} direction="column">
|
||||
<ButtonGroup size="sm" variant="outline" isAttached>
|
||||
<Button
|
||||
onClick={() => setListingsToShow("all")}
|
||||
variant={listingsToShow === "all" ? "solid" : "outline"}
|
||||
>
|
||||
All
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => setListingsToShow("active")}
|
||||
variant={listingsToShow === "active" ? "solid" : "outline"}
|
||||
>
|
||||
Active
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
<TableContainer>
|
||||
{((listingsToShow === "all" && getAllQueryResult.isFetching) ||
|
||||
(listingsToShow === "active" && getActiveQueryResult.isFetching)) && (
|
||||
<Spinner
|
||||
color="primary"
|
||||
size="xs"
|
||||
position="absolute"
|
||||
top={2}
|
||||
right={4}
|
||||
/>
|
||||
)}
|
||||
<ListingDrawer
|
||||
contract={contract}
|
||||
data={tokenRow}
|
||||
isOpen={!!tokenRow}
|
||||
onClose={() => setTokenRow(null)}
|
||||
/>
|
||||
<Table {...getTableProps()}>
|
||||
<Thead>
|
||||
{headerGroups.map((headerGroup, headerGroupIndex) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: FIXME
|
||||
<Tr {...headerGroup.getHeaderGroupProps()} key={headerGroupIndex}>
|
||||
{headerGroup.headers.map((column, columnIndex) => (
|
||||
<Th
|
||||
{...column.getHeaderProps()}
|
||||
border="none"
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: FIXME
|
||||
key={columnIndex}
|
||||
>
|
||||
<Text as="label" size="label.sm" color="faded">
|
||||
{column.render("Header")}
|
||||
</Text>
|
||||
</Th>
|
||||
))}
|
||||
<Th border="none" />
|
||||
</Tr>
|
||||
))}
|
||||
</Thead>
|
||||
<Tbody {...getTableBodyProps()}>
|
||||
{page.map((row, rowIndex) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: FIXME
|
||||
<Tr {...row.getRowProps()} key={rowIndex}>
|
||||
{row.cells.map((cell, cellIndex) => {
|
||||
return (
|
||||
<Td
|
||||
{...cell.getCellProps()}
|
||||
borderColor="borderColor"
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: FIXME
|
||||
key={cellIndex}
|
||||
>
|
||||
{cell.render("Cell")}
|
||||
</Td>
|
||||
);
|
||||
})}
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Center w="100%">
|
||||
<Flex gap={2} direction="row" align="center">
|
||||
<IconButton
|
||||
isDisabled={!canPreviousPage || totalCountQuery.isLoading}
|
||||
aria-label="first page"
|
||||
icon={<Icon as={MdFirstPage} />}
|
||||
onClick={() => gotoPage(0)}
|
||||
/>
|
||||
<IconButton
|
||||
isDisabled={!canPreviousPage || totalCountQuery.isLoading}
|
||||
aria-label="previous page"
|
||||
icon={<Icon as={MdNavigateBefore} />}
|
||||
onClick={() => previousPage()}
|
||||
/>
|
||||
<Text whiteSpace="nowrap">
|
||||
Page <strong>{pageIndex + 1}</strong> of{" "}
|
||||
<Skeleton
|
||||
as="span"
|
||||
display="inline"
|
||||
isLoaded={totalCountQuery.isSuccess}
|
||||
>
|
||||
<strong>{pageCount}</strong>
|
||||
</Skeleton>
|
||||
</Text>
|
||||
<IconButton
|
||||
isDisabled={!canNextPage || totalCountQuery.isLoading}
|
||||
aria-label="next page"
|
||||
icon={<Icon as={MdNavigateNext} />}
|
||||
onClick={() => nextPage()}
|
||||
/>
|
||||
<IconButton
|
||||
isDisabled={!canNextPage || totalCountQuery.isLoading}
|
||||
aria-label="last page"
|
||||
icon={<Icon as={MdLastPage} />}
|
||||
onClick={() => gotoPage(pageCount - 1)}
|
||||
/>
|
||||
|
||||
<Select
|
||||
onChange={(e) => {
|
||||
setPageSize(Number.parseInt(e.target.value as string, 10));
|
||||
}}
|
||||
value={pageSize}
|
||||
isDisabled={totalCountQuery.isLoading}
|
||||
>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
<option value="250">250</option>
|
||||
<option value="500">500</option>
|
||||
</Select>
|
||||
</Flex>
|
||||
</Center>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { useContract } from "@thirdweb-dev/react";
|
||||
import { Heading } from "tw-components";
|
||||
import { CreateListingButton } from "../shared-components/list-button";
|
||||
import { ListingStats } from "./components/listing-stats";
|
||||
import { ListingsTable } from "./components/table";
|
||||
|
||||
interface ListingsPageProps {
|
||||
contractAddress?: string;
|
||||
}
|
||||
|
||||
export const ContractListingsPage: React.FC<ListingsPageProps> = ({
|
||||
contractAddress,
|
||||
}) => {
|
||||
const contractQuery = useContract(contractAddress, "marketplace");
|
||||
|
||||
if (contractQuery.isLoading) {
|
||||
// TODO build a skeleton for this
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (!contractQuery?.contract) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap={6}>
|
||||
<Flex direction="row" justify="space-between" align="center">
|
||||
<Heading size="title.sm">Contract Listings</Heading>
|
||||
<Flex gap={4}>
|
||||
<CreateListingButton
|
||||
contractAddress={contractQuery.contract.getAddress()}
|
||||
chainId={contractQuery.contract.chainId}
|
||||
createText="Create Listing"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<ListingStats contract={contractQuery.contract} />
|
||||
<ListingsTable contract={contractQuery.contract} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user