"use client"; import { useState, CSSProperties } from "react"; import { useRouter } from "next/navigation"; import { Permission } from "../auth-context"; import { tradesApi } from "../api"; import { PageLayout } from "../components/PageLayout"; import { SatsDisplay } from "../components/SatsDisplay"; import { StatusBadge } from "../components/StatusBadge"; import { EmptyState } from "../components/EmptyState"; import { ConfirmationButton } from "../components/ConfirmationButton"; import { useRequireAuth } from "../hooks/useRequireAuth"; import { useAsyncData } from "../hooks/useAsyncData"; import { useMutation } from "../hooks/useMutation"; import { formatDateTime } from "../utils/date"; import { formatEur } from "../utils/exchange"; import { typographyStyles, tradeCardStyles } from "../styles/shared"; import { useTranslation } from "../hooks/useTranslation"; export default function TradesPage() { const router = useRouter(); const t = useTranslation("trades"); const tExchange = useTranslation("exchange"); const { user, isLoading, isAuthorized } = useRequireAuth({ requiredPermission: Permission.VIEW_OWN_EXCHANGES, fallbackRedirect: "/", }); const { data: trades = [], isLoading: isLoadingTrades, error, refetch: fetchTrades, } = useAsyncData(() => tradesApi.getTrades(), { enabled: !!user && isAuthorized, initialData: [], }); const [cancellingId, setCancellingId] = useState(null); const [confirmCancelId, setConfirmCancelId] = useState(null); const { mutate: cancelTrade } = useMutation( (publicId: string) => tradesApi.cancelTrade(publicId), { onSuccess: () => { fetchTrades(); setConfirmCancelId(null); }, } ); const handleCancel = async (publicId: string) => { setCancellingId(publicId); try { await cancelTrade(publicId); } finally { setCancellingId(null); } }; const upcomingTrades = (trades ?? []).filter( (t) => t.status === "booked" && new Date(t.slot_start) > new Date() ); const pastOrFinalTrades = (trades ?? []).filter( (t) => t.status !== "booked" || new Date(t.slot_start) <= new Date() ); return (

{t("page.title")}

{t("page.subtitle")}

{isLoadingTrades ? ( ) : (trades?.length ?? 0) === 0 ? ( {t("page.startTrading")} } /> ) : ( <> {/* Upcoming Trades */} {upcomingTrades.length > 0 && (

{t("page.upcoming", { count: upcomingTrades.length })}

{upcomingTrades.map((trade) => { const isBuy = trade.direction === "buy"; return (
{formatDateTime(trade.slot_start)}
{isBuy ? tExchange("direction.buy") : tExchange("direction.sell")} {isBuy ? `${tExchange("bookingStep.receiveVia")} ${trade.bitcoin_transfer_method === "onchain" ? tExchange("transferMethod.onchain") : tExchange("transferMethod.lightning")}` : `${tExchange("bookingStep.sendVia")} ${trade.bitcoin_transfer_method === "onchain" ? tExchange("transferMethod.onchain") : tExchange("transferMethod.lightning")}`} {formatEur(trade.eur_amount)}
{t("trade.rate")} € {trade.agreed_price_eur.toLocaleString("de-DE", { maximumFractionDigits: 0, })} /BTC
{""}
{trade.status === "booked" && ( handleCancel(trade.public_id)} onCancel={() => setConfirmCancelId(null)} onActionClick={() => setConfirmCancelId(trade.public_id)} actionLabel={t("trade.cancel")} isLoading={cancellingId === trade.public_id} confirmVariant="danger" confirmButtonStyle={styles.confirmButton} /> )}
); })}
)} {/* Past/Completed/Cancelled Trades */} {pastOrFinalTrades.length > 0 && (

{t("page.history", { count: pastOrFinalTrades.length })}

{pastOrFinalTrades.map((trade) => { const isBuy = trade.direction === "buy"; return (
{formatDateTime(trade.slot_start)}
{isBuy ? "BUY BTC" : "SELL BTC"} {isBuy ? `Receive via ${trade.bitcoin_transfer_method === "onchain" ? "Onchain" : "Lightning"}` : `Send via ${trade.bitcoin_transfer_method === "onchain" ? "Onchain" : "Lightning"}`} {formatEur(trade.eur_amount)}
{""}
); })}
)} )}
); } // Page-specific styles (trade card styles are shared via tradeCardStyles) const styles: Record = { content: { flex: 1, padding: "2rem", maxWidth: "800px", margin: "0 auto", width: "100%", }, section: { marginBottom: "2rem", }, sectionTitle: { fontFamily: "'DM Sans', system-ui, sans-serif", fontSize: "1.1rem", fontWeight: 500, color: "#fff", marginBottom: "1rem", }, tradeCardPast: { opacity: 0.6, background: "rgba(255, 255, 255, 0.01)", }, confirmButton: { fontFamily: "'DM Sans', system-ui, sans-serif", padding: "0.35rem 0.75rem", fontSize: "0.75rem", background: "rgba(239, 68, 68, 0.2)", border: "1px solid rgba(239, 68, 68, 0.3)", borderRadius: "6px", color: "#f87171", cursor: "pointer", transition: "all 0.2s", }, emptyStateLink: { color: "#a78bfa", textDecoration: "none", }, viewDetailsButton: { fontFamily: "'DM Sans', system-ui, sans-serif", padding: "0.35rem 0.75rem", fontSize: "0.75rem", background: "rgba(167, 139, 250, 0.15)", border: "1px solid rgba(167, 139, 250, 0.3)", borderRadius: "6px", color: "#a78bfa", cursor: "pointer", transition: "all 0.2s", }, };