Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 41 additions & 1 deletion src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,48 @@
<div className="w-full sm:w-auto">
<ExportButton />
</div>
</div>

<div className="mt-6">
<CodingActivityInsightsCard />
</div>

<div className="mt-6">
<PRReviewTrendChart />
</div>

<div
id="issues"
className="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-6"
>
<div className="lg:col-span-2">
<IssueMetrics />
</div>
<CIAnalytics />
</div>

{/* Row 3b: Discussion activity */}
<div className="mt-6">
<DiscussionsWidget />
</div>
<div className="mt-6">
<PinnedRepos />

Check failure on line 165 in src/app/dashboard/page.tsx

View workflow job for this annotation

GitHub Actions / Type check

Cannot find name 'PinnedRepos'.
</div>

<div className="mt-6">
<InactiveRepositoriesCard />
</div>
<div
id="top-repos"
className="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-6"
>
<TopRepos />
<LanguageBreakdown />
<GoalTracker />
</div>
<div id="recent-activity" className="mt-6">
<RecentActivity />
</div>
</div>
<div className="space-y-4">
<StreakAtRiskBanner />
</div>
Expand Down
122 changes: 122 additions & 0 deletions src/components/DashboardSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"use client";
import { useEffect, useState } from "react";
import {
Menu,
X,
Flame,
GitPullRequest,
Target,
AlertCircle,
BarChart2,
GitCommit,
CalendarDays,
Bot,
Trophy,
} from "lucide-react";

const sections = [
{ id: "weekly-summary", label: "Weekly Summary", icon: CalendarDays },
{ id: "personal-records", label: "Personal Records", icon: Trophy },
{ id: "contribution", label: "Contributions", icon: GitCommit },
{ id: "pr-analytics", label: "PR Analytics", icon: GitPullRequest },
{ id: "top-repos", label: "Top Repos & Goals", icon: Target },
{ id: "recent-activity", label: "Recent Activity", icon: BarChart2 },
];

export default function DashboardSidebar() {
const [activeId, setActiveId] = useState<string>("");
const [mobileOpen, setMobileOpen] = useState(false);

useEffect(() => {
const observers: IntersectionObserver[] = [];

sections.forEach(({ id }) => {
const el = document.getElementById(id);
if (!el) return;

const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) setActiveId(id);
},
{ threshold: 0.3 }
);

observer.observe(el);
observers.push(observer);
});

return () => observers.forEach((o) => o.disconnect());
}, []);

const handleNavClick = () => {
setMobileOpen(false);
};

return (
<>
<aside className="hidden lg:flex flex-col sticky top-8 ml-5 rounded-xl h-full gap-1 bg-[var(--control)] border-r border-[var(--border)] p-3 min-w-[48px] xl:min-w-[200px]">
<div
className="text-lg flex items-center font-bold text-[var(--foreground)] mb-4 px-3">
Devtrack
</div>
{sections.map(({ id, label, icon: Icon }) => (
<a
key={id}
href={`#${id}`}
title={label}
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm transition-all hover:bg-[var(--background)] ${
activeId === id
? "bg-[var(--background)] text-[var(--foreground)] font-semibold"
: "text-[var(--foreground)] opacity-60"
}`}
>
<Icon size={18} />
<span className="hidden xl:inline">{label}</span>
</a>
))}
</aside>
<button
onClick={() => setMobileOpen(true)}
className="lg:hidden fixed top-4 left-4 z-50 p-2 rounded-lg bg-[var(--control)]/80 backdrop-blur-sm border border-[var(--border)] shadow-md"
>
<Menu size={20} />
</button>
{mobileOpen && (
<div
className="lg:hidden fixed inset-0 z-40 bg-black/40 backdrop-blur-sm"
onClick={() => setMobileOpen(false)}
/>
)}
<div
className={`lg:hidden fixed top-0 left-0 h-full z-50 w-64 bg-[var(--control)]/90 backdrop-blur-md border-r border-[var(--border)] p-4 shadow-xl transition-transform duration-300 ${
mobileOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
<div className="flex justify-between items-center mb-4">
<span className="text-sm font-semibold text-[var(--foreground)]">
Navigate
</span>
<button onClick={() => setMobileOpen(false)}>
<X size={20} />
</button>
</div>

{sections.map(({ id, label, icon: Icon }) => (
<a
key={id}
href={`#${id}`}
onClick={handleNavClick}
className={`flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-all hover:bg-[var(--background)] mb-1 ${
activeId === id
? "bg-[var(--background)] text-[var(--foreground)] font-semibold"
: "text-[var(--foreground)] opacity-60"
}`}
>
<Icon size={18} />
<span>{label}</span>
</a>
))}
</div>
</>
);
}
Loading