diff --git a/frontend/app/admin/trades/page.tsx b/frontend/app/admin/trades/page.tsx index 1356544..625938d 100644 --- a/frontend/app/admin/trades/page.tsx +++ b/frontend/app/admin/trades/page.tsx @@ -5,6 +5,7 @@ import { useEffect, useState, useCallback } from "react"; import { Permission } from "../../auth-context"; import { api } from "../../api"; import { Header } from "../../components/Header"; +import { SatsDisplay } from "../../components/SatsDisplay"; import { useRequireAuth } from "../../hooks/useRequireAuth"; import { components } from "../../generated/api"; import { formatDateTime } from "../../utils/date"; @@ -25,59 +26,6 @@ function formatEur(cents: number): string { return `€${(cents / 100).toLocaleString("de-DE")}`; } -/** - * Format satoshi amount with styled components - * Leading zeros are subtle, main digits are prominent - */ -function SatsDisplay({ sats }: { sats: number }) { - const btc = sats / 100_000_000; - const btcStr = btc.toFixed(8); - const [whole, decimal] = btcStr.split("."); - - const part1 = decimal.slice(0, 2); - const part2 = decimal.slice(2, 5); - const part3 = decimal.slice(5, 8); - - const fullDecimal = part1 + part2 + part3; - let firstNonZero = fullDecimal.length; - for (let i = 0; i < fullDecimal.length; i++) { - if (fullDecimal[i] !== "0") { - firstNonZero = i; - break; - } - } - - const subtleStyle: React.CSSProperties = { - opacity: 0.45, - fontWeight: 400, - }; - - const renderPart = (part: string, startIdx: number) => { - return part.split("").map((char, i) => { - const globalIdx = startIdx + i; - const isSubtle = globalIdx < firstNonZero; - return ( - - {char} - - ); - }); - }; - - return ( - - - {whole}. - {renderPart(part1, 0)} - - {renderPart(part2, 2)} - - {renderPart(part3, 5)} - sats - - ); -} - /** * Get status display properties */ @@ -230,7 +178,7 @@ export default function AdminTradesPage() { return (
-
+

Trades

Manage Bitcoin exchange trades

diff --git a/frontend/app/components/Header.tsx b/frontend/app/components/Header.tsx index bb0b2d9..db88a04 100644 --- a/frontend/app/components/Header.tsx +++ b/frontend/app/components/Header.tsx @@ -14,7 +14,7 @@ type PageId = | "trades" | "admin-invites" | "admin-availability" - | "admin-appointments" + | "admin-trades" | "admin-price-history"; interface HeaderProps { @@ -37,7 +37,7 @@ const REGULAR_NAV_ITEMS: NavItem[] = [ ]; const ADMIN_NAV_ITEMS: NavItem[] = [ - { id: "admin-appointments", label: "Trades", href: "/admin/trades", adminOnly: true }, + { id: "admin-trades", label: "Trades", href: "/admin/trades", adminOnly: true }, { id: "admin-availability", label: "Availability", href: "/admin/availability", adminOnly: true }, { id: "admin-invites", label: "Invites", href: "/admin/invites", adminOnly: true }, { id: "admin-price-history", label: "Prices", href: "/admin/price-history", adminOnly: true }, diff --git a/frontend/app/components/SatsDisplay.tsx b/frontend/app/components/SatsDisplay.tsx new file mode 100644 index 0000000..8fef594 --- /dev/null +++ b/frontend/app/components/SatsDisplay.tsx @@ -0,0 +1,59 @@ +import React from "react"; + +/** + * Format satoshi amount with styled components. + * Leading zeros are subtle, main digits are prominent. + * e.g., 1876088 -> "₿ 0.01" (subtle) + "876 088 sats" (prominent) + */ +export function SatsDisplay({ sats }: { sats: number }) { + const btc = sats / 100_000_000; + const btcStr = btc.toFixed(8); + const [whole, decimal] = btcStr.split("."); + + // Group decimal into chunks: first 2, then two groups of 3 + const part1 = decimal.slice(0, 2); + const part2 = decimal.slice(2, 5); + const part3 = decimal.slice(5, 8); + + // Find where meaningful digits start (first non-zero after decimal) + const fullDecimal = part1 + part2 + part3; + let firstNonZero = fullDecimal.length; + for (let i = 0; i < fullDecimal.length; i++) { + if (fullDecimal[i] !== "0") { + firstNonZero = i; + break; + } + } + + // Build the display with subtle leading zeros and prominent digits + const subtleStyle: React.CSSProperties = { + opacity: 0.45, + fontWeight: 400, + }; + + // Determine which parts are subtle vs prominent + const renderPart = (part: string, startIdx: number) => { + return part.split("").map((char, i) => { + const globalIdx = startIdx + i; + const isSubtle = globalIdx < firstNonZero; + return ( + + {char} + + ); + }); + }; + + return ( + + + {whole}. + {renderPart(part1, 0)} + + {renderPart(part2, 2)} + + {renderPart(part3, 5)} + sats + + ); +} diff --git a/frontend/app/exchange/page.tsx b/frontend/app/exchange/page.tsx index 45ab773..7ad0a81 100644 --- a/frontend/app/exchange/page.tsx +++ b/frontend/app/exchange/page.tsx @@ -6,6 +6,7 @@ import { useRouter } from "next/navigation"; import { Permission } from "../auth-context"; import { api } from "../api"; import { Header } from "../components/Header"; +import { SatsDisplay } from "../components/SatsDisplay"; import { useRequireAuth } from "../hooks/useRequireAuth"; import { components } from "../generated/api"; import { formatDate, formatTime, getDateRange } from "../utils/date"; @@ -30,65 +31,6 @@ function formatEur(cents: number): string { return `€${(cents / 100).toLocaleString("de-DE")}`; } -/** - * Format satoshi amount with styled components - * Leading zeros are subtle, main digits are prominent - * e.g., 1876088 -> "₿ 0.01" (subtle) + "876 088 sats" (prominent) - */ -function SatsDisplay({ sats }: { sats: number }) { - const btc = sats / 100_000_000; - const btcStr = btc.toFixed(8); - const [whole, decimal] = btcStr.split("."); - - // Group decimal into chunks: first 2, then two groups of 3 - const part1 = decimal.slice(0, 2); - const part2 = decimal.slice(2, 5); - const part3 = decimal.slice(5, 8); - - // Find where meaningful digits start (first non-zero after decimal) - const fullDecimal = part1 + part2 + part3; - let firstNonZero = fullDecimal.length; - for (let i = 0; i < fullDecimal.length; i++) { - if (fullDecimal[i] !== "0") { - firstNonZero = i; - break; - } - } - - // Build the display with subtle leading zeros and prominent digits - const subtleStyle: React.CSSProperties = { - opacity: 0.45, - fontWeight: 400, - }; - - // Determine which parts are subtle vs prominent - const renderPart = (part: string, startIdx: number) => { - const chars = part.split("").map((char, i) => { - const globalIdx = startIdx + i; - const isSubtle = globalIdx < firstNonZero; - return ( - - {char} - - ); - }); - return chars; - }; - - return ( - - - {whole}. - {renderPart(part1, 0)} - - {renderPart(part2, 2)} - - {renderPart(part3, 5)} - sats - - ); -} - /** * Format price for display */ diff --git a/frontend/app/trades/page.tsx b/frontend/app/trades/page.tsx index 67af4ce..046d3b2 100644 --- a/frontend/app/trades/page.tsx +++ b/frontend/app/trades/page.tsx @@ -5,6 +5,7 @@ import { useEffect, useState, useCallback } from "react"; import { Permission } from "../auth-context"; import { api } from "../api"; import { Header } from "../components/Header"; +import { SatsDisplay } from "../components/SatsDisplay"; import { useRequireAuth } from "../hooks/useRequireAuth"; import { components } from "../generated/api"; import { formatDateTime } from "../utils/date"; @@ -25,59 +26,6 @@ function formatEur(cents: number): string { return `€${(cents / 100).toLocaleString("de-DE")}`; } -/** - * Format satoshi amount with styled components - * Leading zeros are subtle, main digits are prominent - */ -function SatsDisplay({ sats }: { sats: number }) { - const btc = sats / 100_000_000; - const btcStr = btc.toFixed(8); - const [whole, decimal] = btcStr.split("."); - - const part1 = decimal.slice(0, 2); - const part2 = decimal.slice(2, 5); - const part3 = decimal.slice(5, 8); - - const fullDecimal = part1 + part2 + part3; - let firstNonZero = fullDecimal.length; - for (let i = 0; i < fullDecimal.length; i++) { - if (fullDecimal[i] !== "0") { - firstNonZero = i; - break; - } - } - - const subtleStyle: React.CSSProperties = { - opacity: 0.45, - fontWeight: 400, - }; - - const renderPart = (part: string, startIdx: number) => { - return part.split("").map((char, i) => { - const globalIdx = startIdx + i; - const isSubtle = globalIdx < firstNonZero; - return ( - - {char} - - ); - }); - }; - - return ( - - - {whole}. - {renderPart(part1, 0)} - - {renderPart(part2, 2)} - - {renderPart(part3, 5)} - sats - - ); -} - /** * Get status display properties */ @@ -192,7 +140,7 @@ export default function TradesPage() { return (
-
+

My Trades

View and manage your Bitcoin trades