Skip to content

Commit f329f20

Browse files
feat(frontend): ft & nft contract pages (#1357)
1 parent ec784b4 commit f329f20

File tree

48 files changed

+2597
-25
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2597
-25
lines changed

apps/frontend/next.config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ const nextConfig: NextConfig = {
2727
];
2828
},
2929
output: 'standalone',
30+
async redirects() {
31+
return [
32+
{
33+
destination: '/tokens/transfers',
34+
permanent: true,
35+
source: '/tokentxns',
36+
},
37+
{
38+
destination: '/nft-tokens/transfers',
39+
permanent: true,
40+
source: '/nft-tokentxns',
41+
},
42+
];
43+
},
3044
};
3145

3246
export default nextConfig;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { NftTokenHolders } from '@/components/nft-tokens/token/holders';
3+
import {
4+
fetchNFTContract,
5+
fetchNFTContractHolderCount,
6+
fetchNFTContractHolders,
7+
} from '@/data/nft-tokens/contract';
8+
9+
type Props = PageProps<'/[lang]/nft-tokens/[token]/holders'>;
10+
11+
const HoldersPage = async ({ params, searchParams }: Props) => {
12+
const [{ token }, filters] = await Promise.all([params, searchParams]);
13+
const holdersPromise = fetchNFTContractHolders(token, filters);
14+
const holderCountPromise = fetchNFTContractHolderCount(token);
15+
const contractPromise = fetchNFTContract(token);
16+
17+
return (
18+
<ErrorSuspense fallback={<NftTokenHolders loading />}>
19+
<NftTokenHolders
20+
contractPromise={contractPromise}
21+
holderCountPromise={holderCountPromise}
22+
holdersPromise={holdersPromise}
23+
/>
24+
</ErrorSuspense>
25+
);
26+
};
27+
28+
export default HoldersPage;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { ActiveLink } from '@/components/link';
3+
import { TabLink, TabLinks } from '@/components/tab-links';
4+
import { NftTokenHeader } from '@/components/nft-tokens/token/header';
5+
import { Overview } from '@/components/nft-tokens/token/overview';
6+
import { Profile } from '@/components/nft-tokens/token/profile';
7+
import {
8+
fetchNFTContract,
9+
fetchNFTContractHolderCount,
10+
fetchNFTContractTxnCount,
11+
} from '@/data/nft-tokens/contract';
12+
import { ScrollArea, ScrollBar } from '@/ui/scroll-area';
13+
14+
type Props = LayoutProps<'/[lang]/nft-tokens/[token]'>;
15+
16+
const NftTokenLayout = async ({ children, params }: Props) => {
17+
const { token } = await params;
18+
const contractPromise = fetchNFTContract(token);
19+
const holderCountPromise = fetchNFTContractHolderCount(token);
20+
const txCountPromise = fetchNFTContractTxnCount(token, {});
21+
22+
return (
23+
<>
24+
<h1 className="text-body-xl text-muted-foreground flex flex-wrap items-center gap-2">
25+
NFT Token:{' '}
26+
<ErrorSuspense fallback={<NftTokenHeader loading token={token} />}>
27+
<NftTokenHeader contractPromise={contractPromise} token={token} />
28+
</ErrorSuspense>
29+
</h1>
30+
<div className="mt-6 grid gap-4 lg:grid-cols-2">
31+
<ErrorSuspense fallback={<Overview loading />}>
32+
<Overview
33+
contractPromise={contractPromise}
34+
holderCountPromise={holderCountPromise}
35+
txCountPromise={txCountPromise}
36+
/>
37+
</ErrorSuspense>
38+
<ErrorSuspense fallback={<Profile loading token={token} />}>
39+
<Profile contractPromise={contractPromise} token={token} />
40+
</ErrorSuspense>
41+
</div>
42+
<ScrollArea className="mt-10 mb-3 w-full whitespace-nowrap">
43+
<TabLinks>
44+
<TabLink asChild>
45+
<ActiveLink exact href={`/nft-tokens/${token}`}>
46+
Transfers
47+
</ActiveLink>
48+
</TabLink>
49+
<TabLink asChild>
50+
<ActiveLink href={`/nft-tokens/${token}/holders`}>
51+
Holders
52+
</ActiveLink>
53+
</TabLink>
54+
</TabLinks>
55+
<ScrollBar orientation="horizontal" />
56+
</ScrollArea>
57+
{children}
58+
</>
59+
);
60+
};
61+
62+
export default NftTokenLayout;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { NftTokenTransfers } from '@/components/nft-tokens/token/transfers';
3+
import {
4+
fetchNFTContractTxnCount,
5+
fetchNFTContractTxns,
6+
} from '@/data/nft-tokens/contract';
7+
8+
type Props = PageProps<'/[lang]/nft-tokens/[token]'>;
9+
10+
const NftTokenPage = async ({ params, searchParams }: Props) => {
11+
const [{ token }, filters] = await Promise.all([params, searchParams]);
12+
const nftsPromise = fetchNFTContractTxns(token, filters);
13+
const nftCountPromise = fetchNFTContractTxnCount(token, filters);
14+
15+
return (
16+
<ErrorSuspense fallback={<NftTokenTransfers loading />}>
17+
<NftTokenTransfers
18+
nftCountPromise={nftCountPromise}
19+
nftsPromise={nftsPromise}
20+
/>
21+
</ErrorSuspense>
22+
);
23+
};
24+
25+
export default NftTokenPage;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { NftTokenTransfers } from '@/components/nft-tokens/transfers';
3+
import { fetchNFTTxnCount, fetchNFTTxns } from '@/data/nft-tokens';
4+
5+
const NftTransfersPage = async ({
6+
searchParams,
7+
}: {
8+
searchParams: Promise<Record<string, string | string[] | undefined>>;
9+
}) => {
10+
const filters = await searchParams;
11+
const nftsPromise = fetchNFTTxns(filters);
12+
const nftCountPromise = fetchNFTTxnCount(filters);
13+
14+
return (
15+
<>
16+
<h1 className="text-headline-lg mb-6">NFT Token Transfers (NEP-171)</h1>
17+
<ErrorSuspense fallback={<NftTokenTransfers loading />}>
18+
<NftTokenTransfers
19+
nftCountPromise={nftCountPromise}
20+
nftsPromise={nftsPromise}
21+
/>
22+
</ErrorSuspense>
23+
</>
24+
);
25+
};
26+
27+
export default NftTransfersPage;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { TokenFaq } from '@/components/tokens/token/faq';
3+
import { fetchDeployments } from '@/data/address/contract';
4+
import {
5+
fetchFTContract,
6+
fetchFTContractHolderCount,
7+
fetchFTContractTxnCount,
8+
} from '@/data/tokens/contract';
9+
10+
type Props = PageProps<'/[lang]/tokens/[token]/faq'>;
11+
12+
const FaqPage = async ({ params }: Props) => {
13+
const { token } = await params;
14+
const contractPromise = fetchFTContract(token);
15+
const deploymentsPromise = fetchDeployments(token);
16+
const txCountPromise = fetchFTContractTxnCount(token, {});
17+
const holderCountPromise = fetchFTContractHolderCount(token);
18+
19+
return (
20+
<ErrorSuspense fallback={<TokenFaq loading />}>
21+
<TokenFaq
22+
contractPromise={contractPromise}
23+
deploymentsPromise={deploymentsPromise}
24+
holderCountPromise={holderCountPromise}
25+
txCountPromise={txCountPromise}
26+
/>
27+
</ErrorSuspense>
28+
);
29+
};
30+
31+
export default FaqPage;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { TokenHolders } from '@/components/tokens/token/holders';
3+
import {
4+
fetchFTContract,
5+
fetchFTContractHolderCount,
6+
fetchFTContractHolders,
7+
} from '@/data/tokens/contract';
8+
9+
type Props = PageProps<'/[lang]/tokens/[token]/holders'>;
10+
11+
const HoldersPage = async ({ params, searchParams }: Props) => {
12+
const [{ token }, filters] = await Promise.all([params, searchParams]);
13+
const holdersPromise = fetchFTContractHolders(token, filters);
14+
const holderCountPromise = fetchFTContractHolderCount(token);
15+
const contractPromise = fetchFTContract(token);
16+
17+
return (
18+
<ErrorSuspense fallback={<TokenHolders loading />}>
19+
<TokenHolders
20+
contractPromise={contractPromise}
21+
holderCountPromise={holderCountPromise}
22+
holdersPromise={holdersPromise}
23+
/>
24+
</ErrorSuspense>
25+
);
26+
};
27+
28+
export default HoldersPage;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { TokenInfo } from '@/components/tokens/token/info';
3+
import { fetchFTContract } from '@/data/tokens/contract';
4+
5+
type Props = PageProps<'/[lang]/tokens/[token]/info'>;
6+
7+
const InfoPage = async ({ params }: Props) => {
8+
const { token } = await params;
9+
const contractPromise = fetchFTContract(token);
10+
11+
return (
12+
<ErrorSuspense fallback={<TokenInfo loading />}>
13+
<TokenInfo contractPromise={contractPromise} />
14+
</ErrorSuspense>
15+
);
16+
};
17+
18+
export default InfoPage;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { ActiveLink } from '@/components/link';
3+
import { TabLink, TabLinks } from '@/components/tab-links';
4+
import { TokenHeader } from '@/components/tokens/token/header';
5+
import { Overview } from '@/components/tokens/token/overview';
6+
import { Profile } from '@/components/tokens/token/profile';
7+
import {
8+
fetchFTContract,
9+
fetchFTContractHolderCount,
10+
fetchFTContractTxnCount,
11+
} from '@/data/tokens/contract';
12+
import { ScrollArea, ScrollBar } from '@/ui/scroll-area';
13+
14+
type Props = LayoutProps<'/[lang]/tokens/[token]'>;
15+
16+
const TokenLayout = async ({ children, params }: Props) => {
17+
const { token } = await params;
18+
const contractPromise = fetchFTContract(token);
19+
const holderCountPromise = fetchFTContractHolderCount(token);
20+
const txCountPromise = fetchFTContractTxnCount(token, {});
21+
22+
return (
23+
<>
24+
<h1 className="text-body-xl text-muted-foreground flex flex-wrap items-center gap-2">
25+
Token:{' '}
26+
<ErrorSuspense fallback={<TokenHeader loading token={token} />}>
27+
<TokenHeader contractPromise={contractPromise} token={token} />
28+
</ErrorSuspense>
29+
</h1>
30+
<div className="mt-6 grid gap-4 lg:grid-cols-2">
31+
<ErrorSuspense fallback={<Overview loading />}>
32+
<Overview
33+
contractPromise={contractPromise}
34+
holderCountPromise={holderCountPromise}
35+
txCountPromise={txCountPromise}
36+
/>
37+
</ErrorSuspense>
38+
<ErrorSuspense fallback={<Profile loading token={token} />}>
39+
<Profile contractPromise={contractPromise} token={token} />
40+
</ErrorSuspense>
41+
</div>
42+
<ScrollArea className="mt-10 mb-3 w-full whitespace-nowrap">
43+
<TabLinks>
44+
<TabLink asChild>
45+
<ActiveLink exact href={`/tokens/${token}`}>
46+
Transfers
47+
</ActiveLink>
48+
</TabLink>
49+
<TabLink asChild>
50+
<ActiveLink href={`/tokens/${token}/holders`}>Holders</ActiveLink>
51+
</TabLink>
52+
<TabLink asChild>
53+
<ActiveLink href={`/tokens/${token}/info`}>Information</ActiveLink>
54+
</TabLink>
55+
<TabLink asChild>
56+
<ActiveLink href={`/tokens/${token}/faq`}>FAQ</ActiveLink>
57+
</TabLink>
58+
</TabLinks>
59+
<ScrollBar orientation="horizontal" />
60+
</ScrollArea>
61+
{children}
62+
</>
63+
);
64+
};
65+
66+
export default TokenLayout;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ErrorSuspense } from '@/components/error-suspense';
2+
import { TokenTransfers } from '@/components/tokens/token/transfers';
3+
import {
4+
fetchFTContractTxnCount,
5+
fetchFTContractTxns,
6+
} from '@/data/tokens/contract';
7+
8+
type Props = PageProps<'/[lang]/tokens/[token]'>;
9+
10+
const TokenPage = async ({ params, searchParams }: Props) => {
11+
const [{ token }, filters] = await Promise.all([params, searchParams]);
12+
const ftsPromise = fetchFTContractTxns(token, filters);
13+
const ftCountPromise = fetchFTContractTxnCount(token, filters);
14+
15+
return (
16+
<ErrorSuspense fallback={<TokenTransfers loading />}>
17+
<TokenTransfers ftCountPromise={ftCountPromise} ftsPromise={ftsPromise} />
18+
</ErrorSuspense>
19+
);
20+
};
21+
22+
export default TokenPage;

0 commit comments

Comments
 (0)