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
8 changes: 8 additions & 0 deletions e2e/dashboard-widgets.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,20 @@ test.beforeEach(async ({ page }) => {
unit: "commits",
recurrence: "weekly",
period_start: "2026-05-18",
last_synced_at: new Date().toISOString(),
},
],
}),
});
});

await page.route("**/api/goals/sync", async (route) => {
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({ updated: 1, commitCount: 4 }),
});
});

const metricRoutes = [
"**/api/metrics/prs**",
"**/api/metrics/pr-breakdown**",
Expand Down
5 changes: 3 additions & 2 deletions src/app/auth/signin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { signIn } from "next-auth/react";
export default function SignInPage() {
return (
<main className="min-h-screen flex items-center justify-center bg-[var(--background)] px-4">
<div className="w-full max-w-md rounded-2xl border border-[var(--border)] bg-[var(--card-muted)] backdrop-blur-md p-8 shadow-2xl text-center">
<div className="relative w-full max-w-md overflow-hidden rounded-3xl border border-[var(--border)] bg-[var(--card)] p-8 text-center shadow-[var(--shadow-medium)]">
<div className="pointer-events-none absolute -right-12 -top-12 h-36 w-36 rounded-full bg-[var(--accent)]/20 blur-2xl" />

<h1 className="text-4xl font-bold text-[var(--foreground)] mb-3">
DevTrack
Expand All @@ -17,7 +18,7 @@ export default function SignInPage() {

<button
onClick={() => signIn("github", { callbackUrl: "/dashboard" })}
className="w-full inline-flex items-center justify-center gap-3 bg-[var(--background)] text-[var(--foreground)] font-semibold py-3 rounded-xl hover:opacity-90 transition-all duration-200 hover:scale-[1.02]"
className="primary-button relative w-full inline-flex items-center justify-center gap-3 rounded-xl py-3 font-semibold"
>
Sign in with GitHub
</button>
Expand Down
4 changes: 2 additions & 2 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ export default async function DashboardPage() {
if (session.error === "TokenRevoked") redirect("/");

return (
<div className="min-h-screen bg-[var(--background)] p-4 md:p-8 text-[var(--foreground)] transition-colors">
<div className="min-h-screen bg-[var(--background)] p-4 text-[var(--foreground)] transition-colors md:p-8">
<DashboardHeader />
<div className="mb-6 flex justify-end items-center gap-2">
<Link
href="/dashboard/settings"
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"
className="secondary-button flex min-w-[140px] items-center justify-center rounded-xl px-4 py-2 text-sm font-medium"
>
Settings
</Link>
Expand Down
83 changes: 61 additions & 22 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,98 @@

:root {
color-scheme: light;
--background: #f8fafc;
--foreground: #0f172a;
--muted-foreground: #475569;
--card: #ffffff;
--card-foreground: #0f172a;
--card-muted: #e2e8f0;
--border: #cbd5e1;
--accent: #6366f1;
--background: #ffffff;
--foreground: #111827;
--muted-foreground: #4b5563;
--card: #f8fafc;
--card-foreground: #111827;
--card-muted: #eff6ff;
--border: #e5e7eb;
--accent: #3b82f6;
--accent-secondary: #60a5fa;
--success: #10b981;
--warning: #f59e0b;
--destructive: #ef4444;
--accent-soft: rgba(99, 102, 241, 0.15);
--accent-soft: rgba(59, 130, 246, 0.14);
--accent-foreground: #ffffff;
--control: #e2e8f0;
--control-hover: #cbd5e1;
--control: #f1f5f9;
--control-hover: #e2e8f0;
--tooltip: #ffffff;
--tooltip-foreground: #0f172a;
--destructive: #ef4444;
--tooltip-foreground: #111827;
--destructive-muted: rgba(239, 68, 68, 0.1);
--destructive-muted-border: rgba(239, 68, 68, 0.3);
--destructive-foreground: #ffffff;
--shadow-soft: 0 12px 30px -20px rgba(37, 99, 235, 0.35);
--shadow-medium: 0 18px 35px -24px rgba(37, 99, 235, 0.4);
}

.dark {
color-scheme: dark;
--background: #0f172a;
--foreground: #f8fafc;
--muted-foreground: #94a3b8;
--card: #1e293b;
--card: #1a2538;
--card-foreground: #f8fafc;
--card-muted: #334155;
--card-muted: #223248;
--border: #334155;
--accent: #818cf8;
--accent: #60a5fa;
--accent-secondary: #3b82f6;
--success: #10b981;
--warning: #fbbf24;
--destructive: #f87171;
--accent-soft: rgba(99, 102, 241, 0.2);
--accent-soft: rgba(96, 165, 250, 0.2);
--accent-foreground: #ffffff;
--control: #334155;
--control-hover: #475569;
--tooltip: #1e293b;
--control: #2a3b53;
--control-hover: #344a67;
--tooltip: #1a2538;
--tooltip-foreground: #f8fafc;
--destructive: #f87171;
--destructive-muted: rgba(248, 113, 113, 0.1);
--destructive-muted-border: rgba(248, 113, 113, 0.3);
--destructive-foreground: #ffffff;
--shadow-soft: 0 16px 34px -24px rgba(2, 6, 23, 0.8);
--shadow-medium: 0 24px 45px -28px rgba(2, 6, 23, 0.85);
}

body {
background: var(--background);
background:
radial-gradient(circle at 0% 0%, rgba(96, 165, 250, 0.16), transparent 36%),
radial-gradient(circle at 100% 18%, rgba(59, 130, 246, 0.1), transparent 32%),
var(--background);
color: var(--foreground);
transition: background-color 200ms ease, color 200ms ease;
}

.surface-card {
border: 1px solid var(--border);
background: linear-gradient(180deg, color-mix(in srgb, var(--card) 92%, #ffffff 8%), var(--card));
box-shadow: var(--shadow-soft);
}

.primary-button {
border: 1px solid color-mix(in srgb, var(--accent) 86%, white 14%);
background: linear-gradient(140deg, var(--accent), var(--accent-secondary));
color: var(--accent-foreground);
transition: transform 180ms ease, box-shadow 180ms ease, filter 180ms ease;
}

.primary-button:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-medium);
filter: saturate(1.05);
}

.secondary-button {
border: 1px solid var(--border);
background: color-mix(in srgb, var(--card) 88%, white 12%);
color: var(--card-foreground);
transition: transform 180ms ease, border-color 180ms ease, background-color 180ms ease;
}

.secondary-button:hover {
transform: translateY(-1px);
border-color: color-mix(in srgb, var(--accent) 50%, var(--border) 50%);
background: color-mix(in srgb, var(--control) 90%, white 10%);
}
/* Custom slim scrollbar for dashboard widgets */
.scrollbar-thin {
scrollbar-width: thin;
Expand Down
26 changes: 18 additions & 8 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Providers from "./providers";
import PWARegister from "@/components/pwa-register";
import "./globals.css";
import { Toaster } from "sonner";
import { Analytics } from "@vercel/analytics/next";
import { SpeedInsights } from "@vercel/speed-insights/next";
// Load Vercel integrations dynamically so build doesn't fail when packages
// aren't installed in CI/environments where they're optional.

const inter = Inter({ subsets: ["latin"] });

Expand Down Expand Up @@ -37,11 +37,23 @@ export const viewport: Viewport = {
],
};

export default function RootLayout({
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
let Analytics: any = null;
let SpeedInsights: any = null;

try {
const a = await import("@vercel/analytics/next");
Analytics = a?.Analytics ?? a?.default ?? null;
} catch (e) {}

try {
const s = await import("@vercel/speed-insights/next");
SpeedInsights = s?.SpeedInsights ?? s?.default ?? null;
} catch (e) {}
return (
<html lang="en" suppressHydrationWarning>
<head>
Expand All @@ -51,10 +63,8 @@ export default function RootLayout({
(function() {
try {
const stored = localStorage.getItem('theme');
const supportDarkMode =
window.matchMedia('(prefers-color-scheme: dark)').matches === true;

if (stored === 'dark' || (!stored && supportDarkMode)) {
if (stored === 'dark') {
document.documentElement.classList.add('dark');
document.documentElement.style.colorScheme = 'dark';
} else {
Expand Down Expand Up @@ -82,8 +92,8 @@ export default function RootLayout({

<Toaster richColors position="top-right" />
</div>
<Analytics />
<SpeedInsights />
{Analytics ? <Analytics /> : null}
{SpeedInsights ? <SpeedInsights /> : null}
</body>
</html>
);
Expand Down
8 changes: 4 additions & 4 deletions src/app/leaderboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default async function LeaderboardPage({
)}
</div>

<div className="mb-6 flex flex-wrap gap-2">
<div className="mb-6 flex flex-wrap gap-2 rounded-2xl border border-[var(--border)] bg-[var(--card)]/90 p-2 shadow-[var(--shadow-soft)]">
{tabs.map((tab) => {
const active = tab.id === activeTab;
return (
Expand All @@ -101,7 +101,7 @@ export default async function LeaderboardPage({
href={`/leaderboard?tab=${tab.id}`}
className={`rounded-lg border px-4 py-2 text-sm font-semibold transition-colors ${
active
? "border-[var(--accent)] bg-[var(--accent)] text-[var(--accent-foreground)]"
? "border-[var(--accent)] bg-[var(--accent)] text-[var(--accent-foreground)] shadow-sm"
: "border-[var(--border)] bg-[var(--card)] text-[var(--card-foreground)] hover:bg-[var(--control)]"
}`}
>
Expand All @@ -111,7 +111,7 @@ export default async function LeaderboardPage({
})}
</div>

<section className="overflow-hidden rounded-xl border border-[var(--border)] bg-[var(--card)] shadow-sm">
<section className="overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--card)] shadow-[var(--shadow-soft)]">
<div className="grid grid-cols-[72px_1fr_110px_110px] border-b border-[var(--border)] bg-[var(--control)] px-4 py-3 text-xs font-semibold uppercase tracking-wide text-[var(--muted-foreground)] md:grid-cols-[80px_1fr_140px_140px_120px]">
<div>Rank</div>
<div>Contributor</div>
Expand Down Expand Up @@ -168,7 +168,7 @@ export default async function LeaderboardPage({
<div>
<Link
href={entry.profileUrl}
className="inline-flex rounded-lg border border-[var(--border)] px-3 py-2 text-sm font-medium text-[var(--card-foreground)] hover:bg-[var(--control)]"
className="secondary-button inline-flex rounded-lg px-3 py-2 text-sm font-medium"
>
View
</Link>
Expand Down
Loading
Loading