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
70 changes: 3 additions & 67 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
import ContributionGraph from "@/components/ContributionGraph";
import ContributionHeatmap from "@/components/ContributionHeatmap";
import PRMetrics from "@/components/PRMetrics";
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 LanguageBreakdown from "@/components/LanguageBreakdown";
import CommitTimeChart from "@/components/CommitTimeChart";
import IssueMetrics from "@/components/IssueMetrics";
import StreakAtRiskBanner from "@/components/StreakAtRiskBanner";
import FriendComparison from "@/components/FriendComparison";
import WeeklySummaryCard from "@/components/WeeklySummaryCard";
import ExportButton from "@/components/ExportButton";
import PersonalRecords from "@/components/PersonalRecords";
import DashboardWidgets from "@/components/DashboardWidgets";

import { authOptions } from "@/lib/auth";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
Expand All @@ -28,56 +13,7 @@ export default async function DashboardPage() {

return (
<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">
<ExportButton />
</div>
<StreakAtRiskBanner />

<WeeklySummaryCard />

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

{/* Row 1: Contribution graph + Streak + Friend Comparison */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<ContributionGraph />
<div className="mt-6">
<ContributionHeatmap />
</div>
</div>

<div className="flex flex-col gap-6">
<StreakTracker />
<FriendComparison />
</div>
</div>

{/* Row 2: PR metrics, PR breakdown & Time Chart */}
<div className="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-6">
<PRMetrics />
<PRBreakdownChart />
<CommitTimeChart />
</div>

{/* Row 3: Issue metrics */}
<div className="mt-6">
<IssueMetrics />
</div>

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

{/* Row 5: Top repos + Language breakdown + Goal tracker */}
<div className="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-6">
<TopRepos />
<LanguageBreakdown />
<GoalTracker />
</div>
<DashboardWidgets />
</div>
);
}
114 changes: 114 additions & 0 deletions src/components/DashboardWidgets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"use client";

import ContributionGraph from "@/components/ContributionGraph";
import ContributionHeatmap from "@/components/ContributionHeatmap";
import PRMetrics from "@/components/PRMetrics";
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 LanguageBreakdown from "@/components/LanguageBreakdown";
import CommitTimeChart from "@/components/CommitTimeChart";
import IssueMetrics from "@/components/IssueMetrics";
import StreakAtRiskBanner from "@/components/StreakAtRiskBanner";
import FriendComparison from "@/components/FriendComparison";
import WeeklySummaryCard from "@/components/WeeklySummaryCard";
import ExportButton from "@/components/ExportButton";
import PersonalRecords from "@/components/PersonalRecords";
import WidgetErrorBoundary from "@/components/WidgetErrorBoundary";

export default function DashboardWidgets() {
return (
<>
<WidgetErrorBoundary>
<DashboardHeader />
</WidgetErrorBoundary>

<div className="mb-6 flex justify-end">
<WidgetErrorBoundary>
<ExportButton />
</WidgetErrorBoundary>
</div>

<WidgetErrorBoundary>
<StreakAtRiskBanner />
</WidgetErrorBoundary>

<WidgetErrorBoundary>
<WeeklySummaryCard />
</WidgetErrorBoundary>

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

<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<WidgetErrorBoundary>
<ContributionGraph />
</WidgetErrorBoundary>

<div className="mt-6">
<WidgetErrorBoundary>
<ContributionHeatmap />
</WidgetErrorBoundary>
</div>
</div>

<div className="flex flex-col gap-6">
<WidgetErrorBoundary>
<StreakTracker />
</WidgetErrorBoundary>

<WidgetErrorBoundary>
<FriendComparison />
</WidgetErrorBoundary>
</div>
</div>

<div className="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-6">
<WidgetErrorBoundary>
<PRMetrics />
</WidgetErrorBoundary>

<WidgetErrorBoundary>
<PRBreakdownChart />
</WidgetErrorBoundary>

<WidgetErrorBoundary>
<CommitTimeChart />
</WidgetErrorBoundary>
</div>

<div className="mt-6">
<WidgetErrorBoundary>
<IssueMetrics />
</WidgetErrorBoundary>
</div>

<div className="mt-6">
<WidgetErrorBoundary>
<PinnedRepos />
</WidgetErrorBoundary>
</div>

<div className="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-6">
<WidgetErrorBoundary>
<TopRepos />
</WidgetErrorBoundary>

<WidgetErrorBoundary>
<LanguageBreakdown />
</WidgetErrorBoundary>

<WidgetErrorBoundary>
<GoalTracker />
</WidgetErrorBoundary>
</div>
</>
);
}
1 change: 1 addition & 0 deletions src/components/TopRepos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export default function TopRepos() {
}, [fetchRepos, fetchHealthScores, selectedAccount]);

const maxCommits = repos[0]?.commits ?? 1;
// throw new Error("Test widget crash");

return (
<div className="rounded-xl border border-[var(--border)] bg-[var(--card)] p-6 shadow-sm">
Expand Down
64 changes: 64 additions & 0 deletions src/components/WidgetErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use client";

import React, { Component, ReactNode } from "react";

interface Props {
children: ReactNode;
}

interface State {
hasError: boolean;
}

class WidgetErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

this.state = {
hasError: false,
};
}

static getDerivedStateFromError(_: Error): State {
return {
hasError: true,
};
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error("Widget crashed:", error, errorInfo);
}

handleRetry = () => {
this.setState({
hasError: false,
});
};

render() {
if (this.state.hasError) {
return (
<div className="rounded-xl border border-red-500/20 bg-red-500/10 p-4 text-center">
<h2 className="mb-2 text-lg font-semibold">
Something went wrong
</h2>

<p className="mb-4 text-sm text-gray-400">
This widget failed to load.
</p>

<button
onClick={this.handleRetry}
className="rounded-lg bg-white px-4 py-2 text-black transition hover:opacity-80"
>
Retry
</button>
</div>
);
}

return this.props.children;
}
}

export default WidgetErrorBoundary;
Loading