diff --git a/app/admin/components/statsCard.tsx b/app/admin/components/statsCard.tsx index 679f8be..2c48176 100644 --- a/app/admin/components/statsCard.tsx +++ b/app/admin/components/statsCard.tsx @@ -26,7 +26,7 @@ export function StatsCards({ data }: StatsCardsProps) { }, { title: 'Total Confirmed Amount', - value: calculatedUsdtBal(), + value: data.totalConfirmedAmount, description: 'Across all transactions', }, { diff --git a/app/stats/page.tsx b/app/stats/page.tsx index a3f363c..4e5ca60 100644 --- a/app/stats/page.tsx +++ b/app/stats/page.tsx @@ -1,9 +1,234 @@ -import React from 'react' -import DashboardPage from '../admin/dashboard/page' +'use client'; + +import { useEffect, useState } from 'react'; +import { StatsCards } from '../admin/components/statsCard'; +import { ChainStats } from '../admin/components/chain-stats'; +import { UsageStats } from '@/app/admin/components/usage-stats'; +import { StatsData } from '@/types/admin'; +import { getStats } from '@/lib/api'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/cards'; +import { Button } from '@/components/ui/buttons'; +import { RefreshCw } from 'lucide-react'; + +// Cache key for localStorage +const STATS_CACHE_KEY = 'admin_dashboard_stats'; +const CACHE_DURATION = 5 * 60 * 1000; + +interface CachedStats { + data: StatsData; + timestamp: number; +} + +export default function DashboardPage() { + const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [isRefreshing, setIsRefreshing] = useState(false); + + // Get cached data + const getCachedStats = (): CachedStats | null => { + if (typeof window === 'undefined') return null; + + try { + const cached = localStorage.getItem(STATS_CACHE_KEY); + if (!cached) return null; + + const parsed: CachedStats = JSON.parse(cached); + const isExpired = Date.now() - parsed.timestamp > CACHE_DURATION; + + return isExpired ? null : parsed; + } catch { + return null; + } + }; + + // Save data to cache + const setCachedStats = (data: StatsData) => { + if (typeof window === 'undefined') return; + + const cachedData: CachedStats = { + data, + timestamp: Date.now() + }; + + localStorage.setItem(STATS_CACHE_KEY, JSON.stringify(cachedData)); + }; + + // Clear cache + const clearCache = () => { + if (typeof window === 'undefined') return; + localStorage.removeItem(STATS_CACHE_KEY); + }; + + const fetchStats = async (backgroundRefresh = false) => { + if (!backgroundRefresh) { + setLoading(true); + } else { + setIsRefreshing(true); + } + + setError(null); + + try { + // Try to use cached data first (only for initial load) + if (!backgroundRefresh) { + const cached = getCachedStats(); + if (cached) { + setStats(cached.data); + setLoading(false); + + // Background refresh if cache is older than 2 minutes + const cacheAge = Date.now() - cached.timestamp; + if (cacheAge > 2 * 60 * 1000) { + fetchStats(true); + } + return; + } + } + + // Fetch fresh data + const data = await getStats(); + setStats(data); + setCachedStats(data); + + } catch (err) { + // Only show error if it's not a background refresh + if (!backgroundRefresh) { + setError(err instanceof Error ? err.message : 'Failed to fetch stats'); + } else { + console.error('Background refresh failed:', err); + } + } finally { + if (!backgroundRefresh) { + setLoading(false); + } else { + setIsRefreshing(false); + } + } + }; + + // Manual refresh handler + const handleManualRefresh = async () => { + clearCache(); + await fetchStats(false); + }; + + useEffect(() => { + fetchStats(false); + + // Set up periodic background refresh every 5 minutes + const interval = setInterval(() => { + fetchStats(true); + }, 5 * 60 * 1000); + + return () => clearInterval(interval); + }, []); + + if (loading) { + return ( +
+
+ +

Loading dashboard...

