diff --git a/apps/dashboard/src/@/components/blocks/charts/area-chart.tsx b/apps/dashboard/src/@/components/blocks/charts/area-chart.tsx index 92d3c967c3c..8de8fff2ba9 100644 --- a/apps/dashboard/src/@/components/blocks/charts/area-chart.tsx +++ b/apps/dashboard/src/@/components/blocks/charts/area-chart.tsx @@ -30,6 +30,7 @@ type ThirdwebAreaChartProps = { description?: string; titleClassName?: string; }; + customHeader?: React.ReactNode; // chart config config: TConfig; data: Array & { time: number | string | Date }>; @@ -60,6 +61,8 @@ export function ThirdwebAreaChart( )} + {props.customHeader && props.customHeader} + {props.isPending ? ( diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/NFTCards.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/NFTCards.tsx index 7cce6de053b..cee6608db96 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/NFTCards.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/NFTCards.tsx @@ -1,14 +1,7 @@ -import { - Flex, - GridItem, - SimpleGrid, - Skeleton, - SkeletonText, - useBreakpointValue, -} from "@chakra-ui/react"; +import { SkeletonContainer } from "@/components/ui/skeleton"; +import { TrackedLinkTW } from "@/components/ui/tracked-link"; import { useMemo } from "react"; import { type NFT, ZERO_ADDRESS } from "thirdweb"; -import { Card, TrackedLink, type TrackedLinkProps } from "tw-components"; import { NFTMediaWithEmptyState } from "tw-components/nft-media"; type NFTWithContract = NFT & { contractAddress: string; chainId: number }; @@ -20,7 +13,8 @@ const dummyMetadata: (idx: number) => NFTWithContract = (idx) => ({ tokenURI: `1-0x123-${idx}`, metadata: { name: "Loading...", - description: "lorem ipsum loading sit amet", + description: + "lorem ipsum loading sit amet consectetur adipisicing elit. Quisquam, quos.", id: BigInt(idx || 0), uri: `1-0x123-${idx}`, }, @@ -31,7 +25,7 @@ const dummyMetadata: (idx: number) => NFTWithContract = (idx) => ({ interface NFTCardsProps { nfts: Array; - trackingCategory: TrackedLinkProps["category"]; + trackingCategory: string; isPending: boolean; allNfts?: boolean; } @@ -42,61 +36,82 @@ export const NFTCards: React.FC = ({ isPending, allNfts, }) => { - const isMobile = useBreakpointValue({ base: true, md: false }); - const dummyData = useMemo(() => { return Array.from({ - length: allNfts ? nfts.length : isMobile ? 2 : 3, + length: allNfts ? nfts.length : 3, }).map((_, idx) => dummyMetadata(idx)); - }, [nfts.length, isMobile, allNfts]); + }, [nfts.length, allNfts]); return ( - +
{(isPending ? dummyData : nfts).map((token) => { const tokenId = token.id.toString(); return ( - - -
- - - -
- - -

- {token.metadata.name} -

-
- -

- {token.metadata.description ? ( - token.metadata.description - ) : ( - No description - )} -

-
-
-
-
+ {/* border */} +
+ + {/* image */} +
+ { + return ( + + ); + }} + /> +
+ +
+ {/* title */} + { + return ( +

+ + {v.name} + +

+ ); + }} + /> + + {/* Description */} + { + return ( +

+ {v.description ? v.description : No description} +

+ ); + }} + /> +
+
); })} - +
); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/supply-cards.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/supply-cards.tsx index dc93b10f6e2..9be19e6a58a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/supply-cards.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/supply-cards.tsx @@ -7,7 +7,7 @@ import { totalSupply, } from "thirdweb/extensions/erc721"; import { useReadContract } from "thirdweb/react"; -import { StatCard } from "../../overview/components/stat-card"; +import { Stat } from "../../overview/components/stat-card"; interface SupplyCardsProps { contract: ThirdwebContract; @@ -36,17 +36,17 @@ export const SupplyCards: React.FC = ({ contract }) => { return (
- - - = ({ }) => { return (
-
+
= ({ /> {isAnalyticsSupported && ( - = ({ /> )} - {isErc20 && } + {isErc20 && } = ({ - chainId, - contractAddress, - trackingCategory, - chainSlug, -}) => { +}) { const trackEvent = useTrack(); const [startDate] = useState( (() => { @@ -35,55 +32,27 @@ export const AnalyticsOverview: React.FC = ({ ); const [endDate] = useState(new Date()); - return ( -
-
-

Analytics

- -
- - -
- ); -}; + const wallets = useContractUniqueWalletAnalytics({ + chainId: props.chainId, + contractAddress: props.contractAddress, + startDate, + endDate, + }); -type ChartProps = { - contractAddress: string; - chainId: number; - startDate: Date; - endDate: Date; -}; + const transactions = useContractTransactionAnalytics({ + chainId: props.chainId, + contractAddress: props.contractAddress, + startDate, + endDate, + }); -function getDayKey(date: Date) { - return date.toISOString().split("T")[0]; -} + const events = useContractEventAnalytics({ + chainId: props.chainId, + contractAddress: props.contractAddress, + startDate, + endDate, + }); -function OverviewAnalytics(props: ChartProps) { - const wallets = useContractUniqueWalletAnalytics(props); - const transactions = useContractTransactionAnalytics(props); - const events = useContractEventAnalytics(props); const isPending = wallets.isPending || transactions.isPending || events.isPending; @@ -135,7 +104,32 @@ function OverviewAnalytics(props: ChartProps) { data={mergedData || []} isPending={isPending} showLegend - chartClassName="aspect-[1.5] lg:aspect-[3.5]" + chartClassName="aspect-[1.5] lg:aspect-[3]" + customHeader={ +
+

Analytics

+ +
+ } /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx index 9b283c1e78f..794a1ba897c 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx @@ -1,79 +1,41 @@ -"use client"; +import { TrackedLinkTW } from "@/components/ui/tracked-link"; +import { ReactIcon } from "../../../../../../../components/icons/brand-icons/ReactIcon"; +import { TypeScriptIcon } from "../../../../../../../components/icons/brand-icons/TypeScriptIcon"; -import { - Flex, - GridItem, - LinkBox, - LinkOverlay, - SimpleGrid, -} from "@chakra-ui/react"; -import { ChakraNextImage as Image } from "components/Image"; -import { PRODUCTS } from "components/product-pages/common/nav/data"; -import { Card } from "tw-components/card"; -import { TrackedLink, type TrackedLinkProps } from "tw-components/link"; -import { Text } from "tw-components/text"; - -const RENDERED_PRODUCTS = ["sdk", "storage", "ui-components", "auth"]; - -interface BuildYourAppProps { - trackingCategory: TrackedLinkProps["category"]; +export function BuildYourApp(props: { + trackingCategory: string; contractAddress: string; chainSlug: string; -} - -export const BuildYourApp: React.FC = ({ - trackingCategory, - contractAddress, - chainSlug, -}) => { +}) { return ( - - - -

- Build your app -

- - - Learn more - {" "} - about how you can use thirdweb tools to build apps on top of this - contract. - -
- - {RENDERED_PRODUCTS.map((p) => { - const product = PRODUCTS.find((item) => item.label === p); - return ( - product?.icon && ( - - {product.name} - - ) - ); - })} - -
-
+
+ {/* left */} +
+

+ Build your app +

+ + Learn more about how you can use thirdweb tools to build apps on top + of this contract + {" "} +
+ + {/* right */} +
+ {[TypeScriptIcon, ReactIcon].map((Icon, i) => { + return ( + // biome-ignore lint/suspicious/noArrayIndexKey: +
+ +
+ ); + })} +
+
); -}; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.stories.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.stories.tsx new file mode 100644 index 00000000000..866b11b6c99 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.stories.tsx @@ -0,0 +1,105 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { LatestEventsUI } from "./LatestEvents"; + +const meta: Meta = { + title: "Contracts/Overview/LatestEvents", + component: LatestEventsUI, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: { + allEvents: [], + autoUpdate: false, + eventsHref: "/ethereum/0x123456789/events", + trackingCategory: "test-category", + }, +}; + +function eventStub(eventName: string) { + return { + logIndex: 0, + eventName, + address: "0x", + args: [], + }; +} + +const txStub = + "0x9ef44ea701637e0f7c42666b6f99f4aad9aa6b9b51995cd22df1507288e51427"; + +export default meta; +type Story = StoryObj; + +export const Loading: Story = { + args: { + allEvents: [], + autoUpdate: true, + eventsHref: "/ethereum/0x123456789/events", + }, +}; + +export const NoEvents: Story = { + args: { + allEvents: [], + autoUpdate: false, + eventsHref: "/ethereum/0x123456789/events", + }, +}; + +export const WithEvents: Story = { + args: { + allEvents: [ + { + transactionHash: txStub, + events: [eventStub("Transfer"), eventStub("Approval")], + }, + { + transactionHash: txStub, + events: [eventStub("Mint")], + }, + { + transactionHash: txStub, + events: [ + eventStub("Transfer"), + eventStub("Burn"), + eventStub("Approval"), + ], + }, + ], + autoUpdate: false, + eventsHref: "/ethereum/0x123456789/events", + }, +}; + +export const LiveWithEvents: Story = { + args: { + allEvents: [ + { + transactionHash: txStub, + events: [eventStub("Transfer"), eventStub("Approval")], + }, + { + transactionHash: txStub, + events: [eventStub("Mint")], + }, + { + transactionHash: txStub, + events: [ + eventStub("Transfer"), + eventStub("Burn"), + eventStub("Approval"), + ], + }, + { + transactionHash: txStub, + events: [eventStub("RoleGranted"), eventStub("OwnershipTransferred")], + }, + ], + autoUpdate: true, + eventsHref: "/ethereum/0x123456789/events", + }, +}; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx index baf9154a65a..d01e697beae 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx @@ -1,192 +1,145 @@ "use client"; +import { CopyTextButton } from "@/components/ui/CopyTextButton"; +import { Spinner } from "@/components/ui/Spinner/Spinner"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { TrackedLinkTW } from "@/components/ui/tracked-link"; import { type InternalTransaction, useActivity, } from "@3rdweb-sdk/react/hooks/useActivity"; -import { - Box, - ButtonGroup, - Flex, - List, - SimpleGrid, - Spinner, - Tooltip, -} from "@chakra-ui/react"; -import { useClipboard } from "hooks/useClipboard"; -import { CopyIcon } from "lucide-react"; -import { useState } from "react"; -import { toast } from "sonner"; +import { ArrowRightIcon } from "lucide-react"; +import Link from "next/link"; import type { ThirdwebContract } from "thirdweb"; -import { - Button, - Card, - Heading, - LinkButton, - Text, - TrackedLink, - type TrackedLinkProps, -} from "tw-components"; import { shortenString } from "utils/usedapp-external"; -interface LatestEventsProps { - trackingCategory: TrackedLinkProps["category"]; +export function LatestEvents(props: { + trackingCategory: string; contract: ThirdwebContract; chainSlug: string; -} - -export const LatestEvents: React.FC = ({ - contract, - trackingCategory, - chainSlug, -}) => { - const [autoUpdate] = useState(true); - const eventsHref = `/${chainSlug}/${contract.address}/events`; - const allEvents = useActivity(contract, autoUpdate); +}) { + const autoUpdate = true; + const allEvents = useActivity(props.contract, autoUpdate); return ( - - -

Latest Events

- - View all -> - -
- - - - Transaction Hash - - - Events - - - - - {allEvents.length === 0 ? ( -
- - {autoUpdate && } - - {autoUpdate ? "listening for events" : "no events to show"} - - -
- ) : null} -
- {allEvents?.slice(0, 3).map((e) => ( - - ))} -
-
-
-
+ ); -}; - -interface EventsFeedItemProps { - transaction: InternalTransaction; - contractAddress: string; - chainSlug: string; } -const EventsFeedItem: React.FC = ({ - transaction, - contractAddress, - chainSlug, -}) => { - const { onCopy } = useClipboard(transaction.transactionHash); - const eventsHref = `/${chainSlug}/${contractAddress}/events`; +export function LatestEventsUI(props: { + allEvents: Pick[]; + autoUpdate: boolean; + eventsHref: string; + trackingCategory: string; +}) { + const { allEvents, autoUpdate, eventsHref } = props; return ( -
- - -
- - - Copy transaction hash to clipboard - - - } +
+ {/* header */} +
+
+

+ Latest Events +

+ {autoUpdate && ( + - - - - {shortenString(transaction.transactionHash)} - -
- + + + + + Live + + )} +
+ +
+ + {/* table */} + + + + + Transaction Hash + Events + + - - {transaction.events.slice(0, 2).map((e, idx) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: FIXME - - - {e.eventName} - - - ))} - {transaction.events.length > 2 && ( - + + {allEvents.slice(0, 3).map((transaction) => ( + + + + + +
+ {transaction.events.map((e) => ( + + ))} +
+
+
+ ))} +
+
+
+ + {allEvents.length === 0 && ( +
+ {autoUpdate ? ( +
+ +

Listening for events

+
+ ) : ( +

No events found

)} - - +
+ )}
); -}; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx index 66eb6f7eb0a..2c60d3f064f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx @@ -183,8 +183,7 @@ export const MarketplaceDetails: React.FC = ({ chainSlug, }) => { return ( -
-

Listings

+
= ({ +export function NFTDetails({ contract, trackingCategory, isErc721, chainSlug, -}) => { - const isMobile = useBreakpointValue({ base: true, md: false }); +}: NFTDetailsProps) { const nftsHref = `/${chainSlug}/${contract.address}/nfts`; const nftQuery = useReadContract( @@ -36,34 +36,37 @@ export const NFTDetails: React.FC = ({ const displayableNFTs = nftQuery.data ?.filter((token) => token.metadata.image || token.metadata.animation_url) - .slice(0, isMobile ? 2 : 3) || []; + .slice(0, 3) || []; return displayableNFTs.length === 0 && !nftQuery.isPending ? null : ( - - -

NFT Details

- - View all -> - -
- ({ - ...t, - contractAddress: contract.address, - chainId: contract.chain.id, - }))} - trackingCategory={trackingCategory} - isPending={nftQuery.isPending} - /> -
+
+ {/* header */} +
+

NFT Details

+ +
+ + {/* cards */} +
+ ({ + ...t, + contractAddress: contract.address, + chainId: contract.chain.id, + }))} + trackingCategory={trackingCategory} + isPending={nftQuery.isPending} + /> +
+
); -}; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.stories.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.stories.tsx new file mode 100644 index 00000000000..4e624e1d906 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.stories.tsx @@ -0,0 +1,84 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { ThirdwebProvider } from "thirdweb/react"; +import { PermissionsTableUI } from "./PermissionsTable"; + +const meta: Meta = { + title: "Contracts/Overview/PermissionsTable", + component: PermissionsTableUI, + decorators: [ + (Story) => ( + +
+ +
+
+ ), + ], + args: { + viewMoreLink: "#", + trackingCategory: "test-category", + members: [], + isPending: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Loading: Story = { + args: { + isPending: true, + members: [], + }, +}; + +export const NoMembers: Story = { + args: { + isPending: false, + members: [], + }, +}; + +export const OneMember: Story = { + args: { + isPending: false, + members: [ + { + member: "0x1F846F6DAE38E1C88D71EAA191760B15f38B7A37", + roles: ["admin"], + }, + ], + }, +}; + +export const FiveMembers: Story = { + args: { + isPending: false, + members: [ + { + member: "0x1F846F6DAE38E1C88D71EAA191760B15f38B7A37", + roles: ["admin", "minter"], + }, + { + member: "0x83Dd93fA5D8343094f850f90B3fb90088C1bB425", + roles: ["transfer"], + }, + { + member: "0x1f4202eD5c33d229bCda7B9B6AB3B57caDf423e6", + roles: ["admin", "transfer", "minter"], + }, + { + member: "0x6844F0056d4C84CA2a894Fcf85473d75d8252b24", + roles: [ + "admin", + "minter", + "transfer", + "metadata", + "burner", + "manager", + "lister", + ], + }, + ], + }, +}; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx index 1725170816d..a45e97cb2d3 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx @@ -1,36 +1,34 @@ "use client"; -import { ToolTipLabel } from "@/components/ui/tooltip"; -import { Box, Flex, List, SimpleGrid, Tag } from "@chakra-ui/react"; +import { WalletAddress } from "@/components/blocks/wallet-address"; +import { Spinner } from "@/components/ui/Spinner/Spinner"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { TrackedLinkTW } from "@/components/ui/tracked-link"; import { getAllRoleMembers } from "contract-ui/hooks/permissions"; -import { useClipboard } from "hooks/useClipboard"; -import { CopyIcon } from "lucide-react"; +import { ArrowRightIcon } from "lucide-react"; import { useMemo } from "react"; -import { toast } from "sonner"; import { type ThirdwebContract, ZERO_ADDRESS } from "thirdweb"; import { useReadContract } from "thirdweb/react"; -import { Button } from "tw-components/button"; -import { Card } from "tw-components/card"; -import { Heading } from "tw-components/heading"; -import { TrackedLink, type TrackedLinkProps } from "tw-components/link"; -import { Text } from "tw-components/text"; -import { shortenIfAddress } from "utils/usedapp-external"; -interface PermissionsTableProps { +export function PermissionsTable(props: { contract: ThirdwebContract; - trackingCategory: TrackedLinkProps["category"]; + trackingCategory: string; chainSlug: string; -} - -export const PermissionsTable: React.FC = ({ - contract, - trackingCategory, - chainSlug, -}) => { +}) { const allRoleMembers = useReadContract(getAllRoleMembers, { - contract, + contract: props.contract, }); - const permissionsHref = `/${chainSlug}/${contract.address}/permissions`; + const members = useMemo(() => { return ( Object.entries(allRoleMembers.data || {}).reduce( @@ -52,128 +50,82 @@ export const PermissionsTable: React.FC = ({ }, [allRoleMembers.data]); return ( - - -

Permissions

- - View all -> - -
- {contract && ( - - - - Member - - - Roles - - - - - {members.length === 0 && ( -
- - - {allRoleMembers.isPending - ? "loading permissions" - : "no permissions found"} - - -
- )} -
- {members.map((e) => ( - - ))} -
-
-
- )} -
+ ); -}; - -interface PermissionsItemProps { - data: { member: string; roles: string[] }; } -const PermissionsItem: React.FC = ({ data }) => { - const { onCopy } = useClipboard(data.member); - +export function PermissionsTableUI(props: { + viewMoreLink: string; + trackingCategory: string; + members: { member: string; roles: string[] }[]; + isPending: boolean; +}) { return ( -
- - -
- - - - - {shortenIfAddress(data.member)} - -
-
+
+ {/* header */} +
+

Permissions

+ +
- + {/* table */} + + + + + Member + Roles + + - - {data.roles.slice(0, 3).map((role) => ( - - - {role} - - - ))} - {data.roles.length > 3 && ( - - - + {data.roles.length - 3} - - + + {props.members.map((data) => ( + + + + + +
+ {data.roles.map((role) => ( + + {role} + + ))} +
+
+
+ ))} +
+
+
+ + {props.members.length === 0 && ( +
+ {props.isPending ? ( + + ) : ( +

No Permissions

)} - - +
+ )}
); -}; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/TokenDetails.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/TokenDetails.tsx deleted file mode 100644 index 10f1ae5edc7..00000000000 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/TokenDetails.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { ThirdwebContract } from "thirdweb"; -import { TokenSupply } from "../../tokens/components/supply"; - -interface TokenDetailsProps { - contract: ThirdwebContract; -} - -export const TokenDetails: React.FC = ({ contract }) => { - return ( -
-
-

Token Details

-
- -
- ); -}; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/listing-stats.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/listing-stats.tsx index bab523ce7c1..ca9ecf570ac 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/listing-stats.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/listing-stats.tsx @@ -2,7 +2,7 @@ import { useMemo } from "react"; import type { ThirdwebContract } from "thirdweb"; import { totalAuctions, totalListings } from "thirdweb/extensions/marketplace"; import { useReadContract } from "thirdweb/react"; -import { StatCard } from "./stat-card"; +import { Stat } from "./stat-card"; const TotalListingsStat: React.FC<{ contract: ThirdwebContract }> = ({ contract, @@ -19,7 +19,7 @@ const TotalListingsStat: React.FC<{ contract: ThirdwebContract }> = ({ ); return ( - = ({ }); return ( - = ({ }); return ( - = ({ hasEnglishAuctions, }) => { return ( -
- {hasDirectListings && hasEnglishAuctions && contract && ( - - )} - {hasDirectListings && } - {hasEnglishAuctions && } +
+

+ Listing Details +

+
+ {hasDirectListings && hasEnglishAuctions && contract && ( + + )} + {hasDirectListings && } + {hasEnglishAuctions && } +
); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/stat-card.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/stat-card.tsx index 8e2b1e16f34..8ea5ee19733 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/stat-card.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/stat-card.tsx @@ -1,17 +1,17 @@ import { SkeletonContainer } from "@/components/ui/skeleton"; -export function StatCard(props: { - value: string; - isPending: boolean; - label: string; -}) { +export function Stat({ + label, + value, + isPending, +}: { label: string; value: string; isPending: boolean }) { return ( -
-
{props.label}
+
+
{label}
{v}
} + skeletonData={"000000"} + loadedData={isPending ? undefined : value} + render={(v) =>
{v}
} />
); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx index bd378a5a905..d7d6b09eaf9 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx @@ -6,7 +6,7 @@ import { TokenAirdropButton } from "./components/airdrop-button"; import { TokenBurnButton } from "./components/burn-button"; import { TokenClaimButton } from "./components/claim-button"; import { TokenMintButton } from "./components/mint-button"; -import { TokenSupply } from "./components/supply"; +import { TokenDetailsCard } from "./components/supply"; import { TokenTransferButton } from "./components/transfer-button"; interface ContractTokenPageProps { @@ -49,7 +49,7 @@ export const ContractTokensPage: React.FC = ({ return (
- Contract Tokens +

Tokens

{isClaimToSupported && ( @@ -63,7 +63,7 @@ export const ContractTokensPage: React.FC = ({
- +
); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx index 664f7bf24e0..78aaf63f80a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx @@ -1,60 +1,46 @@ -import { - Skeleton, - Stat, - StatHelpText, - StatLabel, - StatNumber, -} from "@chakra-ui/react"; import type { GetBalanceResult } from "thirdweb/extensions/erc20"; -import { Card } from "tw-components"; +import { Stat } from "../../overview/components/stat-card"; -interface TokenSupplyLayoutProps { - isTokenSupplySuccess: boolean; +export function TokenDetailsCardUI(props: { tokenSupply: GetBalanceResult | undefined; - isOwnedBalanceSuccess: boolean; - address: string | undefined; + isWalletConnected: boolean; ownedBalance: GetBalanceResult | undefined; -} - -export const TokenSupplyLayout: React.FC = ({ - isTokenSupplySuccess, - tokenSupply, - isOwnedBalanceSuccess, - address, - ownedBalance, -}) => { +}) { + const { tokenSupply, isWalletConnected, ownedBalance } = props; return ( -
- - Total Supply - - - {tokenSupply?.displayValue} {tokenSupply?.symbol} - - - - - Owned by you - - - {address ? ( - <> - {ownedBalance?.displayValue} {ownedBalance?.symbol} - - ) : ( - - Connect your wallet to see your balance - - )} - - - - - Decimals - - {tokenSupply?.decimals} - - +
+

+ Token Details +

+
+ + + {isWalletConnected && ( + + )} + + +
); -}; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply.tsx index 89b955f958a..3cfcbe0cbb7 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply.tsx @@ -8,13 +8,15 @@ import { totalSupply, } from "thirdweb/extensions/erc20"; import { useActiveAccount, useReadContract } from "thirdweb/react"; -import { TokenSupplyLayout } from "./supply-layout"; +import { TokenDetailsCardUI } from "./supply-layout"; interface TokenBalancesProps { contract: ThirdwebContract; } -export const TokenSupply: React.FC = ({ contract }) => { +export const TokenDetailsCard: React.FC = ({ + contract, +}) => { const address = useActiveAccount()?.address; const tokenBalanceQuery = useReadContract(getBalance, { @@ -35,13 +37,7 @@ export const TokenSupply: React.FC = ({ contract }) => { tokenMetadataQuery.data === undefined || tokenSupplyQuery.data === undefined ) { - return { - value: 0n, - displayValue: "0.0", - symbol: "LOA", - decimals: 18, - name: "Loading...", - }; + return undefined; } return { @@ -55,13 +51,9 @@ export const TokenSupply: React.FC = ({ contract }) => { }, [tokenMetadataQuery.data, tokenSupplyQuery.data]); return ( - ); diff --git a/apps/dashboard/src/components/product-pages/common/nav/data.ts b/apps/dashboard/src/components/product-pages/common/nav/data.ts deleted file mode 100644 index 49a8d1cb330..00000000000 --- a/apps/dashboard/src/components/product-pages/common/nav/data.ts +++ /dev/null @@ -1,130 +0,0 @@ -import authIcon from "../../../../../public/assets/product-icons/auth.png"; -import contractsProductIcon from "../../../../../public/assets/product-icons/contracts.png"; -import deployIcon from "../../../../../public/assets/product-icons/deploy.png"; -import embeddedWalletIcon from "../../../../../public/assets/product-icons/embedded-wallet.png"; -import engineProductIcon from "../../../../../public/assets/product-icons/engine.png"; -import extensionIcon from "../../../../../public/assets/product-icons/extensions.png"; -import payIcon from "../../../../../public/assets/product-icons/pay.svg"; -import publishIcon from "../../../../../public/assets/product-icons/publish.png"; -import rpcEdgeIcon from "../../../../../public/assets/product-icons/rpc-edge.png"; -import smartWalletIcon from "../../../../../public/assets/product-icons/smart-wallet.png"; -import storageIcon from "../../../../../public/assets/product-icons/storage.png"; -import walletSdkIcon from "../../../../../public/assets/product-icons/wallet-sdk.png"; -import type { SectionItemProps } from "./types"; - -export const PRODUCTS: SectionItemProps[] = [ - { - name: "Deploy", - label: "deploy", - description: "Contract deployment built for any use-case", - link: "/deploy", - dashboardLink: "/team/~/~/contracts", - icon: deployIcon, - section: "contracts", - inLandingPage: true, - }, - { - name: "Build", - label: "contractkit", - description: "Write your own smart contracts", - link: "/build", - dashboardLink: "https://portal.thirdweb.com/contracts/build/overview", - icon: extensionIcon, - section: "contracts", - inLandingPage: true, - }, - { - name: "Explore", - label: "explore", - description: "Ready-to-deploy contracts", - link: "/smart-contracts", - icon: contractsProductIcon, - section: "contracts", - inLandingPage: true, - }, - { - name: "Publish", - label: "publish", - description: "Publish your contracts on-chain", - link: "/publish", - icon: publishIcon, - section: "contracts", - }, - { - name: "Sign in", - label: "sign-in", - description: - "Flexible user sign-up flow with wallet and social sign-in methods", - link: "/connect", - dashboardLink: "https://playground.thirdweb.com/connect/sign-in/button", - icon: walletSdkIcon, - section: "connect", - inLandingPage: true, - }, - { - name: "Account Abstraction", - label: "smart-wallet", - description: "Complete toolkit for Account Abstraction", - link: "/account-abstraction", - dashboardLink: - "https://portal.thirdweb.com/connect/account-abstraction/overview", - icon: smartWalletIcon, - section: "connect", - inLandingPage: true, - }, - { - name: "In-App Wallets", - label: "embedded-wallets", - description: "Email & social login wallets for your customers", - link: "/embedded-wallets", - dashboardLink: "https://portal.thirdweb.com/connect/in-app-wallet/overview", - icon: embeddedWalletIcon, - section: "connect", - inLandingPage: true, - }, - { - name: "Pay", - label: "pay", - description: - "Easily integrate fiat onramps and cross-chain crypto purchases", - link: "/pay", - dashboardLink: "https://portal.thirdweb.com/connect/pay/overview", - icon: payIcon, - section: "connect", - inLandingPage: true, - }, - { - name: "Auth", - label: "auth", - description: "Authenticate users with their wallets", - link: "/auth", - icon: authIcon, - section: "connect", - }, - { - name: "Storage", - label: "storage", - description: "Secure, fast, decentralized storage", - link: "/storage", - icon: storageIcon, - section: "infrastructure", - }, - { - name: "RPC Edge", - label: "rpc-edge", - description: "Enterprise-grade RPCs, for free", - link: "/rpc-edge", - icon: rpcEdgeIcon, - section: "infrastructure", - }, - { - name: "Engine", - label: "engine", - description: "HTTP server with contract APIs and backend wallets", - link: "/engine", - dashboardLink: "/team/~/~/engine", - icon: engineProductIcon, - section: "infrastructure", - inLandingPage: true, - }, -]; diff --git a/apps/dashboard/src/components/product-pages/common/nav/types.ts b/apps/dashboard/src/components/product-pages/common/nav/types.ts deleted file mode 100644 index 67da358bc80..00000000000 --- a/apps/dashboard/src/components/product-pages/common/nav/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { StaticImageData } from "next/image"; - -export interface SectionItemProps { - name: string; - dashboardName?: string; - label: string; - description: string; - link: string; - dashboardLink?: string; - icon?: StaticImageData; - comingSoon?: boolean; - inLandingPage?: boolean; - section: - | "contracts" - | "connect" - | "infrastructure" - | "payments" - | "solutions" - | "resources" - | "tools" - | "sdks" - | "company"; -} diff --git a/apps/dashboard/src/tw-components/link.tsx b/apps/dashboard/src/tw-components/link.tsx index 065d8b92a0a..fbf21fee5bd 100644 --- a/apps/dashboard/src/tw-components/link.tsx +++ b/apps/dashboard/src/tw-components/link.tsx @@ -58,7 +58,7 @@ export const Link = reactForwardRef( Link.displayName = "Link"; -export interface TrackedLinkProps extends LinkProps { +interface TrackedLinkProps extends LinkProps { category: string; label?: string; trackingProps?: Record; diff --git a/apps/dashboard/src/tw-components/nft-media.tsx b/apps/dashboard/src/tw-components/nft-media.tsx index c0971270a3b..0b1b55f327b 100644 --- a/apps/dashboard/src/tw-components/nft-media.tsx +++ b/apps/dashboard/src/tw-components/nft-media.tsx @@ -18,6 +18,8 @@ export const NFTMediaWithEmptyState: React.FC<{ controls?: boolean; }> = (props) => { const client = useThirdwebClient(); + + // No media if (!(props.metadata.image || props.metadata.animation_url)) { return (