diff --git a/app/src/app/dashboard/page.tsx b/app/src/app/dashboard/page.tsx
new file mode 100644
index 0000000..9028383
--- /dev/null
+++ b/app/src/app/dashboard/page.tsx
@@ -0,0 +1,5 @@
+import { Dashboard } from '@/components/Dashboard'
+
+export default function DashboardPage() {
+ return
+}
diff --git a/app/src/components/Dashboard.tsx b/app/src/components/Dashboard.tsx
new file mode 100644
index 0000000..f1f1000
--- /dev/null
+++ b/app/src/components/Dashboard.tsx
@@ -0,0 +1,280 @@
+'use client'
+
+import { useState } from 'react'
+import { useRouter } from 'next/navigation'
+import { useWallet } from '@solana/connector/react'
+import { useMyListings } from '@/hooks/useMyListings'
+import { useMyOrders } from '@/hooks/useMyOrders'
+import { Badge, STATUS_COLORS } from '@/components/ui/Badge'
+import { Button } from '@/components/ui/Button'
+import { abbrev, fmtSol } from '@/lib/format'
+
+export function Dashboard() {
+ const router = useRouter()
+ const { account } = useWallet()
+ const [tab, setTab] = useState<'listings' | 'orders'>('listings')
+ const { data: myListings = [], isLoading: loadingL } = useMyListings()
+ const { data: myOrders = [], isLoading: loadingO } = useMyOrders()
+
+ if (!account) {
+ return (
+
+
+
+
+ My Dashboard
+
+
+ {abbrev(account)}
+
+
+
+
+
+
+ {(
+ [
+ { key: 'listings' as const, label: `My Listings · ${myListings.length}` },
+ { key: 'orders' as const, label: `My Orders · ${myOrders.length}` },
+ ] as const
+ ).map((t) => (
+
+ ))}
+
+
+ {tab === 'listings' && (
+
+ {loadingL && (
+
Loading…
+ )}
+ {!loadingL && myListings.length === 0 && (
+
+ You have no listings yet.
+
+ )}
+
+ {myListings.map((l) => {
+ const sc = STATUS_COLORS[l.data.isActive ? 'active' : 'inactive']
+ const avail = l.data.quantity - l.data.quantityReserved
+ return (
+
+
router.push(`/listing/${l.address}`)}
+ style={{ flex: 1, minWidth: 0, cursor: 'pointer' }}
+ >
+
+
+ {l.data.metadataUri || abbrev(l.address)}
+
+
+ {l.data.isActive ? 'Active' : 'Inactive'}
+
+
+
+ #{String(l.data.listingId)} · {fmtSol(l.data.price)} · {avail}/
+ {String(l.data.quantity)} avail · {String(l.data.quantityReserved)} pending
+
+
+
+
+
+
+
+ )
+ })}
+
+
+ )}
+
+ {tab === 'orders' && (
+
+ {loadingO && (
+
Loading…
+ )}
+ {!loadingO && myOrders.length === 0 && (
+
+ You have no orders as a buyer.
+
+ )}
+
+ {myOrders.map((o) => (
+
router.push(`/order/${o.address}`)}
+ style={{
+ background: 'var(--bg2)',
+ border: '1px solid var(--bd)',
+ borderRadius: 'var(--radius)',
+ padding: '18px 20px',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 18,
+ cursor: 'pointer',
+ }}
+ >
+
+
{abbrev(o.data.listing)}
+
+ #{String(o.data.orderId)} · {fmtSol(o.data.amount)}
+
+
+
+ #{String(o.data.escrowId)}
+
+
+ ))}
+
+
+ )}
+
+ )
+}
diff --git a/app/src/hooks/useMyListings.ts b/app/src/hooks/useMyListings.ts
new file mode 100644
index 0000000..48ff261
--- /dev/null
+++ b/app/src/hooks/useMyListings.ts
@@ -0,0 +1,8 @@
+import { useWallet } from '@solana/connector/react'
+import { useListings } from './useListings'
+
+export function useMyListings() {
+ const { account } = useWallet()
+ const { data: all = [], ...rest } = useListings()
+ return { data: all.filter((l) => l.data.seller === account), ...rest }
+}
diff --git a/app/src/hooks/useMyOrders.ts b/app/src/hooks/useMyOrders.ts
new file mode 100644
index 0000000..232636f
--- /dev/null
+++ b/app/src/hooks/useMyOrders.ts
@@ -0,0 +1,19 @@
+import { useQuery } from '@tanstack/react-query'
+import { useCluster, useWallet } from '@solana/connector/react'
+import { createSolanaRpc } from '@solana/kit'
+import { fetchOrdersByBuyer } from '@solisting/sdk'
+import type { Address } from '@solisting/sdk'
+
+export function useMyOrders() {
+ const { account } = useWallet()
+ const { cluster } = useCluster()
+ return useQuery({
+ queryKey: ['myOrders', account, cluster?.id],
+ queryFn: () => {
+ const rpc = createSolanaRpc(cluster!.url)
+ return fetchOrdersByBuyer(rpc, account as Address)
+ },
+ enabled: !!account && !!cluster,
+ refetchInterval: 30_000,
+ })
+}