{pageTitle == "Editor" && configurationId ? (
diff --git a/app/src/components/ContractsTable/ContractsTable.tsx b/app/src/components/ContractsTable/ContractsTable.tsx
index a7afe92..a74b627 100644
--- a/app/src/components/ContractsTable/ContractsTable.tsx
+++ b/app/src/components/ContractsTable/ContractsTable.tsx
@@ -2,9 +2,11 @@ import { useContracts } from "@/chain/client";
import { DataTable } from "../ListTable/DataTable";
import { Contract } from "@/lib/types";
import { ContractInfoColumns } from "../ListTable/ContractInfoColumns";
+import { useRouter } from "next/navigation";
export function ContractsTable() {
const { contracts, loading } = useContracts();
+ const router = useRouter();
return (
@@ -12,6 +14,9 @@ export function ContractsTable() {
columns={ContractInfoColumns()}
data={contracts}
isLoading={loading}
+ onRowClick={(row) =>
+ router.push(`/marketplace/gas/collection?id=${row.address}`)
+ }
/>
);
diff --git a/app/src/components/ListTable/CollectionsColumns.tsx b/app/src/components/ListTable/CollectionsColumns.tsx
new file mode 100644
index 0000000..393e249
--- /dev/null
+++ b/app/src/components/ListTable/CollectionsColumns.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import { ColumnDef } from "@tanstack/react-table";
+import { DataTableColumnHeader } from "./ColumnHeader";
+import { Contract } from "@/lib/types";
+import { formatAddress } from "@/lib/utils";
+
+export const CollectionsColumns = (): ColumnDef
[] => [
+ {
+ accessorKey: "address",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => formatAddress(row.original.address),
+ },
+ {
+ accessorKey: "name",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ return row.original.name;
+ },
+ },
+ {
+ accessorKey: "symbol",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ return row.original.symbol;
+ },
+ },
+];
diff --git a/app/src/components/NftCards/NftCards.tsx b/app/src/components/NftCards/NftCards.tsx
new file mode 100644
index 0000000..89c2751
--- /dev/null
+++ b/app/src/components/NftCards/NftCards.tsx
@@ -0,0 +1,109 @@
+import { Nft } from "@/lib/types";
+import {
+ Card,
+ CardHeader,
+ CardTitle,
+ CardDescription,
+ CardContent,
+} from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { Pagination } from "@/components/Pagination";
+import { useEffect, useState } from "react";
+import Image from "next/image";
+import { useNft } from "@/chain/client";
+
+const NFT_CARDS_QUANTITY = 20;
+
+export function NftCards({ id }: { id: string }) {
+ const { getCollectionNft } = useNft();
+ const [nft, setNft] = useState([]);
+ const [pageLoading, setPageLoading] = useState(false);
+ const [page, setPage] = useState(0);
+ const [startAfterMap, setStartAfterMap] = useState<{
+ [key: number]: string | undefined;
+ }>({ 0: undefined });
+ const [hasNextPage, setHasNextPage] = useState(false);
+
+ useEffect(() => {
+ const fetch = async () => {
+ setPageLoading(true);
+ const startAfter = startAfterMap[page];
+
+ try {
+ const { nft: fetchedNft, nextStartAfter } = await getCollectionNft(
+ id,
+ startAfter,
+ NFT_CARDS_QUANTITY,
+ );
+ setNft(fetchedNft);
+ setHasNextPage(!!nextStartAfter);
+
+ if (nextStartAfter) {
+ setStartAfterMap((prev) => ({
+ ...prev,
+ [page + 1]: nextStartAfter,
+ }));
+ }
+ } finally {
+ setPageLoading(false);
+ }
+ };
+
+ fetch();
+ }, [id, page, getCollectionNft]);
+
+ const handlePageChange = (newPage: number) => {
+ if (newPage < 0) return;
+ if (newPage > page && !hasNextPage) return;
+ setPage(newPage);
+ };
+
+ return (
+
+
+ {nft.map((n) => (
+
+ {n.image ? (
+
+
+
+ ) : (
+
+ No Image
+
+ )}
+
+
+ {n.name || `Token #${n.tokenId}`}
+
+ {n.description && (
+
+ {n.description}
+
+ )}
+
+
+
+
+
+ ))}
+
+
+ {nft.length > 0 && (
+
+ )}
+
+ );
+}
diff --git a/app/src/components/NftCards/index.ts b/app/src/components/NftCards/index.ts
new file mode 100644
index 0000000..ceb5ea9
--- /dev/null
+++ b/app/src/components/NftCards/index.ts
@@ -0,0 +1 @@
+export { NftCards } from "./NftCards";
diff --git a/app/src/components/Pagination/Pagination.tsx b/app/src/components/Pagination/Pagination.tsx
new file mode 100644
index 0000000..299a8a2
--- /dev/null
+++ b/app/src/components/Pagination/Pagination.tsx
@@ -0,0 +1,92 @@
+"use client";
+
+import {
+ Pagination as ShadcnPagination,
+ PaginationContent,
+ PaginationEllipsis,
+ PaginationItem,
+ PaginationLink,
+ PaginationNext,
+ PaginationPrevious,
+} from "@/components/ui/pagination";
+import { Spinner } from "@/components/Spinner";
+
+interface PaginationProps {
+ page: number;
+ hasNextPage: boolean;
+ onPageChange: (newPage: number) => void;
+ loading?: boolean;
+}
+
+export function Pagination({
+ page,
+ hasNextPage,
+ onPageChange,
+ loading = false,
+}: PaginationProps) {
+ return (
+
+
+
+ {
+ e.preventDefault();
+ onPageChange(page - 1);
+ }}
+ isActive={page === 0}
+ />
+
+
+
+ {
+ e.preventDefault();
+ onPageChange(0);
+ }}
+ >
+ {loading && page === 0 ? : "1"}
+
+
+
+ {page > 2 && (
+
+
+
+ )}
+
+ {[page - 1, page, page + 1]
+ .filter((p) => p > 0)
+ .map((p) => (
+
+ {
+ e.preventDefault();
+ onPageChange(p);
+ }}
+ >
+ {loading && p === page ? : p + 1}
+
+
+ ))}
+
+ {hasNextPage && (
+
+
+
+ )}
+
+
+ {
+ e.preventDefault();
+ onPageChange(page + 1);
+ }}
+ isActive={!hasNextPage}
+ />
+
+
+
+ );
+}
diff --git a/app/src/components/Pagination/index.ts b/app/src/components/Pagination/index.ts
new file mode 100644
index 0000000..dc87b27
--- /dev/null
+++ b/app/src/components/Pagination/index.ts
@@ -0,0 +1 @@
+export { Pagination } from "./Pagination";
diff --git a/app/src/lib/types.ts b/app/src/lib/types.ts
index 30e0bef..83aa8da 100644
--- a/app/src/lib/types.ts
+++ b/app/src/lib/types.ts
@@ -74,3 +74,11 @@ export interface Contract {
symbol: string;
address: string;
}
+
+export interface Nft {
+ tokenId: string;
+ tokenUri: string;
+ name: string;
+ image: string;
+ description: string;
+}