+
+
+ ); + } + + if (error) { + return ( +
+ + +
+

Error: {error}

+ +
+
+
+
+ ); + } + + if (!stats) { + return ( +
+

No data available

+
+ ); + } -export default function Stats() { return ( -
+
+
+
+

Admin Dashboard

+

+ Overview of platform statistics and usage + {isRefreshing && ( + + + Updating... + + )} +

+
+ +
+ +
+ + +
+ + +
+ + {/* Summary Card */} + + + Platform Summary + + Current overview of platform performance and user activity + {getCachedStats() && ( + + • Cached data (updates every 5 min) + + )} + + + +
+
+

Transaction Summary

+
    +
  • Total transactions across all chains: {stats.perChain.reduce((sum, chain) => sum + chain.count, 0)}
  • +
  • Most active chain: {stats.mostUsedChain}
  • +
  • Total confirmed amount: {stats.totalConfirmedAmount}
  • +
+
+
+

Feature Usage

+
    +
  • Most used feature: Splitting ({stats.usage.splitting} times)
  • +
  • Send operations: {stats.usage.send}
  • +
  • QR Payments: {stats.usage.qrPayment}
  • +
+
+
+
+
+
- ) -} + ); +} \ No newline at end of file diff --git a/components/dashboard/tabs/qr-payment.tsx b/components/dashboard/tabs/qr-payment.tsx index 102d38d..f924ad7 100644 --- a/components/dashboard/tabs/qr-payment.tsx +++ b/components/dashboard/tabs/qr-payment.tsx @@ -289,7 +289,7 @@ export default function QrPayment() { const requestBody: any = { amount: parseFloat(tokenAmount), chain: chain, - network: "testnet", + network: "mainnet", description: description || "QR Payment request", }; diff --git a/components/dashboard/tabs/send-funds.tsx b/components/dashboard/tabs/send-funds.tsx index 322380d..6beb4ea 100644 --- a/components/dashboard/tabs/send-funds.tsx +++ b/components/dashboard/tabs/send-funds.tsx @@ -121,9 +121,9 @@ export default function SendFunds() { }, [addresses, selectedToken]); const currentNetwork = useMemo(() => { - if (!addresses) return "testnet"; + if (!addresses) return "mainnet"; const addressInfo = addresses.find((addr) => addr.chain === selectedToken); - return addressInfo?.network || "testnet"; + return addressInfo?.network || "mainnet"; }, [addresses, selectedToken]); // Check if selected token has a wallet diff --git a/components/hooks/useNotifications.ts b/components/hooks/useNotifications.ts index 1e79a71..de4346a 100644 --- a/components/hooks/useNotifications.ts +++ b/components/hooks/useNotifications.ts @@ -100,6 +100,8 @@ export const useNotifications = () => { const [notifications, setNotifications] = useState([]); const [unreadCount, setUnreadCount] = useState(0); + // console.log("XXXXXXXXXXXXXXXXXXXXXXXXXX",notifications) + // Use useRef to track shown notifications - persists across renders without causing re-renders const shownNotificationIds = useRef>(new Set()); const isInitialMount = useRef(true); diff --git a/components/hooks/useTokenBalance.ts b/components/hooks/useTokenBalance.ts index 688ed70..f02eab1 100644 --- a/components/hooks/useTokenBalance.ts +++ b/components/hooks/useTokenBalance.ts @@ -58,9 +58,9 @@ export function useTokenBalance() { const getWalletNetwork = useCallback( (chain: string): string => { - if (!addresses || !Array.isArray(addresses)) return "testnet"; + if (!addresses || !Array.isArray(addresses)) return "mainnet"; const addressInfo = addresses.find((addr) => addr.chain === chain); - return addressInfo?.network || "testnet"; + return addressInfo?.network || "mainnet"; }, [addresses] ); diff --git a/components/landingpage/landing/navigation.tsx b/components/landingpage/landing/navigation.tsx index 6b3f819..4f6116d 100644 --- a/components/landingpage/landing/navigation.tsx +++ b/components/landingpage/landing/navigation.tsx @@ -194,6 +194,17 @@ export function Navigation() { blog + + + Stats + +
diff --git a/components/modals/add-split.tsx b/components/modals/add-split.tsx index 2dd4be6..5e6c9d4 100644 --- a/components/modals/add-split.tsx +++ b/components/modals/add-split.tsx @@ -35,7 +35,7 @@ export default function AddSplit({ close, onSuccess }: AddSplitProps) { }); const [errors, setErrors] = useState>({}); - const network = "testnet"; + const network = "mainnet"; // CHANGED: Use addresses from useWalletData const availableChains = [...new Set(addresses.map(a => a.chain))]; diff --git a/lib/api-client.ts b/lib/api-client.ts index 2a8ebac..1be85c9 100644 --- a/lib/api-client.ts +++ b/lib/api-client.ts @@ -255,7 +255,7 @@ class ApiClient { // Wallet methods async getWalletAddresses(): Promise { return this.request<{ addresses: WalletAddress[] }>( - "/wallet/addresses/testnet", + "/wallet/addresses/mainnet", { method: "GET" }, { ttl: 10 * 60 * 1000, // 10 minutes - addresses don't change often @@ -266,7 +266,7 @@ class ApiClient { async getWalletBalances(): Promise { return this.request<{ balances: WalletBalance[] }>( - "/wallet/balances/testnet", + "/wallet/balances/mainnet", { method: "GET" }, { ttl: 2 * 60 * 1000, // 2 minutes - balances change more frequently @@ -282,7 +282,7 @@ class ApiClient { }); // Invalidate balance cache after sending transaction - this.cache.invalidateCache(["/wallet/balances/testnet"]); + this.cache.invalidateCache(["/wallet/balances/mainnet"]); return result; } @@ -539,7 +539,7 @@ async executeSplitPayment(id: string, pin: string): Promise { return this.request( - "/checkdeploy/balances/testnet/deploy", + "/checkdeploy/balances/mainnet/deploy", { method: "GET", },