import { useState, useEffect } from 'react';
interface OrderLevel {
price: string;
size: string;
}
interface Props {
marketId: string;
}
export default function OrderbookMini({ marketId }: Props) {
const [bids, setBids] = useState<OrderLevel[]>([]);
const [asks, setAsks] = useState<OrderLevel[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [note, setNote] = useState<string | null>(null);
useEffect(() => {
const t = setTimeout(() => {
setLoading(true);
setError(null);
setNote(null);
fetch(`/api/screener/market/${encodeURIComponent(marketId)}/orderbook`)
.then(r => r.json())
.then(d => {
if (d.success) {
setBids(d.data.bids || []);
setAsks(d.data.asks || []);
if (d.data.note) setNote(d.data.note);
} else {
setError(d.error || 'No orderbook');
}
})
.catch(() => setError('Network error'))
.finally(() => setLoading(false));
}, 0);
return () => { clearTimeout(t); };
}, [marketId]);
if (loading) {
return (
<div className="orderbook-mini">
<h4 className="orderbook-title">Orderbook</h4>
<div className="orderbook-loading">
{Array.from({ length: 5 }, (_, i) => (
<div key={i} className="skeleton-line" style={{ width: '100%', height: 20, marginBottom: 3 }} />
))}
</div>
</div>
);
}
if (error) {
return (
<div className="orderbook-mini">
<h4 className="orderbook-title">Orderbook</h4>
<div className="orderbook-empty">{error}</div>
</div>
);
}
if (bids.length === 0 && asks.length === 0) {
return (
<div className="orderbook-mini">
<h4 className="orderbook-title">Orderbook</h4>
<div className="orderbook-empty">{note || 'No orders available'}</div>
</div>
);
}
// Find max size for bar width calculation
const allSizes = [...bids, ...asks].map(o => parseFloat(o.size) || 0);
const maxSize = Math.max(...allSizes, 1);
return (
<div className="orderbook-mini">
<h4 className="orderbook-title">Orderbook <span className="orderbook-subtitle">YES token</span></h4>
<div className="orderbook-header">
<span>Price</span>
<span>Size</span>
</div>
{/* Asks (reversed — lowest ask on bottom, closest to spread) */}
<div className="orderbook-asks">
{asks.slice(0, 5).reverse().map(ask => {
const size = parseFloat(ask.size) || 0;
const pct = (size / maxSize) * 100;
return (
<div key={ask.price} className="ob-row ob-ask">
<div className="ob-bar ob-bar-ask" style={{ width: `${pct}%` }} />
<span className="ob-price">{(parseFloat(ask.price) * 100).toFixed(1)}</span>
<span className="ob-size">{size.toFixed(0)}</span>
</div>
);
})}
</div>
{/* Spread indicator */}
{bids.length > 0 && asks.length > 0 && (
<div className="ob-spread">
Spread: {((parseFloat(asks[0].price) - parseFloat(bids[0].price)) * 100).toFixed(1)}c
</div>
)}
{/* Bids (highest bid on top, closest to spread) */}
<div className="orderbook-bids">
{bids.slice(0, 5).map(bid => {
const size = parseFloat(bid.size) || 0;
const pct = (size / maxSize) * 100;
return (
<div key={bid.price} className="ob-row ob-bid">
<div className="ob-bar ob-bar-bid" style={{ width: `${pct}%` }} />
<span className="ob-price">{(parseFloat(bid.price) * 100).toFixed(1)}</span>
<span className="ob-size">{size.toFixed(0)}</span>
</div>
);
})}
</div>
</div>
);
}
📜 Git History
6c47fa4chore: local Polikopi project home + Phase 1 redesign artifacts12 days ago
Show last diff
Loading...