'use client' import { useState } from 'react' import useSWR from 'swr' const fetcher = (url: string) => fetch(url).then((r) => r.json()) interface PositionRow { id: number target_address: string target_nft_mint: string our_nft_mint: string | null pool_id: string pool_label: string tick_lower: number tick_upper: number price_lower: string price_upper: string size_usd: string amount_a: string amount_b: string symbol_a: string symbol_b: string status: string created_at: string } function formatPrice(price: string): string { if (!price) return '' const n = parseFloat(price) if (isNaN(n)) return price if (n >= 1000) return n.toFixed(2) if (n >= 1) return n.toFixed(4) if (n >= 0.0001) return n.toFixed(6) return n.toExponential(3) } export default function PositionsPage() { const { data: positions, mutate } = useSWR('/api/positions', fetcher, { refreshInterval: 5000 }) const [closingId, setClosingId] = useState(null) const [deletingId, setDeletingId] = useState(null) const [error, setError] = useState(null) const rows: PositionRow[] = positions || [] const handleClose = async (row: PositionRow) => { if ( !confirm( `Close position ${row.our_nft_mint?.slice(0, 8)}...? This will remove all liquidity on-chain and swap tokens back to USDC.`, ) ) { return } setClosingId(row.id) setError(null) try { const res = await fetch('/api/positions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: row.id }), }) const data = await res.json() if (!res.ok) { setError(data.error || 'Close failed') } else { mutate() } } catch (e) { setError(e instanceof Error ? e.message : 'Close failed') } finally { setClosingId(null) } } const handleDelete = async (row: PositionRow) => { if ( !confirm( `Delete position mapping #${row.id}? This only removes the record from the database, not from the blockchain.`, ) ) { return } setDeletingId(row.id) setError(null) try { const res = await fetch(`/api/positions?id=${row.id}`, { method: 'DELETE' }) const data = await res.json() if (!res.ok) { setError(data.error || 'Delete failed') } else { mutate() } } catch (e) { setError(e instanceof Error ? e.message : 'Delete failed') } finally { setDeletingId(null) } } return (

Position Mappings

{error && (
{error}
)}
{rows.length === 0 ? (

No position mappings yet

) : (
{rows.map((row) => ( ))}
Target Target NFT Our NFT Pool Size Price Range Status Created Actions
{row.target_address.slice(0, 4)}...{row.target_address.slice(-4)} {row.target_nft_mint.slice(0, 6)}... {row.our_nft_mint ? ( {row.our_nft_mint.slice(0, 6)}... ) : ( pending )} {row.pool_label || `${row.pool_id.slice(0, 6)}...`} {row.size_usd ? (
${row.size_usd}
{row.amount_a && row.symbol_a && `${row.amount_a} ${row.symbol_a}`} {row.amount_a && row.amount_b && ' + '} {row.amount_b && row.symbol_b && `${row.amount_b} ${row.symbol_b}`}
) : ( - )}
{row.price_lower && row.price_upper ? `${formatPrice(row.price_lower)} ~ ${formatPrice(row.price_upper)}` : `${row.tick_lower} ~ ${row.tick_upper}`} {row.status} {new Date(row.created_at + 'Z').toLocaleDateString()}
{row.status === 'active' && row.our_nft_mint && ( )}
)}
) }