arbret/frontend/app/exchange/components/ConfirmationStep.tsx
2025-12-26 19:00:56 +01:00

265 lines
8.1 KiB
TypeScript

"use client";
import { CSSProperties } from "react";
import { SatsDisplay } from "../../components/SatsDisplay";
import { components } from "../../generated/api";
import { formatTime } from "../../utils/date";
import { formatEur } from "../../utils/exchange";
import { buttonStyles } from "../../styles/shared";
import { useTranslation } from "../../hooks/useTranslation";
import { useLanguage } from "../../hooks/useLanguage";
type BookableSlot = components["schemas"]["BookableSlot"];
type Direction = "buy" | "sell";
type BitcoinTransferMethod = "onchain" | "lightning";
interface ConfirmationStepProps {
selectedSlot: BookableSlot;
selectedDate: Date | null;
direction: Direction;
bitcoinTransferMethod: BitcoinTransferMethod;
eurAmount: number;
satsAmount: number;
agreedPrice: number;
isBooking: boolean;
isPriceStale: boolean;
onConfirm: () => void;
onBack: () => void;
}
/**
* Format price for display
*/
function formatPrice(price: number): string {
return `${price.toLocaleString("es-ES", { maximumFractionDigits: 0 })}`;
}
const styles: Record<string, CSSProperties> = {
confirmCard: {
background: "rgba(255, 255, 255, 0.03)",
border: "1px solid rgba(255, 255, 255, 0.08)",
borderRadius: "12px",
padding: "1.5rem",
maxWidth: "400px",
},
confirmTitle: {
fontFamily: "'DM Sans', system-ui, sans-serif",
fontSize: "1.1rem",
fontWeight: 500,
color: "#fff",
marginBottom: "1rem",
},
confirmDetails: {
marginBottom: "1.5rem",
},
confirmRow: {
display: "flex",
justifyContent: "space-between",
padding: "0.5rem 0",
borderBottom: "1px solid rgba(255, 255, 255, 0.05)",
},
confirmLabel: {
fontFamily: "'DM Sans', system-ui, sans-serif",
color: "rgba(255, 255, 255, 0.5)",
fontSize: "0.875rem",
},
confirmValue: {
fontFamily: "'DM Sans', system-ui, sans-serif",
color: "#fff",
fontSize: "0.875rem",
fontWeight: 500,
},
satsValue: {
fontFamily: "'DM Mono', monospace",
color: "#f7931a", // Bitcoin orange
},
buttonRow: {
display: "flex",
gap: "0.75rem",
},
bookButton: {
fontFamily: "'DM Sans', system-ui, sans-serif",
flex: 1,
padding: "0.875rem",
border: "none",
borderRadius: "8px",
color: "#fff",
fontWeight: 600,
cursor: "pointer",
transition: "all 0.2s",
},
cancelButton: {
fontFamily: "'DM Sans', system-ui, sans-serif",
padding: "0.875rem 1.25rem",
background: "rgba(255, 255, 255, 0.05)",
border: "1px solid rgba(255, 255, 255, 0.1)",
borderRadius: "8px",
color: "rgba(255, 255, 255, 0.7)",
cursor: "pointer",
transition: "all 0.2s",
},
compressedBookingCard: {
background: "rgba(255, 255, 255, 0.03)",
border: "1px solid rgba(255, 255, 255, 0.08)",
borderRadius: "12px",
padding: "1rem 1.5rem",
marginBottom: "1.5rem",
},
compressedBookingHeader: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "0.5rem",
},
compressedBookingTitle: {
fontFamily: "'DM Sans', system-ui, sans-serif",
fontSize: "0.875rem",
color: "rgba(255, 255, 255, 0.5)",
},
compressedBookingDetails: {
display: "flex",
alignItems: "center",
gap: "0.75rem",
flexWrap: "wrap",
fontFamily: "'DM Sans', system-ui, sans-serif",
fontSize: "1rem",
color: "#fff",
},
summaryDivider: {
color: "rgba(255, 255, 255, 0.3)",
},
editButton: {
fontFamily: "'DM Sans', system-ui, sans-serif",
fontSize: "0.75rem",
color: "#a78bfa",
background: "transparent",
border: "none",
cursor: "pointer",
padding: 0,
},
};
/**
* Step 3 of the exchange wizard: Confirmation
* Shows compressed booking summary and final confirmation form.
*/
export function ConfirmationStep({
selectedSlot,
selectedDate,
direction,
bitcoinTransferMethod,
eurAmount,
satsAmount,
agreedPrice,
isBooking,
isPriceStale,
onConfirm,
onBack,
}: ConfirmationStepProps) {
const t = useTranslation("exchange");
const { locale } = useLanguage();
// Map locale codes to Intl locale strings
const intlLocale = locale === "es" ? "es-ES" : locale === "ca" ? "ca-ES" : "en-US";
return (
<>
{/* Compressed Booking Summary */}
<div style={styles.compressedBookingCard}>
<div style={styles.compressedBookingHeader}>
<span style={styles.compressedBookingTitle}>{t("confirmationStep.appointment")}</span>
<button onClick={onBack} style={styles.editButton}>
{t("confirmationStep.edit")}
</button>
</div>
<div style={styles.compressedBookingDetails}>
<span>
{selectedDate?.toLocaleDateString(intlLocale, {
weekday: "short",
month: "short",
day: "numeric",
})}
</span>
<span style={styles.summaryDivider}></span>
<span>
{formatTime(selectedSlot.start_time)} - {formatTime(selectedSlot.end_time)}
</span>
</div>
</div>
{/* Confirmation Card */}
<div style={styles.confirmCard}>
<h3 style={styles.confirmTitle}>{t("confirmationStep.confirmTrade")}</h3>
<div style={styles.confirmDetails}>
<div style={styles.confirmRow}>
<span style={styles.confirmLabel}>{t("confirmationStep.time")}</span>
<span style={styles.confirmValue}>
{formatTime(selectedSlot.start_time)} - {formatTime(selectedSlot.end_time)}
</span>
</div>
<div style={styles.confirmRow}>
<span style={styles.confirmLabel}>{t("confirmationStep.direction")}</span>
<span
style={{
...styles.confirmValue,
color: direction === "buy" ? "#4ade80" : "#f87171",
}}
>
{direction === "buy" ? t("direction.buyShort") : t("direction.sellShort")}
</span>
</div>
<div style={styles.confirmRow}>
<span style={styles.confirmLabel}>{t("confirmationStep.eur")}</span>
<span style={styles.confirmValue}>{formatEur(eurAmount)}</span>
</div>
<div style={styles.confirmRow}>
<span style={styles.confirmLabel}>{t("confirmationStep.btc")}</span>
<span style={{ ...styles.confirmValue, ...styles.satsValue }}>
<SatsDisplay sats={satsAmount} />
</span>
</div>
<div style={styles.confirmRow}>
<span style={styles.confirmLabel}>{t("confirmationStep.rate")}</span>
<span style={styles.confirmValue}>{formatPrice(agreedPrice)}/BTC</span>
</div>
<div style={styles.confirmRow}>
<span style={styles.confirmLabel}>{t("confirmationStep.payment")}</span>
<span style={styles.confirmValue}>
{direction === "buy"
? t("confirmationStep.receiveVia")
: t("confirmationStep.sendVia")}{" "}
{bitcoinTransferMethod === "onchain"
? t("transferMethod.onchain")
: t("transferMethod.lightning")}
</span>
</div>
</div>
<div style={styles.buttonRow}>
<button
onClick={onConfirm}
disabled={isBooking || isPriceStale}
style={{
...styles.bookButton,
background:
direction === "buy"
? "linear-gradient(135deg, #4ade80 0%, #22c55e 100%)"
: "linear-gradient(135deg, #f87171 0%, #ef4444 100%)",
...(isBooking || isPriceStale ? buttonStyles.buttonDisabled : {}),
}}
>
{isBooking
? t("confirmationStep.booking")
: isPriceStale
? t("confirmationStep.priceStale")
: direction === "buy"
? t("confirmationStep.confirmBuy")
: t("confirmationStep.confirmSell")}
</button>
<button onClick={onBack} disabled={isBooking} style={styles.cancelButton}>
{t("confirmationStep.back")}
</button>
</div>
</div>
</>
);
}