diff --git a/frontend/app/admin/trades/page.tsx b/frontend/app/admin/trades/page.tsx index 8909f7a..d1f4b9f 100644 --- a/frontend/app/admin/trades/page.tsx +++ b/frontend/app/admin/trades/page.tsx @@ -16,6 +16,7 @@ import { bannerStyles, badgeStyles, buttonStyles, + tradeCardStyles, } from "../../styles/shared"; type AdminExchangeResponse = components["schemas"]["AdminExchangeResponse"]; @@ -178,13 +179,13 @@ export default function AdminTradesPage() { )} {isLoadingTrades ? ( -
Loading trades...
+
Loading trades...
) : trades.length === 0 ? ( -
+
{activeTab === "upcoming" ? "No upcoming trades." : "No trades found."}
) : ( -
+
{trades.map((trade) => { const status = getTradeStatusDisplay(trade.status); const isBuy = trade.direction === "buy"; @@ -192,10 +193,12 @@ export default function AdminTradesPage() { const canComplete = trade.status === "booked" && isPast && activeTab === "past"; return ( -
-
-
-
{formatDateTime(trade.slot_start)}
+
+
+
+
+ {formatDateTime(trade.slot_start)} +
{/* User Info */}
@@ -211,10 +214,10 @@ export default function AdminTradesPage() {
{/* Trade Details */} -
+
{isBuy ? "BUY" : "SELL"} - {formatEur(trade.eur_amount)} - - + {formatEur(trade.eur_amount)} + +
-
- Rate: - +
+ Rate: + € {trade.agreed_price_eur.toLocaleString("de-DE", { maximumFractionDigits: 0, })} /BTC - Market: - + Market: + € {trade.market_price_eur.toLocaleString("de-DE", { maximumFractionDigits: 0, @@ -337,7 +340,7 @@ export default function AdminTradesPage() { ); } -// Page-specific styles +// Page-specific styles (trade card styles are shared via tradeCardStyles) const styles: Record = { content: { flex: 1, @@ -393,36 +396,7 @@ const styles: Record = { fontSize: "0.875rem", minWidth: "200px", }, - tradeList: { - display: "flex", - flexDirection: "column", - gap: "0.75rem", - }, - tradeCard: { - background: "rgba(255, 255, 255, 0.03)", - border: "1px solid rgba(255, 255, 255, 0.08)", - borderRadius: "12px", - padding: "1.25rem", - transition: "all 0.2s", - }, - tradeHeader: { - display: "flex", - justifyContent: "space-between", - alignItems: "flex-start", - gap: "1rem", - }, - tradeInfo: { - display: "flex", - flexDirection: "column", - gap: "0.25rem", - }, - tradeTime: { - fontFamily: "'DM Sans', system-ui, sans-serif", - fontSize: "1rem", - fontWeight: 500, - color: "#fff", - marginBottom: "0.25rem", - }, + // Admin-specific: user contact info userInfo: { display: "flex", alignItems: "center", @@ -444,52 +418,7 @@ const styles: Record = { borderRadius: "4px", color: "rgba(129, 140, 248, 0.9)", }, - tradeDetails: { - display: "flex", - alignItems: "center", - gap: "0.5rem", - flexWrap: "wrap", - }, - directionBadge: { - fontFamily: "'DM Sans', system-ui, sans-serif", - fontSize: "0.7rem", - fontWeight: 600, - padding: "0.2rem 0.5rem", - borderRadius: "4px", - textTransform: "uppercase", - }, - amount: { - fontFamily: "'DM Mono', monospace", - fontSize: "0.95rem", - color: "#fff", - fontWeight: 500, - }, - arrow: { - color: "rgba(255, 255, 255, 0.3)", - fontSize: "0.8rem", - }, - satsAmount: { - fontFamily: "'DM Mono', monospace", - fontSize: "0.9rem", - color: "#f7931a", - }, - rateRow: { - display: "flex", - alignItems: "center", - gap: "0.5rem", - marginTop: "0.25rem", - flexWrap: "wrap", - }, - rateLabel: { - fontFamily: "'DM Sans', system-ui, sans-serif", - fontSize: "0.75rem", - color: "rgba(255, 255, 255, 0.4)", - }, - rateValue: { - fontFamily: "'DM Mono', monospace", - fontSize: "0.8rem", - color: "rgba(255, 255, 255, 0.7)", - }, + // Admin-specific: vertical button group buttonGroup: { display: "flex", flexDirection: "column", @@ -532,10 +461,4 @@ const styles: Record = { cursor: "pointer", transition: "all 0.2s", }, - emptyState: { - fontFamily: "'DM Sans', system-ui, sans-serif", - color: "rgba(255, 255, 255, 0.4)", - textAlign: "center", - padding: "3rem", - }, }; diff --git a/frontend/app/styles/shared.ts b/frontend/app/styles/shared.ts index dcae224..cc5fd59 100644 --- a/frontend/app/styles/shared.ts +++ b/frontend/app/styles/shared.ts @@ -659,6 +659,114 @@ export const toastStyles: StyleRecord = { }, }; +// ============================================================================= +// Trade Card Styles (shared between trades and admin/trades pages) +// ============================================================================= + +export const tradeCardStyles: StyleRecord = { + /** Container for list of trade cards */ + tradeList: { + display: "flex", + flexDirection: "column", + gap: "0.75rem", + }, + /** Individual trade card */ + tradeCard: { + background: tokens.surfaceBg, + border: `1px solid ${tokens.surfaceBorder}`, + borderRadius: tokens.radiusLg, + padding: "1.25rem", + transition: "all 0.2s", + }, + /** Trade card header - flex row for info + actions */ + tradeHeader: { + display: "flex", + justifyContent: "space-between", + alignItems: "flex-start", + gap: "1rem", + }, + /** Left side info container */ + tradeInfo: { + display: "flex", + flexDirection: "column", + gap: "0.25rem", + }, + /** Trade date/time display */ + tradeTime: { + fontFamily: tokens.fontSans, + fontSize: "1rem", + fontWeight: 500, + color: tokens.white, + marginBottom: "0.5rem", + }, + /** Trade amount details row */ + tradeDetails: { + display: "flex", + alignItems: "center", + gap: "0.5rem", + flexWrap: "wrap", + }, + /** BUY/SELL direction badge (base - color applied inline) */ + directionBadge: { + fontFamily: tokens.fontSans, + fontSize: "0.7rem", + fontWeight: 600, + padding: "0.2rem 0.5rem", + borderRadius: "4px", + textTransform: "uppercase", + }, + /** EUR amount display */ + amount: { + fontFamily: tokens.fontMono, + fontSize: "0.95rem", + color: tokens.white, + fontWeight: 500, + }, + /** Arrow between EUR and sats */ + arrow: { + color: tokens.textDisabled, + fontSize: "0.8rem", + }, + /** Sats amount in Bitcoin orange */ + satsAmount: { + fontFamily: tokens.fontMono, + fontSize: "0.9rem", + color: "#f7931a", // Bitcoin orange + }, + /** Rate info row */ + rateRow: { + display: "flex", + alignItems: "center", + gap: "0.5rem", + marginTop: "0.25rem", + flexWrap: "wrap", + }, + /** Rate label text */ + rateLabel: { + fontFamily: tokens.fontSans, + fontSize: "0.75rem", + color: tokens.textDim, + }, + /** Rate value in monospace */ + rateValue: { + fontFamily: tokens.fontMono, + fontSize: "0.8rem", + color: tokens.textSecondary, + }, + /** Button group container */ + buttonGroup: { + display: "flex", + gap: "0.5rem", + }, + /** Empty state for no trades */ + emptyState: { + fontFamily: tokens.fontSans, + color: tokens.textDim, + textAlign: "center" as const, + padding: "3rem", + }, +}; + // ============================================================================= // Misc Utility Styles // ============================================================================= diff --git a/frontend/app/trades/page.tsx b/frontend/app/trades/page.tsx index 87d4917..d311187 100644 --- a/frontend/app/trades/page.tsx +++ b/frontend/app/trades/page.tsx @@ -16,6 +16,7 @@ import { bannerStyles, badgeStyles, buttonStyles, + tradeCardStyles, } from "../styles/shared"; type ExchangeResponse = components["schemas"]["ExchangeResponse"]; @@ -94,9 +95,9 @@ export default function TradesPage() { {error &&
{error}
} {isLoadingTrades ? ( -
Loading trades...
+
Loading trades...
) : trades.length === 0 ? ( -
+

You don't have any trades yet.

Start trading @@ -108,19 +109,21 @@ export default function TradesPage() { {upcomingTrades.length > 0 && (

Upcoming ({upcomingTrades.length})

-
+
{upcomingTrades.map((trade) => { const status = getTradeStatusDisplay(trade.status); const isBuy = trade.direction === "buy"; return ( -
-
-
-
{formatDateTime(trade.slot_start)}
-
+
+
+
+
+ {formatDateTime(trade.slot_start)} +
+
{isBuy ? "BUY" : "SELL"} - {formatEur(trade.eur_amount)} - - + + {formatEur(trade.eur_amount)} + + +
-
- Rate: - +
+ Rate: + € {trade.agreed_price_eur.toLocaleString("de-DE", { maximumFractionDigits: 0, @@ -158,7 +163,7 @@ export default function TradesPage() {
{trade.status === "booked" && ( -
+
{confirmCancelId === trade.id ? ( <>