import { useMemo, useState } from "react"; import { Plus, RefreshCw } from "lucide-react"; import { Button } from "@/components/ui/Button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card"; import { CryptoAddModal } from "@/components/crypto/CryptoAddModal"; import { CryptoHoldingsTable } from "@/components/crypto/CryptoHoldingsTable"; import { CryptoOverviewCard } from "@/components/crypto/CryptoOverviewCard"; import { CryptoPortfolioFormModal } from "@/components/crypto/CryptoPortfolioFormModal"; import { CryptoPortfolioTabs } from "@/components/crypto/CryptoPortfolioTabs"; import { CryptoStatsRow } from "@/components/crypto/CryptoStatsRow"; import { CryptoTransactionHistoryModal } from "@/components/crypto/CryptoTransactionHistoryModal"; import { CryptoTransactionModal } from "@/components/crypto/CryptoTransactionModal"; import { useCrypto90dPerformance, useCryptoHistory, useCryptoHoldings, useCryptoPortfolios, useDeleteCryptoHolding, useRefreshCryptoPrices, useUpdateCryptoHoldingRisk, } from "@/hooks/useCrypto"; import { getIntlLocale } from "@/lib/format"; import { useTranslation } from "@/lib/i18n"; import type { CryptoHolding, CryptoPortfolio, CryptoRange, CryptoTransaction, RiskLevel } from "@/types"; function formatSyncedAt(iso: string): string { return new Intl.DateTimeFormat(getIntlLocale(), { dateStyle: "medium", timeStyle: "short" }).format(new Date(iso)); } export function CryptoPage() { const { t } = useTranslation(); // "all" by default — a 30-day window makes a portfolio that's actually // grown steadily for years look demotivating whenever it's mid-dip. const [range, setRange] = useState("all"); // Single "hide balance" switch for the whole tab — owned here so every // money-displaying section (chart, stats, allocation, table, trade // history) masks together instead of the toggle only affecting whichever // component happened to own it. const [hidden, setHidden] = useState(false); // null = "All portfolios" tab. const [portfolioFilter, setPortfolioFilter] = useState(null); const { data: portfolios } = useCryptoPortfolios(); const { data, isLoading } = useCryptoHoldings(portfolioFilter); const { data: history, isLoading: isHistoryLoading } = useCryptoHistory(range, portfolioFilter); const { data: performance90d, isLoading: isPerformance90dLoading } = useCrypto90dPerformance(range, portfolioFilter); const refresh = useRefreshCryptoPrices(); const deleteHolding = useDeleteCryptoHolding(); const updateRisk = useUpdateCryptoHoldingRisk(); const [addOpen, setAddOpen] = useState(false); const [editingPortfolio, setEditingPortfolio] = useState(null); const [portfolioModalOpen, setPortfolioModalOpen] = useState(false); const [tradingHolding, setTradingHolding] = useState(null); const [historyHolding, setHistoryHolding] = useState(null); // Set together with tradingHolding when editing an entry from the history // list — CryptoTransactionModal is the same dialog for both "+" (new // trade, transaction stays null) and this (pre-filled, PATCHes instead of // POSTing). Cleared whenever that dialog closes, same as tradingHolding. const [editingTransaction, setEditingTransaction] = useState(null); const holdings = data?.holdings ?? []; const totalValue = holdings.reduce((sum, holding) => sum + (holding.value !== null ? Number(holding.value) : 0), 0); const portfoliosById = useMemo(() => new Map((portfolios ?? []).map((p) => [p.id, p])), [portfolios]); const performance90dByAsset = useMemo( () => new Map((performance90d?.items ?? []).map((item) => [item.asset_id, item.price_change_percent])), [performance90d] ); function openAddPortfolioModal() { setEditingPortfolio(null); setPortfolioModalOpen(true); } function openEditPortfolioModal(portfolio: CryptoPortfolio) { setEditingPortfolio(portfolio); setPortfolioModalOpen(true); } function handleDelete(holding: CryptoHolding) { if (window.confirm(t("crypto.confirmDelete", { name: holding.name }))) { deleteHolding.mutate(holding.asset_id); } } function handleRiskChange(holding: CryptoHolding, riskLevel: RiskLevel) { updateRisk.mutate({ assetId: holding.asset_id, riskLevel }); } function handleEditTransaction(transaction: CryptoTransaction) { setEditingTransaction(transaction); setTradingHolding(historyHolding); setHistoryHolding(null); } function closeTradeModal() { setTradingHolding(null); setEditingTransaction(null); } function openTradeModal(holding: CryptoHolding) { setEditingTransaction(null); setTradingHolding(holding); } return (
); }