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
9 changes: 1 addition & 8 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,4 @@ UPSTASH_REDIS_REST_TOKEN=your_upstash_redis_rest_token
# AI Mentor widget). Without this key the widget still works and shows
# rule-based insights only.
# console.anthropic.com → API Keys
# ANTHROPIC_API_KEY=sk-ant-...

# -------------------------------------------------------
# Groq API Key (optional — enables AI-generated weekly summaries in the
# AI Mentor widget using Llama-3).
# console.groq.com → API Keys
GROQ_API_KEY=gsk_...

ANTHROPIC_API_KEY=sk-ant-...
27 changes: 27 additions & 0 deletions src/app/dashboard/activity/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import DashboardHeader from "@/components/DashboardHeader";
import ContributionGraph from "@/components/ContributionGraph";
import RecentActivity from "@/components/RecentActivity";
import { authOptions } from "@/lib/auth";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export default async function ActivityPage() {
const session = await getServerSession(authOptions);
if (!session) redirect("/");
if (session.error === "TokenRevoked") redirect("/");

return (
<div className="space-y-6">
<DashboardHeader
title="Activity"
description="Browse your commit logs, event timeline, and daily summaries ⚡"
/>

{/* Annual Contribution Grid */}
<ContributionGraph />

{/* Activity Event Feed */}
<RecentActivity />
</div>
);
}
57 changes: 57 additions & 0 deletions src/app/dashboard/analytics/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import DashboardHeader from "@/components/DashboardHeader";
import StreakTracker from "@/components/StreakTracker";
import PRMetrics from "@/components/PRMetrics";
import PRBreakdownChart from "@/components/PRBreakdownChart";
import CommitTimeChart from "@/components/CommitTimeChart";
import PRReviewTrendChart from "@/components/PRReviewTrendChart";
import CIAnalytics from "@/components/CIAnalytics";
import IssueMetrics from "@/components/IssueMetrics";
import CodingActivityInsightsCard from "@/components/CodingActivityInsightsCard";
import { authOptions } from "@/lib/auth";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export default async function AnalyticsPage() {
const session = await getServerSession(authOptions);
if (!session) redirect("/");
if (session.error === "TokenRevoked") redirect("/");

return (
<div className="space-y-6">
<DashboardHeader
title="Analytics"
description="Deep dive into your pull requests, workflows, and commit schedules 📊"
/>

{/* Row 1: Coding Insights & Commit Streaks */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<CodingActivityInsightsCard />
</div>
<div>
<StreakTracker />
</div>
</div>

{/* Row 2: Pull Request Analytics */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<PRMetrics />
<PRBreakdownChart />
</div>

{/* Row 3: Commit and Review Time Trends */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<CommitTimeChart />
<PRReviewTrendChart />
</div>

{/* Row 4: Issues & Continuous Integration (Actions) */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<IssueMetrics />
</div>
<CIAnalytics />
</div>
</div>
);
}
35 changes: 35 additions & 0 deletions src/app/dashboard/community/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import DashboardHeader from "@/components/DashboardHeader";
import FriendComparison from "@/components/FriendComparison";
import CommunityMetrics from "@/components/CommunityMetrics";
import DiscussionsWidget from "@/components/DiscussionsWidget";
import { authOptions } from "@/lib/auth";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export default async function CommunityPage() {
const session = await getServerSession(authOptions);
if (!session) redirect("/");
if (session.error === "TokenRevoked") redirect("/");

return (
<div className="space-y-6">
<DashboardHeader
title="Community"
description="Share stats, check rankings, and monitor discussions 🌐"
/>

{/* Row 1: Leaderboard/Friends Comparison + Repository Community metrics */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<FriendComparison />
</div>
<div>
<CommunityMetrics />
</div>
</div>

{/* Discussions Widget */}
<DiscussionsWidget />
</div>
);
}
30 changes: 30 additions & 0 deletions src/app/dashboard/goals/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import DashboardHeader from "@/components/DashboardHeader";
import GoalTracker from "@/components/GoalTracker";
import ActivityRingChart from "@/components/ActivityRingChart";
import { authOptions } from "@/lib/auth";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export default async function GoalsPage() {
const session = await getServerSession(authOptions);
if (!session) redirect("/");
if (session.error === "TokenRevoked") redirect("/");

return (
<div className="space-y-6">
<DashboardHeader
title="Goals"
description="Set commit, PR, or time-based milestones and monitor your progress 🎯"
/>

<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<GoalTracker />
</div>
<div>
<ActivityRingChart />
</div>
</div>
</div>
);
}
16 changes: 16 additions & 0 deletions src/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Sidebar from "@/components/Sidebar";

export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex flex-col md:flex-row min-h-screen bg-[var(--background)] text-[var(--foreground)] transition-colors">
<Sidebar />
<main className="flex-1 min-w-0 p-4 md:p-8 md:overflow-y-auto max-h-screen">
{children}
</main>
</div>
);
}
98 changes: 13 additions & 85 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import DiscussionsWidget from "@/components/DiscussionsWidget";
import ActivityRingChart from "@/components/ActivityRingChart";
import ContributionGraph from "@/components/ContributionGraph";
import ContributionHeatmap from "@/components/ContributionHeatmap";
import PRMetrics from "@/components/PRMetrics";
import CommunityMetrics from "@/components/CommunityMetrics";
import PRBreakdownChart from "@/components/PRBreakdownChart";
import GoalTracker from "@/components/GoalTracker";
import DashboardHeader from "@/components/DashboardHeader";
import StreakTracker from "@/components/StreakTracker";
import TopRepos from "@/components/TopRepos";
import PinnedRepos from "@/components/PinnedRepos";
import InactiveRepositoriesCard from "@/components/InactiveRepositoriesCard";
import LanguageBreakdown from "@/components/LanguageBreakdown";
import CommitTimeChart from "@/components/CommitTimeChart";
import CodingActivityInsightsCard from "@/components/CodingActivityInsightsCard";
import PRReviewTrendChart from "@/components/PRReviewTrendChart";
import CIAnalytics from "@/components/CIAnalytics";
import IssueMetrics from "@/components/IssueMetrics";
import StreakAtRiskBanner from "@/components/StreakAtRiskBanner";
import RepoAnalyticsExplorer from "@/components/repo-analytics/RepoAnalyticsExplorer";
import dynamic from "next/dynamic";

const SkeletonCard = () => (
Expand Down Expand Up @@ -89,7 +96,7 @@ import ExportButton from "@/components/ExportButton";
import Link from "next/link";
import PersonalRecords from "@/components/PersonalRecords";
import LocalCodingTime from "@/components/LocalCodingTime";
import CodingTimeWidget from "@/components/CodingTimeWidget";
import CodingTimeCard from "@/components/CodingTimeCard";
import RecentActivity from "@/components/RecentActivity";
import { authOptions } from "@/lib/auth";
import { getServerSession } from "next-auth";
Expand All @@ -99,24 +106,15 @@ import DashboardSSEProvider from "@/components/DashboardSSEProvider";
export default async function DashboardPage() {
const session = await getServerSession(authOptions);
if (!session) redirect("/");
// If the JWT callback detected that the GitHub token has been revoked,
// redirect to the landing page so the user must re-authenticate.
if (session.error === "TokenRevoked") redirect("/");

return (
<DashboardSSEProvider>
<div className="min-h-screen bg-[var(--background)] p-4 text-[var(--foreground)] transition-colors md:p-8">
<DashboardHeader />
<div className="min-h-screen bg-[var(--background)] p-4 md:p-8 text-[var(--foreground)] transition-colors">
<DashboardHeader />
<div className="mb-6 flex justify-end items-center gap-2">
<Link
href="/wrapped"
className="rounded-lg border border-[var(--accent)] bg-[var(--accent-soft)] px-4 py-2 text-sm font-semibold text-[var(--accent)] hover:opacity-90 transition-opacity min-w-[140px] flex items-center justify-center"
>
Year in Code
</Link>
<Link
href="/dashboard/settings"
className="secondary-button flex min-w-[140px] items-center justify-center rounded-xl px-4 py-2 text-sm font-medium"
className="rounded-lg border border-[var(--border)] bg-[var(--control)] px-4 py-2 text-sm text-[var(--foreground)] hover:opacity-90 transition-opacity min-w-[140px] flex items-center justify-center"
>
Settings
</Link>
Expand All @@ -142,19 +140,6 @@ export default async function DashboardPage() {
</Link>
</div>

<div className="mb-6">
<WeeklySummaryCard />
</div>

<div className="mb-6">
<AIMentorWidget />
</div>

<div className="mb-6">
<PersonalRecords />
</div>

{/* Row 1: Contribution graph + Streak + Local Coding Time */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<ContributionGraph />
Expand All @@ -164,73 +149,16 @@ export default async function DashboardPage() {
<div className="mt-6">
<FriendComparison />
</div>
<div className="mt-6">
<RepoAnalyticsExplorer />
</div>
</div>

<div>
<StreakTracker />
<LocalCodingTime />
<div className="mt-6">
<CodingTimeWidget />
<CodingTimeCard />
</div>
</div>
</div>

{/* Row 2: PR metrics, community metrics, PR breakdown & Time Chart */}
<div className="mt-6 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
<PRMetrics />
<CommunityMetrics />
<PRBreakdownChart />
<CommitTimeChart />
</div>
{/* Row 2b: Activity Ring Chart */}
<div className="mt-6">
<ActivityRingChart />
</div>

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

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

{/* Row 3: Issue metrics + CI analytics */}
<div 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>

{/* Row 4: Pinned repositories */}
<div className="mt-6">
<PinnedRepos />
</div>

{/* Row 5: Inactive repository reminder */}
<div className="mt-6">
<InactiveRepositoriesCard />
</div>

{/* Row 6: Top repos + Language breakdown + Goal tracker */}
<div className="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-6">
<TopRepos />
<LanguageBreakdown />
<GoalTracker />
</div>

{/* Row 7: Recent GitHub activity */}
<div className="mt-6">
<RecentActivity />
</div>
</div>
</DashboardSSEProvider>
);
Expand Down
39 changes: 39 additions & 0 deletions src/app/dashboard/productivity/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import DashboardHeader from "@/components/DashboardHeader";
import { AIMentorWidget } from "@/components/AIMentorWidget";
import LocalCodingTime from "@/components/LocalCodingTime";
import PersonalRecords from "@/components/PersonalRecords";
import CodingActivityInsightsCard from "@/components/CodingActivityInsightsCard";
import { authOptions } from "@/lib/auth";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export default async function ProductivityPage() {
const session = await getServerSession(authOptions);
if (!session) redirect("/");
if (session.error === "TokenRevoked") redirect("/");

return (
<div className="space-y-6">
<DashboardHeader
title="Productivity"
description="Optimize coding flows, consult the AI Mentor, and view achievements 🏆"
/>

{/* Row 1: AI mentor + Local Coding Time editor sync */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<AIMentorWidget />
</div>
<div>
<LocalCodingTime />
</div>
</div>

{/* Row 2: Personal records & achievements milestones */}
<PersonalRecords />

{/* Row 3: Coding insights & patterns */}
<CodingActivityInsightsCard />
</div>
);
}
Loading
Loading