โ† Back
โ˜†
import { useNavigate } from 'react-router-dom';
import type { TradeActivity } from '../../hooks/usePortfolioHistory';
import { formatUsd } from '../../utils/format';
import { useT } from '../../i18n/LanguageContext';

interface Props {
  trades: TradeActivity[];
  loading: boolean;
  hasMore: boolean;
  onLoadMore: () => void;
  directions?: Record<string, string>;
}

export default function TradeHistory({ trades, loading, hasMore, onLoadMore, directions }: Props) {
  const navigate = useNavigate();
  const { t: tr } = useT();

  if (!loading && trades.length === 0) {
    return (
      <div className="th-empty">
        <p>{tr('th.empty')}</p>
        <p className="th-empty-sub">{tr('th.emptySub')}</p>
      </div>
    );
  }

  return (
    <div className="th-wrap">
      <div className="th-list">
        {trades.map(t => (
          <div
            key={t.id}
            className="th-row"
            onClick={() => t.conditionId && navigate(`/market/${t.conditionId}`)}
          >
            <div className="th-left">
              {t.icon && (
                <img src={t.icon} alt="" className="th-icon"
                  onError={e => { (e.target as HTMLImageElement).style.display = 'none'; }} />
              )}
              <div className="th-info">
                <span className="th-title">{t.title || tr('th.unknownMarket')}
                  {directions?.[(t.conditionId || '').toLowerCase()] === 'fade' && (
                    <span style={{ color: '#ff7a7a', fontWeight: 700, fontSize: '0.7rem', marginLeft: 6 }}>๐Ÿ’€ FADE</span>
                  )}
                </span>
                <div className="th-meta">
                  {t.type === 'REDEEM' ? (
                    // Redemptions have no side/price โ€” label them clearly (won โ†’ payout >0,
                    // lost โ†’ $0) instead of rendering an empty badge + "0.00 shares @ 0ยข".
                    <span className={`th-side ${t.amount > 0 ? 'th-redeem-win' : 'th-redeem-loss'}`}>
                      {tr('th.redeem')} ยท {t.amount > 0 ? tr('th.win') : tr('th.loss')}
                    </span>
                  ) : (
                    <>
                      <span className={`th-side th-side-${t.side?.toLowerCase()}`}>{t.side}</span>
                      <span className={`th-outcome th-outcome-${t.outcome?.toLowerCase()}`}>{t.outcome}</span>
                      <span className="th-size">{t.size?.toFixed(2)} {tr('th.shares')}</span>
                      <span className="th-price">@ {(t.price * 100).toFixed(0)}ยข</span>
                    </>
                  )}
                </div>
              </div>
            </div>
            <div className="th-right">
              {t.type === 'REDEEM' && t.realizedPnl < 0 ? (
                // Losing redemption: Data API pays $0; show the amount actually lost
                // (remaining cost basis) in red instead of a meaningless $0.
                <span className="th-amount th-amt-loss">โˆ’{formatUsd(Math.abs(t.realizedPnl))}</span>
              ) : t.type === 'REDEEM' ? (
                // Winning redemption: payout in green, matching the win badge.
                <span className="th-amount th-amt-win">{formatUsd(t.amount)}</span>
              ) : t.side === 'SELL' ? (
                // Closed via sell: show realized P&L (proceeds โˆ’ cost), signed & colored.
                <span className={`th-amount ${t.realizedPnl >= 0 ? 'th-amt-win' : 'th-amt-loss'}`}>
                  {t.realizedPnl >= 0 ? '+' : 'โˆ’'}{formatUsd(Math.abs(t.realizedPnl))}
                </span>
              ) : (
                <span className="th-amount">{formatUsd(t.amount)}</span>
              )}
              <span className="th-time">{formatTime(t.createdAt, tr)}</span>
            </div>
          </div>
        ))}
      </div>

      {loading && (
        <div className="th-loading">{tr('th.loading')}</div>
      )}

      {!loading && hasMore && (
        <button className="th-load-more" onClick={onLoadMore}>{tr('th.loadMore')}</button>
      )}
    </div>
  );
}

function formatTime(iso: string, tr: (k: string, p?: Record<string, string | number>) => string): string {
  if (!iso) return '';
  const d = new Date(iso);
  const now = new Date();
  const diff = now.getTime() - d.getTime();
  const mins = Math.floor(diff / 60_000);
  if (mins < 60) return tr('time.mAgo', { n: mins });
  const hrs = Math.floor(mins / 60);
  if (hrs < 24) return tr('time.hAgo', { n: hrs });
  const days = Math.floor(hrs / 24);
  if (days < 7) return tr('time.dAgo', { n: days });
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
}

๐Ÿ“œ Git History

b9c19bfchore(poli): reconcile local Flow/Insider/manual-trade work with deployed state3 days ago
6c47fa4chore: local Polikopi project home + Phase 1 redesign artifacts12 days ago
Show last diff
Loading...