1+ import LazyWidget from "@/components/LazyWidget" ;
2+ import DiscussionsWidget from "@/components/DiscussionsWidget" ;
3+ import CommunityMetrics from "@/components/CommunityMetrics" ;
4+ import GoalTracker from "@/components/GoalTracker" ;
15import TodayFocusHero from "@/components/TodayFocusHero" ;
26import DashboardHeader from "@/components/DashboardHeader" ;
37import ExportButton from "@/components/ExportButton" ;
48import Link from "next/link" ;
9+ import PersonalRecords from "@/components/PersonalRecords" ;
10+ import LocalCodingTime from "@/components/LocalCodingTime" ;
11+ import CodingTimeWidget from "@/components/CodingTimeWidget" ;
12+ import RecentActivity from "@/components/RecentActivity" ;
13+ import FriendComparison from "@/components/FriendComparison" ;
514import { ChevronRight } from "lucide-react" ;
615import { authOptions } from "@/lib/auth" ;
716import { getServerSession } from "next-auth" ;
817import { redirect } from "next/navigation" ;
918import DashboardSSEProvider from "@/components/DashboardSSEProvider" ;
19+
20+ const SkeletonCard = ( ) => (
21+ < div
22+ role = "status"
23+ aria-busy = "true"
24+ aria-live = "polite"
25+ className = "rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm"
26+ >
27+ < div className = "h-6 w-48 bg-[var(--card-muted)] rounded mb-4 animate-pulse" />
28+ < div className = "h-40 bg-[var(--card-muted)] rounded animate-pulse" />
29+ </ div >
30+ ) ;
31+
32+ const ContributionGraphSkeleton = ( ) => (
33+ < div className = "rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm" >
34+ < h2 className = "text-lg font-semibold text-[var(--foreground)]" > Your Commits</ h2 >
35+ < div className = "mt-3 h-40 rounded bg-[var(--card-muted)] animate-pulse" />
36+ </ div >
37+ ) ;
38+
39+ const PRMetricsSkeleton = ( ) => (
40+ < div className = "rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm" >
41+ < h2 className = "text-lg font-semibold text-[var(--card-foreground)]" > PR Analytics</ h2 >
42+ < div className = "mt-3 h-40 rounded bg-[var(--card-muted)] animate-pulse" />
43+ </ div >
44+ ) ;
45+
46+ const CodingActivityInsightsCard = dynamic (
47+ ( ) => import ( "@/components/CodingActivityInsightsCard" ) ,
48+ { ssr : false , loading : ( ) => < SkeletonCard /> } ,
49+ ) ;
50+
51+ const ActivityRingChart = dynamic (
52+ ( ) => import ( "@/components/ActivityRingChart" ) ,
53+ { ssr : false , loading : ( ) => < SkeletonCard /> } ,
54+ ) ;
55+
56+ const ContributionGraph = dynamic (
57+ ( ) => import ( "@/components/ContributionGraph" ) ,
58+ { ssr : false , loading : ( ) => < ContributionGraphSkeleton /> } ,
59+ ) ;
60+
61+ const ContributionHeatmap = dynamic (
62+ ( ) => import ( "@/components/ContributionHeatmap" ) ,
63+ { ssr : false , loading : ( ) => < SkeletonCard /> } ,
64+ ) ;
65+
66+ const PRMetrics = dynamic ( ( ) => import ( "@/components/PRMetrics" ) , {
67+ ssr : false ,
68+ loading : ( ) => < PRMetricsSkeleton /> ,
69+ } ) ;
70+
71+ const PRBreakdownChart = dynamic (
72+ ( ) => import ( "@/components/PRBreakdownChart" ) ,
73+ { ssr : false , loading : ( ) => < SkeletonCard /> } ,
74+ ) ;
75+
76+ const CommitTimeChart = dynamic (
77+ ( ) => import ( "@/components/CommitTimeChart" ) ,
78+ { ssr : false , loading : ( ) => < SkeletonCard /> } ,
79+ ) ;
80+
81+ const PRReviewTrendChart = dynamic (
82+ ( ) => import ( "@/components/PRReviewTrendChart" ) ,
83+ { ssr : false , loading : ( ) => < SkeletonCard /> } ,
84+ ) ;
1085import StreakAtRiskBanner from "@/components/StreakAtRiskBanner" ;
1186import ThrottleBanner from "@/components/ThrottleBanner" ;
1287import CustomizableDashboard from "@/components/dashboard/CustomizableDashboard" ;
@@ -22,25 +97,29 @@ export default async function DashboardPage() {
2297 < DashboardHeader />
2398
2499 { /* Quick actions */ }
25- < div className = "mt-10 mb-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4" >
26- { /* Left side actions */ }
27- < div className = "flex flex-wrap items-center gap-3 w-full sm:w-auto" >
28- < Link
29- href = "/wrapped"
30- className = "inline-flex w-full sm:w-auto justify-center items-center gap-2 rounded-lg border border-[var(--accent)] bg-[var(--accent)]/10 px-5 py-2.5 text-sm font-semibold text-[var(--accent)] shadow-sm shadow-[var(--accent)]/20 transition-all hover:bg-[var(--accent)]/20 hover:shadow-md hover:scale-[1.02] active:scale-95"
31- >
32- Year in Code
33- </ Link >
34-
35- < Link
36- href = "/dashboard/settings"
37- className = "inline-flex w-full sm:w-auto justify-center items-center gap-2 rounded-lg border border-[var(--border)] bg-[var(--card)]/60 px-5 py-2.5 text-sm font-medium transition-all hover:bg-[var(--card)]/80 hover:shadow-sm hover:scale-[1.02] active:scale-95"
38- >
39- Settings
40- </ Link >
41- </ div >
42-
43- < div className = "w-full sm:w-auto" >
100+ < div className = "mt-4 flex flex-wrap items-center gap-2 sm:gap-3" >
101+ < Link
102+ href = "/wrapped"
103+ className = "inline-flex items-center gap-2 rounded-lg border border-[var(--accent)] bg-[var(--accent-soft)] px-4 py-2 text-sm font-semibold text-[var(--accent)] transition-opacity hover:opacity-90"
104+ >
105+ ✨ Year in Code
106+ </ Link >
107+ < Link
108+ href = "/friend-compare"
109+ className = "inline-flex items-center gap-2 rounded-lg border border-[var(--accent)] bg-[var(--accent-soft)] px-4 py-2 text-sm font-semibold text-[var(--accent)] transition-opacity hover:opacity-90"
110+ >
111+ < svg className = "w-4 h-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
112+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z" />
113+ </ svg >
114+ Compare Friends
115+ </ Link >
116+ < Link
117+ href = "/dashboard/settings"
118+ className = "secondary-button inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium"
119+ >
120+ Settings
121+ </ Link >
122+ < div className = "sm:ml-auto" >
44123 < ExportButton />
45124 </ div >
46125 </ div >
@@ -73,12 +152,100 @@ export default async function DashboardPage() {
73152 Generate an ATS-Friendly CV Backed by Your Real Code
74153 </ h3 >
75154
76- < p className = "text-sm text-[var(--muted-foreground)] leading-relaxed" >
77- Analyze your GitHub contributions, merged PRs, and lines of code
78- changed to automatically generate professional bullet points for
79- your target roles.
80- </ p >
155+ { /* Right: streak + coding time */ }
156+ < div className = "flex flex-col gap-6" >
157+ < StreakTracker />
158+ < LocalCodingTime />
159+ < CodingTimeWidget />
160+ </ div >
161+
162+ { /* Repo analytics explorer — full width */ }
163+ < div className = "mt-6" >
164+ < LazyWidget fallback = { < SkeletonCard /> } >
165+ < RepoAnalyticsExplorer />
166+ </ LazyWidget >
167+ </ div >
168+
169+ { /* -- Row 2: PR metrics + Community metrics -- */ }
170+ < div id = "pull-requests" className = "mt-6 grid grid-cols-1 gap-6 scroll-mt-24 md:grid-cols-2" >
171+ < PRMetrics />
172+ < CommunityMetrics />
173+ </ div >
174+
175+ { /* PR breakdown + commit time — 2-col so charts have room */ }
176+ < div className = "mt-6 grid grid-cols-1 md:grid-cols-2 gap-6" >
177+ < LazyWidget fallback = { < SkeletonCard /> } >
178+ < PRBreakdownChart />
179+ </ LazyWidget >
180+ < LazyWidget fallback = { < SkeletonCard /> } >
181+ < CommitTimeChart />
182+ </ LazyWidget >
183+ </ div >
184+
185+ { /* Activity ring — full width */ }
186+ < div className = "mt-6" >
187+ < LazyWidget fallback = { < SkeletonCard /> } >
188+ < ActivityRingChart />
189+ </ LazyWidget >
190+ </ div >
191+
192+ { /* Coding activity insights — full width */ }
193+ < div className = "mt-6" >
194+ < LazyWidget fallback = { < SkeletonCard /> } >
195+ < CodingActivityInsightsCard />
196+ </ LazyWidget >
197+ </ div >
198+
199+ { /* PR review trend — full width */ }
200+ < div className = "mt-6" >
201+ < LazyWidget fallback = { < SkeletonCard /> } >
202+ < PRReviewTrendChart />
203+ </ LazyWidget >
204+ </ div >
205+
206+ { /* -- Row 3: Issues (2/3) + CI analytics (1/3) -- */ }
207+ < div id = "goals" className = "mt-6 grid grid-cols-1 gap-6 scroll-mt-24 lg:grid-cols-3" >
208+ < div className = "lg:col-span-2" >
209+ < LazyWidget fallback = { < SkeletonCard /> } >
210+ < RepoAnalyticsExplorer />
211+ </ LazyWidget >
212+ </ div >
213+
214+ < div className = "grid grid-cols-1 lg:grid-cols-2 gap-6 w-full" >
215+ < div className = "flex flex-col gap-6 w-full overflow-hidden" >
216+ < PRMetrics />
217+ < LazyWidget fallback = { < SkeletonCard /> } >
218+ < PRBreakdownChart />
219+ </ LazyWidget >
220+ < LazyWidget fallback = { < SkeletonCard /> } >
221+ < PRReviewTrendChart />
222+ </ LazyWidget >
223+ < LazyWidget fallback = { < SkeletonCard /> } >
224+ < DiscussionsWidget />
225+ </ LazyWidget >
226+ </ div >
227+ < div className = "flex flex-col gap-6 w-full overflow-hidden" >
228+ < CommunityMetrics />
229+ < LazyWidget fallback = { < SkeletonCard /> } >
230+ < PinnedReposWidget />
231+ </ LazyWidget >
232+ < LazyWidget fallback = { < SkeletonCard /> } >
233+ < TopRepos />
234+ </ LazyWidget >
235+ < LazyWidget fallback = { < SkeletonCard /> } >
236+ < InactiveRepositoriesCard />
237+ </ LazyWidget >
81238 </ div >
239+ </ div >
240+ </ div >
241+ </ section >
242+
243+ { /* 4. GOALS & INSIGHTS */ }
244+ < section id = "goals" className = "mt-14 space-y-6 scroll-mt-28 mb-12" >
245+ < div className = "flex items-center gap-3 border-b border-white/10 pb-4" >
246+ < div className = "h-8 w-1.5 rounded-full bg-purple-500 shadow-[0_0_15px_rgba(168,85,247,0.5)]" > </ div >
247+ < h2 className = "text-2xl font-bold tracking-tight" > Goals & Insights </ h2 >
248+ </ div >
82249
83250 < Link
84251 href = "/dashboard/career-intelligence"
0 commit comments