11'use client' ;
22
3- import { Suspense , useState } from 'react' ;
3+ import { Suspense , lazy , useState } from 'react' ;
44import { useTranslation } from 'react-i18next' ;
55import { DashboardLayout } from '@/components/layouts/DashboardLayout' ;
66import { TransactionHistorySection } from '@/components/TransactionHistorySection' ;
@@ -10,12 +10,23 @@ import { SendModal } from '@/components/SendModal';
1010import { PortfolioAssets } from '@/components/PortfolioAssets' ;
1111import { VaultTable } from '@/components/vaults/VaultTable' ;
1212import { PluginGrid } from '@/components/dashboard/PluginGrid' ;
13-
1413import { SwapCard } from '@/components/swap/SwapCard' ;
15-
1614import { ErrorBoundary } from '@/components/ErrorBoundary' ;
1715import { ProtocolStatsBar } from '@/components/dashboard/ProtocolStatsBar' ;
1816import { RecentSwaps } from '@/components/dashboard/RecentSwaps' ;
17+ import { SkeletonLoader } from '@/components/SkeletonLoader' ;
18+
19+ // Lazy-load the chart component so recharts and its dependencies are
20+ // only fetched when the chart section is rendered on the dashboard.
21+ // webpack extracts the charting vendor code into a separate async chunk
22+ // via the 'charts' cacheGroup in next.config.mjs (issue #89).
23+ const PriceChart = lazy ( ( ) => import ( '@/components/Charts/PriceChart' ) ) ;
24+
25+ // Sample price data for demonstration — replace with API data in production.
26+ const samplePriceData = Array . from ( { length : 24 } , ( _ , i ) => ( {
27+ time : `${ String ( i ) . padStart ( 2 , '0' ) } :00` ,
28+ price : 1.0 + Math . sin ( i / 4 ) * 0.05 + ( Math . random ( ) - 0.5 ) * 0.02
29+ } ) ) ;
1930
2031export default function DashboardPage ( ) {
2132 const { t } = useTranslation ( ) ;
@@ -36,10 +47,23 @@ export default function DashboardPage() {
3647 </ ErrorBoundary >
3748 </ section >
3849
39- < section id = "staking" className = "card" >
40- < h2 className = "text-sm font-semibold text-gray-900" > Staking </ h2 >
50+ { /* Price chart section — lazy-loaded, chunked separately (issue #89) */ }
51+ < section id = "price-chart" className = "card lg:col-span-2" >
4152 < ErrorBoundary >
42- < p className = "mt-2 text-sm text-gray-600" > No active stakes yet.</ p >
53+ < Suspense fallback = {
54+ < div className = "h-72 flex items-center justify-center" >
55+ < div className = "w-full max-w-2xl space-y-3" >
56+ < SkeletonLoader height = "1rem" width = "40%" />
57+ < SkeletonLoader height = "220px" />
58+ </ div >
59+ </ div >
60+ } >
61+ < PriceChart data = { samplePriceData } title = { t ( 'dashboard.priceChart' ) } />
62+ </ Suspense >
63+ </ ErrorBoundary >
64+ </ section >
65+
66+ < section id = "staking" className = "card" >
4367 < h2 className = "text-sm font-semibold text-gray-900" > { t ( 'dashboard.staking' ) } </ h2 >
4468 < ErrorBoundary >
4569 < p className = "mt-2 text-sm text-gray-600" > { t ( 'dashboard.noStakes' ) } </ p >
@@ -74,23 +98,17 @@ export default function DashboardPage() {
7498 < CachedActivity />
7599 </ ErrorBoundary >
76100
77- { /* useSearchParams (page state, #15) requires a Suspense boundary. */ }
78101 < Suspense fallback = { null } >
79102 < ErrorBoundary >
80103 < TransactionHistorySection />
81104 </ ErrorBoundary >
82105 </ Suspense >
83106
84- { /*
85- * Community plugin widgets — renders nothing when no plugins are
86- * registered, so existing layout is unaffected by default.
87- * Plugins register themselves via usePluginSDK().registerPlugin().
88- */ }
89107 < ErrorBoundary >
90108 < PluginGrid />
91109 </ ErrorBoundary >
92110 </ div >
93111 < SendModal isOpen = { sendOpen } onClose = { ( ) => setSendOpen ( false ) } />
94112 </ DashboardLayout >
95113 ) ;
96- }
114+ }
0 commit comments