← Back
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...