Skip to content
Merged
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
21 changes: 8 additions & 13 deletions app/(user)/dashboard/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import Dashboard from "@/app/components/admin/Dashbord";
import { createClient } from "@/app/lib/supabase/server";
import { getUserWithProfile } from "@/app/lib/supabase/help/user";
import { Metadata } from "next";
import { redirect } from "next/navigation";

export default async function AdminPage() {
const supabase = await createClient();
export const metadata: Metadata = {
title: "Admin Panel - DevPulse",
};

const {
data: { user },
} = await supabase.auth.getUser();
export default async function AdminPage() {
const { user, profile } = await getUserWithProfile();

if (!user) {
redirect("/login");
redirect("/login?from=/dashboard/admin");
}

const { data: profile } = await supabase
.from("profiles")
.select("role")
.eq("id", user.id)
.single();

if (!profile || profile.role !== "admin") {
redirect("/dashbord");
}
Expand Down
16 changes: 9 additions & 7 deletions app/(user)/dashboard/chat/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import Chat from "@/app/components/Chat";
import { createClient } from "@/app/lib/supabase/server";
import { getUserWithProfile } from "@/app/lib/supabase/help/user";
import { Metadata } from "next";
import { redirect } from "next/navigation";

export default async function ChatPage() {
const supabase = await createClient();
export const metadata: Metadata = {
title: "Chat - DevPulse",
};

const {
data: { user },
} = await supabase.auth.getUser();
export default async function ChatPage() {
const { user } = await getUserWithProfile();

if (!user) return null;
if (!user) return redirect("/login?from=/dashboard/chat");

return <Chat user={user} />;
}
16 changes: 9 additions & 7 deletions app/(user)/dashboard/flex/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import Flex from "@/app/components/Flex";
import { createClient } from "@/app/lib/supabase/server";
import { getUserWithProfile } from "@/app/lib/supabase/help/user";
import { Metadata } from "next";
import { redirect } from "next/navigation";

export default async function FlexPage() {
const supabase = await createClient();
export const metadata: Metadata = {
title: "Flexes - DevPulse",
};

const {
data: { user },
} = await supabase.auth.getUser();
export default async function FlexPage() {
const { user } = await getUserWithProfile();

if (!user) return null;
if (!user) return redirect("/login?from=/dashboard/flex");

return <Flex user={user} />;
}
21 changes: 3 additions & 18 deletions app/(user)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,20 @@
import { redirect } from "next/navigation";
import { createClient } from "../../lib/supabase/server";
import DashboardLayout from "@/app/components/dashboard/Navbar";
import { getUserWithProfile } from "@/app/lib/supabase/help/user";

export default async function Layout({
children,
}: {
children: React.ReactNode;
}) {
const supabase = await createClient();

const {
data: { user },
} = await supabase.auth.getUser();

const { user, profile } = await getUserWithProfile();
if (!user) redirect("/login");

const { data: profile } = await supabase
.from("profiles")
.select("wakatime_api_key, email, role")
.eq("id", user.id)
.single();

if (!profile?.wakatime_api_key) {
return <>{children}</>;
}

const email = profile?.email || user.email!;
const name = user?.user_metadata?.name || email.split("@")[0];

return (
<DashboardLayout email={email} name={name} role={profile.role}>
<DashboardLayout email={email} name={name} role={profile?.role || "user"}>
{children}
</DashboardLayout>
);
Expand Down
8 changes: 6 additions & 2 deletions app/(user)/dashboard/leaderboards/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import DashboardWithKey from "../../../components/dashboard/WithKey";
import LeaderboardsList from "@/app/components/dashboard/LeaderbordList";
import { getUserWithProfile } from "@/app/lib/supabase/help/user";
import { Metadata } from "next";
import { redirect } from "next/navigation";

export const metadata: Metadata = {
title: "Leaderboards - DevPulse",
description: "Create, join, and manage your coding leaderboards.",
};

export default function LeaderboardsPage() {
export default async function LeaderboardsPage() {
const { user } = await getUserWithProfile();
if (!user) return redirect("/login?from=/dashboard/settings");

return (
<div className="p-6 md:p-8 space-y-6 max-w-5xl mx-auto">
<div className="border-b border-white/5 pb-5 flex flex-col sm:flex-row sm:items-center justify-between gap-4">
Expand Down
16 changes: 2 additions & 14 deletions app/(user)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,17 @@
import { redirect } from "next/navigation";
import { createClient } from "../../lib/supabase/server";
import DashboardWithoutKey from "../../components/dashboard/WithoutKey";
import Stats from "@/app/components/dashboard/Stats";
import { Metadata } from "next";
import { getUserWithProfile } from "@/app/lib/supabase/help/user";

export const metadata: Metadata = {
title: "Dashboard - DevPulse",
description: "Monitor your coding activity and manage your leaderboards.",
};

export default async function Dashboard() {
const supabase = await createClient();

const {
data: { user },
} = await supabase.auth.getUser();

const { user, profile } = await getUserWithProfile();
if (!user) redirect("/login");

const { data: profile } = await supabase
.from("profiles")
.select("wakatime_api_key, email")
.eq("id", user.id)
.single();

if (!profile?.wakatime_api_key) {
return <DashboardWithoutKey email={profile?.email || user.email!} />;
}
Expand Down
10 changes: 4 additions & 6 deletions app/(user)/dashboard/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { Metadata } from "next";
import { createClient } from "../../../lib/supabase/server";
import UserProfile from "@/app/components/dashboard/Settings/Profile";
import ResetPassword from "@/app/components/dashboard/Settings/ResetPassword";
import { getUserWithProfile } from "@/app/lib/supabase/help/user";
import { redirect } from "next/navigation";

export const metadata: Metadata = {
title: "Settings - DevPulse",
description:
"Manage your account settings and including your WakaTime API key.",
};

export default async function LeaderboardsPage() {
const supabase = await createClient();
const { data: userData } = await supabase.auth.getUser();
const user = userData.user;
const { user } = await getUserWithProfile();
if (!user) return redirect("/login?from=/dashboard/settings");

return (
<div className="p-6 md:p-8 space-y-6">
Expand Down
1 change: 0 additions & 1 deletion app/(user)/update-password/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import Footer from "@/app/components/layout/Footer";

export const metadata: Metadata = {
title: "Update Password - DevPulse",
description: "Update your DevPulse password to keep your account secure.",
};

export default async function UpdatePassword() {
Expand Down
75 changes: 35 additions & 40 deletions app/api/wakatime/sync/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { NextResponse } from "next/server";
import { createClient } from "../../../lib/supabase/server";
import { getUserWithProfile } from "@/app/lib/supabase/help/user";

export async function GET(request: Request) {
const supabase = await createClient();
const { user, profile } = await getUserWithProfile();
const { searchParams } = new URL(request.url);
const apiKey = searchParams.get("apiKey") || "";
let profile$: { wakatime_api_key: string };
Expand All @@ -16,22 +18,11 @@ export async function GET(request: Request) {

profile$ = { wakatime_api_key: apiKey };

const {
data: { user },
} = await supabase.auth.getUser();

if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

if (!apiKey) {
// Get profile with API key
const { data: profile } = await supabase
.from("profiles")
.select("wakatime_api_key")
.eq("id", user.id)
.single();

if (!profile?.wakatime_api_key) {
return NextResponse.json({ error: "No API key found" }, { status: 400 });
}
Expand Down Expand Up @@ -80,7 +71,7 @@ export async function GET(request: Request) {
`https://wakatime.com/api/v1/users/current/summaries?start=${startStr}&end=${endStr}`,
{
headers: { Authorization: authHeader },
}
},
),
]);

Expand Down Expand Up @@ -129,34 +120,38 @@ export async function GET(request: Request) {
}
}

const { data: statsResult, error: statsError } = await supabase
.from("user_stats")
.upsert({
user_id: user.id,
total_seconds: Math.floor(wakaStats.total_seconds),
daily_average: Math.floor(wakaStats.daily_average || 0),
languages: wakaStats.languages,
operating_systems: wakaStats.operating_systems,
editors: wakaStats.editors,
machines: wakaStats.machines,
categories: wakaStats.categories,
dependencies: wakaStats.dependencies || [],
best_day: wakaStats.best_day || {},
daily_stats: daily_stats,
last_fetched_at: new Date().toISOString(),
})
.select()
.single();

const { data: projectsResult, error: projectsError } = await supabase
.from("user_projects")
.upsert({
user_id: user.id,
projects: wakaStats.projects,
last_fetched_at: new Date().toISOString(),
})
.select()
.single();
const [
{ data: statsResult, error: statsError },
{ data: projectsResult, error: projectsError },
] = await Promise.all([
supabase
.from("user_stats")
.upsert({
user_id: user.id,
total_seconds: Math.floor(wakaStats.total_seconds),
daily_average: Math.floor(wakaStats.daily_average || 0),
languages: wakaStats.languages,
operating_systems: wakaStats.operating_systems,
editors: wakaStats.editors,
machines: wakaStats.machines,
categories: wakaStats.categories,
dependencies: wakaStats.dependencies || [],
best_day: wakaStats.best_day || {},
daily_stats: daily_stats,
last_fetched_at: new Date().toISOString(),
})
.select()
.single(),
supabase
.from("user_projects")
.upsert({
user_id: user.id,
projects: wakaStats.projects,
last_fetched_at: new Date().toISOString(),
})
.select()
.single(),
]);

const mergedResult = {
...statsResult,
Expand Down
7 changes: 7 additions & 0 deletions app/components/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,13 @@ export default function Chat({ user }: { user: User }) {
placeholder="Search user..."
className="w-full mb-3 px-3 py-2 bg-transparent text-gray-100 placeholder:text-gray-500 border border-neutral-800 rounded-xl outline-none"
/>

{allUsers.length == 0 && (
<div className="flex flex-col items-center justify-center py-16 text-center">
<p className="text-gray-400">No users found.</p>
</div>
)}

<div className="space-y-2 max-h-60 overflow-y-auto">
{allUsers
.filter((u) =>
Expand Down
4 changes: 2 additions & 2 deletions app/components/dashboard/WithoutKey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function DashboardWithoutKey({ email }: { email: string }) {
};

return (
<div className="space-y-6">
<div className="p-6 md:p-8 space-y-6">
<div data-aos="fade-up">
<h1 className="text-2xl font-bold text-white">Connect Wakatime</h1>
<p className="text-sm text-gray-600">
Expand All @@ -62,7 +62,7 @@ export default function DashboardWithoutKey({ email }: { email: string }) {
</p>
</div>

<div className="glass-card p-6" data-aos="fade-up" data-aos-delay="100">
<div className="glass-card p-6 max-w-xl" data-aos="fade-up" data-aos-delay="100">
<p className="text-gray-400 mb-8 text-sm">
Welcome <span className="text-white font-medium">{email}</span>. Enter
your WakaTime API key to activate your DevPulse dashboard.
Expand Down
20 changes: 20 additions & 0 deletions app/lib/supabase/help/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { cache } from "react";
import { createClient } from "../server";

export const getUserWithProfile = cache(async () => {
const supabase = await createClient();

const {
data: { user },
} = await supabase.auth.getUser();

if (!user) return { user: null, profile: null };

const { data: profile } = await supabase
.from("profiles")
.select("wakatime_api_key, email, role")
.eq("id", user.id)
.single();

return { user, profile };
});
Loading