diff --git a/components/balanceHistory/BalanceHistoryChart.tsx b/components/balanceHistory/BalanceHistoryChart.tsx index c5bbd2a179298887daef392107739caae403b03f..8c413a91355738dbd689be58e9227a71cf367fc7 100644 --- a/components/balanceHistory/BalanceHistoryChart.tsx +++ b/components/balanceHistory/BalanceHistoryChart.tsx @@ -23,8 +23,6 @@ import { ArrowDown, ArrowUp, Minus } from "lucide-react"; import { useI18n } from "@/i18n/i18n"; import useDynamicGlobal from "@/hooks/api/homePage/useDynamicGlobal"; import { useHiveChainContext } from "@/contexts/HiveChainContext"; -import { convertVestsToHive, convertVestsToHP } from "@/utils/Calculations"; -import { grabNumericValue } from "@/utils/StringUtils"; import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; import { useSettings } from "@/contexts/SettingsContext"; @@ -63,7 +61,7 @@ const BalanceHistoryChart: React.FC = ({ selectedCoinType, setSelectedCoinType, }) => { - const { t, dir, locale } = useI18n(); + const { t, dir } = useI18n(); const { hiveChain } = useHiveChainContext(); const { dynamicGlobalData } = useDynamicGlobal(); const { settings } = useSettings(); @@ -90,89 +88,53 @@ const BalanceHistoryChart: React.FC = ({ return () => window.removeEventListener("resize", handleResize); }, []); - const processedData = useCallback( - (data: any, type: string) => { - if (!dynamicGlobalData || !hiveChain || !data) return []; - - return data.map((item: any) => { - const hivePrice = parseFloat(item.hivePrice || "0"); - - if (type === "VESTS") { - const vests = item.balance?.toString() || "0"; - - // Convert VESTS to HP or Hive - let convertedHPRaw = unit === "hp" - ? hiveChain.vestsToHp( - vests, - dynamicGlobalData.headBlockDetails.rawTotalVestingFundHive, - dynamicGlobalData.headBlockDetails.rawTotalVestingShares - ) - : hiveChain.vestsToHp( // using vestsToHp also for Hive conversion - vests, - dynamicGlobalData.headBlockDetails.rawTotalVestingFundHive, - dynamicGlobalData.headBlockDetails.rawTotalVestingShares - ); - - // Ensure convertedValue is a number - let convertedValue: number; - if (typeof convertedHPRaw === "number") { - convertedValue = convertedHPRaw; - } else if (convertedHPRaw && typeof convertedHPRaw === "object" && "amount" in convertedHPRaw) { - convertedValue = parseFloat(convertedHPRaw.amount); - } else if (typeof convertedHPRaw === "string") { - convertedValue = parseFloat(convertedHPRaw); - } else { - convertedValue = 0; - } + const processedData = useCallback( + (data: any, type: string) => { + if (!dynamicGlobalData || !hiveChain || !data) return []; - const dollarValueFull = - !isNaN(convertedValue) && !isNaN(hivePrice) - ? convertedValue * hivePrice /100 - : 0; + return data.map((item: any) => { + const hivePrice = parseFloat(item.hivePrice || "0"); - return { - ...item, - convertedHive: convertedValue, - dollarValue: dollarValueFull, - }; - } + if (type === "VESTS") { + const vests = item.balance?.toString() || "0"; - if (type === "HIVE") { - const dollarValue = - !isNaN(item.balance) && !isNaN(hivePrice) - ? item.balance * hivePrice - : 0; + let convertedHPRaw = hiveChain.vestsToHp( + vests, + dynamicGlobalData.headBlockDetails.rawTotalVestingFundHive, + dynamicGlobalData.headBlockDetails.rawTotalVestingShares + ); - return { ...item, dollarValue }; - } + let convertedValue: number; + if (typeof convertedHPRaw === "number") convertedValue = convertedHPRaw; + else if (convertedHPRaw && typeof convertedHPRaw === "object" && "amount" in convertedHPRaw) + convertedValue = parseFloat(convertedHPRaw.amount); + else if (typeof convertedHPRaw === "string") convertedValue = parseFloat(convertedHPRaw); + else convertedValue = 0; - if (type === "HBD") { - return { ...item, dollarValue: !isNaN(item.balance) ? item.balance : 0 }; - } + const dollarValueFull = + !isNaN(convertedValue) && !isNaN(hivePrice) ? (convertedValue * hivePrice) : 0; + + return { ...item, convertedHive: convertedValue, dollarValue: dollarValueFull }; + } + + if (type === "HIVE") { + const dollarValue = !isNaN(item.balance) && !isNaN(hivePrice) ? item.balance * hivePrice : 0; + return { ...item, dollarValue }; + } + + if (type === "HBD") { + return { ...item, dollarValue: !isNaN(item.balance) ? item.balance : 0 }; + } + + return item; + }); + }, + [dynamicGlobalData, hiveChain] + ); - return item; - }); - }, - [dynamicGlobalData, hiveChain, unit] -); - - - const dataMap: Record< - string, - { - timestamp: string; - balance_change: number; - balance: number; - savings_balance?: number; - savings_balance_change?: number; - hivePrice: string; - dollarValue?: number; - convertedHive?: number; - }[] - > = useMemo(() => { + const dataMap = useMemo(() => { return { - [selectedCoinType]: - processedData(aggregatedAccountBalanceHistory, selectedCoinType) || [], + [selectedCoinType]: processedData(aggregatedAccountBalanceHistory, selectedCoinType) || [], }; }, [processedData, aggregatedAccountBalanceHistory, selectedCoinType]); @@ -180,17 +142,20 @@ const BalanceHistoryChart: React.FC = ({ setSelectedCoinType(coinType); }; - const displayData = useMemo(() => dataMap[selectedCoinType], [selectedCoinType]); + const displayData = useMemo(() => dataMap[selectedCoinType], [selectedCoinType, dataMap]); // ---------------- Tooltip ---------------- - const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: any[]; label?: string; }) => { + const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: any[]; label?: string }) => { const { dir: tooltipDir } = useI18n(); if (quickView || !active || !payload || payload.length === 0) return null; const isTooltipRTL = tooltipDir === "rtl"; - const selectedData = displayData?.find((item) => item.timestamp === label); + const selectedData = displayData?.find((item: any) => item.timestamp === label); if (!selectedData) return null; - const actualBalance = selectedData?.balance ?? 0; + const actualBalance = selectedCoinType === "VESTS" && unit === "hp" + ? selectedData?.convertedHive ?? 0 + : selectedData?.balance ?? 0; + const balanceChange = selectedData?.balance_change ?? 0; const savingsBalance = selectedData?.savings_balance ?? undefined; const savingsBalanceChange = selectedData?.savings_balance_change ?? 0; @@ -207,85 +172,99 @@ const BalanceHistoryChart: React.FC = ({

{`${t("common.date")}: ${label}`}

-
+
{isPositiveChange ? ( ) : isZeroChange ? ( - + ) : ( )} {` ${formatNumber(balanceChange, selectedCoinType === "VESTS" ? unit === "vests" : false)}`}
-
{`${t("common.balance")}: ${formatNumber(actualBalance, selectedCoinType === "VESTS" ? unit === "vests" : false)}`}
+
+ {`${t("common.balance")}: ${formatNumber(actualBalance, selectedCoinType === "VESTS" && unit === "vests")}`} +
{dollarValue ? (
Dollar Value: ${formatNumber(dollarValue, false, selectedCoinType === "VESTS")}
) : null}
- - {showSavingsBalance === "yes" && savingsBalance !== undefined && selectedCoinType !== "VESTS" && ( -
-
- {isSavingsPositiveChange ? ( - - ) : isSavingsZeroChange ? ( - - ) : ( - - )} - {` ${formatNumber(savingsBalanceChange, selectedCoinType === "VESTS" ? unit === "vests" : false)}`} -
-
- {`${t("balanceHistoryChart.savingsBalance")}: ${formatNumber(savingsBalance, selectedCoinType === "VESTS" ? unit === "vests" : false)}`} + {showSavingsBalance === "yes" && + savingsBalance !== undefined && + selectedCoinType !== "VESTS" && ( +
+
+ {isSavingsPositiveChange ? ( + + ) : isSavingsZeroChange ? ( + + ) : ( + + )} + {` ${formatNumber( + savingsBalanceChange, + selectedCoinType === "VESTS" ? unit === "vests" : false + )}`} +
+
+ {`${t("balanceHistoryChart.savingsBalance")}: ${formatNumber( + savingsBalance, + selectedCoinType === "VESTS" ? unit === "vests" : false + )}`} +
-
- )} + )}
); }; // ---------------- Coin toggle buttons ---------------- -const renderCoinButtons = () => ( -
- {/* Toggle on the far left */} - {selectedCoinType === "VESTS" && ( -
- - setUnit(checked ? "hp" : "vests")} - /> - + const renderCoinButtons = () => ( +
+ {availableCoins.map((coinType) => ( + + ))}
- )} - - {/* Coin buttons always on the right */} - {availableCoins.map((coinType) => ( - - ))} -
- -); - - + ); // ---------------- Min/Max for Y-axis ---------------- const getMinMax = (data: any[]): [number, number] => { @@ -295,14 +274,21 @@ const renderCoinButtons = () => ( if (selectedCoinType === "VESTS") { allValues = data.map((item) => item.convertedHive || 0); - const dollarValues = data.map((item) => item.dollarValue).filter((v): v is number => typeof v === "number" && !Number.isNaN(v)); + const dollarValues = data + .map((item) => item.dollarValue) + .filter((v): v is number => typeof v === "number" && !Number.isNaN(v)); allValues = allValues.concat(dollarValues); } else { allValues = data.map((item) => item.balance); - const dollarValues = data.map((item) => item.dollarValue).filter((v): v is number => typeof v === "number" && !Number.isNaN(v)); + const dollarValues = data + .map((item) => item.dollarValue) + .filter((v): v is number => typeof v === "number" && !Number.isNaN(v)); allValues = allValues.concat(dollarValues); + if (showSavingsBalance === "yes") { - const savingsValues = data.map((item) => item.savings_balance).filter((v): v is number => typeof v === "number"); + const savingsValues = data + .map((item) => item.savings_balance) + .filter((v): v is number => typeof v === "number"); allValues = allValues.concat(savingsValues); } } @@ -313,7 +299,7 @@ const renderCoinButtons = () => ( const [fullDataMin, fullDataMax] = getMinMax(displayData); const [minValue, maxValue] = zoomedDomain || [fullDataMin, fullDataMax]; - const handleBrushAreaChange = (domain: { startIndex?: number; endIndex?: number; }) => { + const handleBrushAreaChange = (domain: { startIndex?: number; endIndex?: number }) => { if (!domain || domain.startIndex === undefined || domain.endIndex === undefined) { setZoomedDomain([fullDataMin, fullDataMax]); return; @@ -327,27 +313,20 @@ const renderCoinButtons = () => ( if (!displayData || !displayData.length) return null; - const isDualAxis = selectedCoinType === "VESTS"; const primaryAxisId = isRTL ? "right" : "left"; const secondaryAxisId = isRTL ? "left" : "right"; - const leftMargin = isMobile - ? 10 - : selectedCoinType === "VESTS" - ? 50 - : 30; - return ( -
+
{renderCoinButtons()} - + ( return formatNumber(tick, false, false); }} /> - {isDualAxis && ( - `$${Math.round(tick)}`} - /> - )} + + `$${formatNumber(tick, false, false)}`} + /> } /> ( dataKey={selectedCoinType === "VESTS" && unit === "hp" ? "convertedHive" : "balance"} stroke={colorMap[selectedCoinType]} strokeWidth={2} - activeDot={{ r: 6 }} - name={selectedCoinType === "VESTS" && unit === "hp" ? "HP" : selectedCoinType} dot={false} - hide={hiddenDataKeys.includes("balance")} + name={selectedCoinType === "VESTS" && unit === "hp" ? "HP" : selectedCoinType === "VESTS" ? "VESTS" : selectedCoinType} + hide={hiddenDataKeys.includes(selectedCoinType === "VESTS" && unit === "hp" ? "convertedHive" : "balance")} /> {showSavingsBalance === "yes" && selectedCoinType !== "VESTS" && ( @@ -413,9 +389,8 @@ const renderCoinButtons = () => ( dataKey="savings_balance" stroke={colorMap.SAVINGS} strokeWidth={2} - activeDot={{ r: 6 }} - name={t("balanceHistoryChart.savingsBalance")} dot={false} + name="Savings Balance" hide={hiddenDataKeys.includes("savings_balance")} /> )} @@ -427,26 +402,75 @@ const renderCoinButtons = () => ( fill="var(--color-background)" travellerWidth={10} tickFormatter={(value) => moment(value).format("MMM D")} - y={380} + y={250} x={50} className="text-xs" onChange={handleBrushAreaChange} /> )} { - const dataKey = event.dataKey as string; - const isHidden = hiddenDataKeys.includes(dataKey); - if (isHidden) setHiddenDataKeys(hiddenDataKeys.filter((key) => key !== dataKey)); - else setHiddenDataKeys([...hiddenDataKeys, dataKey]); + const dataKey = event.dataKey; + const actualDataKey = + (dataKey === selectedCoinType || dataKey === "HP" || dataKey === "VESTS") ? + (selectedCoinType === "VESTS" && unit === "hp" ? "convertedHive" : "balance") : + dataKey; + + const isHidden = hiddenDataKeys.includes(actualDataKey); + if (isHidden) + setHiddenDataKeys(hiddenDataKeys.filter((key) => key !== actualDataKey)); + else + setHiddenDataKeys([...hiddenDataKeys, actualDataKey]); }} /> + + + {/* Toggle next to legend (if VESTS is selected) */} + {selectedCoinType === "VESTS" && ( +
+ + setUnit(checked ? "hp" : "vests")} + /> + +
+ )}
); }; -export default BalanceHistoryChart; +export default BalanceHistoryChart; \ No newline at end of file