)
}
-export default Card
\ No newline at end of file
+export default Card
diff --git a/frontend-lectures/lesson3/openriver/src/components/Cards.tsx b/frontend-lectures/lesson3/openriver/src/components/Cards.tsx
index c16fe41..16f8aae 100644
--- a/frontend-lectures/lesson3/openriver/src/components/Cards.tsx
+++ b/frontend-lectures/lesson3/openriver/src/components/Cards.tsx
@@ -1,25 +1,37 @@
-import { useReadContract } from "wagmi";
-import Card from "./Card";
-import { openriverAbi, openriverAddress } from "../contracts";
+import { useReadContract } from 'wagmi'
+import Card from './Card'
+import { openriverAbi, openriverAddress } from '../contracts'
export const Cards = ({ nftNum }: { nftNum?: bigint }) => {
-
- const {data: tokenIds} = useReadContract({
- abi: openriverAbi,
- address: openriverAddress,
- functionName: "tokenIds",
- })
+ const { data: tokenIds } = useReadContract({
+ abi: openriverAbi,
+ address: openriverAddress,
+ functionName: 'tokenIds',
+ })
+ const count = nftNum ?? tokenIds ?? BigInt(0)
+ if (Number(count) === 0) {
+ return (
+
- {Array.from({ length: Number(nftNum) }, (_, i) => i + 1).map((index) => (
-
- ))}
+
+ {Array.from({ length: Number(count) }, (_, i) => i + 1).map((i) => (
+
+ ))}
)
}
diff --git a/frontend-lectures/lesson3/openriver/src/components/Header.tsx b/frontend-lectures/lesson3/openriver/src/components/Header.tsx
index 162d66b..2eabd5f 100644
--- a/frontend-lectures/lesson3/openriver/src/components/Header.tsx
+++ b/frontend-lectures/lesson3/openriver/src/components/Header.tsx
@@ -1,21 +1,54 @@
import { ConnectButton } from '@rainbow-me/rainbowkit'
import Link from 'next/link'
-import React from 'react'
+import { useRouter } from 'next/router'
+
+const navLinks = [
+ { href: '/', label: 'Explore' },
+ { href: '/mint', label: 'Mint' },
+ { href: '/list', label: 'List' },
+ { href: '/myNFT', label: 'My NFTs' },
+]
const Header = () => {
+ const router = useRouter()
+
return (
-
-
Open River
-
-
Home
-
Minting
-
Listing
-
MyNFTs
+
+
)
}
-export default Header
\ No newline at end of file
+export default Header
diff --git a/frontend-lectures/lesson3/openriver/src/pages/_app.tsx b/frontend-lectures/lesson3/openriver/src/pages/_app.tsx
index e08ba49..a572129 100644
--- a/frontend-lectures/lesson3/openriver/src/pages/_app.tsx
+++ b/frontend-lectures/lesson3/openriver/src/pages/_app.tsx
@@ -1,6 +1,7 @@
import '../styles/globals.css';
import '@rainbow-me/rainbowkit/styles.css';
import type { AppProps } from 'next/app';
+import Head from 'next/head';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WagmiProvider } from 'wagmi';
@@ -13,15 +14,25 @@ const client = new QueryClient();
function MyApp({ Component, pageProps }: AppProps) {
return (
-
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
);
}
diff --git a/frontend-lectures/lesson3/openriver/src/pages/index.tsx b/frontend-lectures/lesson3/openriver/src/pages/index.tsx
index e158e7f..7d7ab4c 100644
--- a/frontend-lectures/lesson3/openriver/src/pages/index.tsx
+++ b/frontend-lectures/lesson3/openriver/src/pages/index.tsx
@@ -1,20 +1,70 @@
-import { ConnectButton } from '@rainbow-me/rainbowkit';
-import type { NextPage } from 'next';
-import Head from 'next/head';
-import styles from '../styles/Home.module.css';
-import Header from '../components/Header';
-import { Cards } from '../components/Cards';
+import type { NextPage } from 'next'
+import Head from 'next/head'
+import Link from 'next/link'
+import { Cards } from '../components/Cards'
const Home: NextPage = () => {
return (
-
+ <>
+
+
OpenRiver — NFT Marketplace
+
+
-
-
-
-
-
- );
-};
+
+ {/* Hero */}
+
+
+
+ Live on Sepolia Testnet
+
+
+ Discover & Trade
+
+ Digital Art
+
+
+
+ OpenRiver is a decentralized NFT marketplace. Mint your creations, set royalties, and trade with the community.
+
+
+
+ Mint an NFT
+
+
+ View My NFTs
+
+
+
-export default Home;
+ {/* Stats bar */}
+
+ {[
+ { label: 'Total NFTs', value: 'On-chain' },
+ { label: 'Network', value: 'Sepolia' },
+ { label: 'Contract', value: '0x8900…4823' },
+ ].map(({ label, value }) => (
+
+ ))}
+
+
+ {/* Marketplace grid */}
+
+
Marketplace
+
+
+
+ >
+ )
+}
+
+export default Home
diff --git a/frontend-lectures/lesson3/openriver/src/pages/layout.tsx b/frontend-lectures/lesson3/openriver/src/pages/layout.tsx
index 96b65cd..9a756b5 100644
--- a/frontend-lectures/lesson3/openriver/src/pages/layout.tsx
+++ b/frontend-lectures/lesson3/openriver/src/pages/layout.tsx
@@ -7,10 +7,12 @@ type LayoutProps = {
export default function DashboardLayout({ children }: LayoutProps) {
return (
-
-
+
+
{children}
-
+
)
-}
\ No newline at end of file
+}
diff --git a/frontend-lectures/lesson3/openriver/src/pages/list/index.tsx b/frontend-lectures/lesson3/openriver/src/pages/list/index.tsx
index d0896b1..2eec934 100644
--- a/frontend-lectures/lesson3/openriver/src/pages/list/index.tsx
+++ b/frontend-lectures/lesson3/openriver/src/pages/list/index.tsx
@@ -1,35 +1,100 @@
import { useState } from 'react'
-import { useWriteContract } from 'wagmi';
-import { openriverAbi, openriverAddress } from '../../contracts';
-
- const index = () => {
- const { writeContract: mintNFT } = useWriteContract();
- const [tokenId, setTokenId] = useState(0);
- const [price, setPrice] = useState(0);
-
- const handleMintNFT = async () => {
- mintNFT(
- {
- abi: openriverAbi,
- address: openriverAddress,
- functionName: "listOnMarketplace",
- args: [
- tokenId,
- price
- ]
- }
- )
- }
- return (
-
-
-
setTokenId(Number(e.target.value))} />
-
setPrice(Number(e.target.value))} />
-
+import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
+import { parseEther } from 'viem'
+import { openriverAbi, openriverAddress } from '../../contracts'
+import Head from 'next/head'
+
+const ListPage = () => {
+ const [tokenId, setTokenId] = useState('')
+ const [price, setPrice] = useState('')
+
+ const { data: hash, writeContract, isPending, error } = useWriteContract()
+ const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
+
+ const handleList = () => {
+ if (!tokenId || !price) return
+ writeContract({
+ abi: openriverAbi,
+ address: openriverAddress,
+ functionName: 'listOnMarketplace',
+ args: [BigInt(tokenId), parseEther(price)],
+ })
+ }
+
+ return (
+ <>
+
List NFT — OpenRiver
+
+
+
List an NFT
+
Put your NFT on the marketplace for sale
+
+
+
+
+
+
+ setTokenId(e.target.value)}
+ className="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-white placeholder-slate-600 outline-none transition focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20"
+ />
+
+
+
+
+ setPrice(e.target.value)}
+ className="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 pr-14 text-white placeholder-slate-600 outline-none transition focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20"
+ />
+ ETH
+
+
+
+
+
+
+ {isSuccess && (
+
+ ✅ NFT listed successfully!
+
+ )}
+ {error && (
+
+ {error.message.split('\n')[0]}
+
+ )}
+ {hash && (
+
+ View transaction ↗
+
+ )}
- )
+
+ >
+ )
}
-
-export default index
\ No newline at end of file
+export default ListPage
diff --git a/frontend-lectures/lesson3/openriver/src/pages/mint/index.tsx b/frontend-lectures/lesson3/openriver/src/pages/mint/index.tsx
index ebd6ea6..8e075ef 100644
--- a/frontend-lectures/lesson3/openriver/src/pages/mint/index.tsx
+++ b/frontend-lectures/lesson3/openriver/src/pages/mint/index.tsx
@@ -1,35 +1,95 @@
-import React, { useState } from 'react'
-import { useWriteContract } from 'wagmi';
-import { openriverAbi, openriverAddress } from '../../contracts';
-
- const index = () => {
- const { writeContract: mintNFT } = useWriteContract();
- const [tokenUrI, setTokenUrI] = useState("");
- const [royalty, setRoyalty] = useState(0);
-
- const handleMintNFT = async () => {
- mintNFT(
- {
- abi: openriverAbi,
- address: openriverAddress,
- functionName: "newItem",
- args: [
- tokenUrI,
- royalty
- ]
- }
- )
- }
- return (
-
-
-
setTokenUrI(e.target.value)} />
-
setRoyalty(Number(e.target.value))} />
-
+import { useState } from 'react'
+import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
+import { openriverAbi, openriverAddress } from '../../contracts'
+import Head from 'next/head'
+
+const MintPage = () => {
+ const [tokenUri, setTokenUri] = useState('')
+ const [royalty, setRoyalty] = useState('')
+
+ const { data: hash, writeContract, isPending, error } = useWriteContract()
+ const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
+
+ const handleMint = () => {
+ if (!tokenUri) return
+ writeContract({
+ abi: openriverAbi,
+ address: openriverAddress,
+ functionName: 'newItem',
+ args: [tokenUri, BigInt(royalty || '0')],
+ })
+ }
+
+ return (
+ <>
+
Mint NFT — OpenRiver
+
+
+
Mint an NFT
+
Upload your creation to the OpenRiver marketplace
+
+
+
+
+
+
+ setTokenUri(e.target.value)}
+ className="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-white placeholder-slate-600 outline-none transition focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20"
+ />
+
+
+
+ setRoyalty(e.target.value)}
+ className="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-white placeholder-slate-600 outline-none transition focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20"
+ />
+
+
+
+
+
+ {isSuccess && (
+
+ ✅ NFT minted successfully!
+
+ )}
+ {error && (
+
+ {error.message.split('\n')[0]}
+
+ )}
+ {hash && (
+
+ View transaction ↗
+
+ )}
- )
+
+ >
+ )
}
-
-export default index
\ No newline at end of file
+export default MintPage
diff --git a/frontend-lectures/lesson3/openriver/src/pages/myNFT/index.tsx b/frontend-lectures/lesson3/openriver/src/pages/myNFT/index.tsx
index 3c1e0b9..1cae3ab 100644
--- a/frontend-lectures/lesson3/openriver/src/pages/myNFT/index.tsx
+++ b/frontend-lectures/lesson3/openriver/src/pages/myNFT/index.tsx
@@ -1,37 +1,55 @@
-import { NextPage } from "next";
-import { Cards } from "../../components/Cards";
-import { useReadContract } from "wagmi";
-import { openriverAbi, openriverAddress } from "../../contracts";
-import { useEffect, useState } from "react";
+import { NextPage } from 'next'
+import Head from 'next/head'
+import { Cards } from '../../components/Cards'
+import { useReadContract, useAccount } from 'wagmi'
+import { openriverAbi, openriverAddress } from '../../contracts'
+import { useEffect, useState } from 'react'
const MyNFT: NextPage = () => {
- const [nftsNum, setNftsNum] = useState
()
-
- const { data: nftmaxNum } = useReadContract({
- abi: openriverAbi,
- address: openriverAddress,
- functionName: "tokenIds",
- }) as any
-
- useEffect(() => {
- setNftsNum(nftmaxNum)
- }, [nftmaxNum])
-
- useEffect(() => {
- setNftsNum(nftmaxNum)
- }, [nftmaxNum])
-
-
- return (
-
-
-
{nftsNum?.toString()}
-
-
-
-
+ const { isConnected } = useAccount()
+ const [nftsNum, setNftsNum] = useState
()
+
+ const { data: nftmaxNum } = useReadContract({
+ abi: openriverAbi,
+ address: openriverAddress,
+ functionName: 'tokenIds',
+ }) as any
+
+ useEffect(() => {
+ setNftsNum(nftmaxNum)
+ }, [nftmaxNum])
+
+ return (
+ <>
+ My NFTs — OpenRiver
+
+
+
My NFTs
+
+ {isConnected
+ ? `${nftsNum?.toString() ?? '…'} NFTs minted on-chain`
+ : 'Connect your wallet to get started'}
+
- );
-};
-export default MyNFT;
\ No newline at end of file
+ {!isConnected ? (
+
+
+
Wallet not connected
+
Connect using the button in the header
+
+ ) : (
+
+ )}
+
+ >
+ )
+}
+
+export default MyNFT
diff --git a/frontend-lectures/lesson3/openriver/src/styles/globals.css b/frontend-lectures/lesson3/openriver/src/styles/globals.css
index a461c50..37a97b4 100644
--- a/frontend-lectures/lesson3/openriver/src/styles/globals.css
+++ b/frontend-lectures/lesson3/openriver/src/styles/globals.css
@@ -1 +1,26 @@
-@import "tailwindcss";
\ No newline at end of file
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
+@import "tailwindcss";
+
+:root {
+ --font-sans: 'Inter', system-ui, sans-serif;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body {
+ margin: 0;
+ padding: 0;
+ font-family: var(--font-sans);
+ background-color: #0a0a0f;
+ color: #e2e8f0;
+ min-height: 100vh;
+}
+
+::-webkit-scrollbar { width: 6px; }
+::-webkit-scrollbar-track { background: #0a0a0f; }
+::-webkit-scrollbar-thumb { background: #3b3b52; border-radius: 3px; }
+
+[data-rk] button { font-family: var(--font-sans) !important; }