diff --git a/app/(public)/(auth)/reset-password/page.tsx b/app/(public)/(auth)/reset-password/page.tsx index aa71c8c..c4f4835 100644 --- a/app/(public)/(auth)/reset-password/page.tsx +++ b/app/(public)/(auth)/reset-password/page.tsx @@ -47,11 +47,11 @@ export const metadata: Metadata = { export default async function ResetPassword() { return ( -
+
{/* Left Side - Visual / Branding */}
{/* Background elements */} -
+
-
+
-
- - -
- Devpulse Logo -

Devpulse Flexes

-
+ <> +
+

+ Flex +

{flexes.length === 0 && ( -
-

No Flexes Yet

+
+

No Project Flexes Yet

- Please come back later to see the latest flexes from our - community. + Come back later to see what the community is building.

)} @@ -111,12 +106,12 @@ export default async function Flexs() { Open Source: - {flex.is_open_source} + {flex.open_source_url} )} @@ -143,6 +138,6 @@ export default async function Flexs() {
-
+ ); } diff --git a/app/(public)/join/[code]/page.tsx b/app/(public)/(guest)/join/[code]/page.tsx similarity index 100% rename from app/(public)/join/[code]/page.tsx rename to app/(public)/(guest)/join/[code]/page.tsx diff --git a/app/(public)/join/page.tsx b/app/(public)/(guest)/join/page.tsx similarity index 73% rename from app/(public)/join/page.tsx rename to app/(public)/(guest)/join/page.tsx index d828a9e..2d46874 100644 --- a/app/(public)/join/page.tsx +++ b/app/(public)/(guest)/join/page.tsx @@ -1,7 +1,7 @@ import { Metadata } from "next/types"; import { prisma } from "@/app/lib/prisma"; import { getCurrentUser } from "@/app/lib/auth/user"; -import JoinButton from "../../components/JoinButton"; +import JoinButton from "../../../components/JoinButton"; import Footer from "@/app/components/layout/Footer"; import Image from "next/image"; import Link from "next/link"; @@ -12,6 +12,7 @@ import { faRankingStar, faUsers, } from "@fortawesome/free-solid-svg-icons"; +import { notFound } from "next/navigation"; type Props = { searchParams: Promise<{ id?: string }>; @@ -81,59 +82,14 @@ export default async function JoinPage({ searchParams }: Props) { const { id } = await searchParams; const code = (id || "").trim(); - if (!code) { - return ( -
-
-
- -
-

- Join a Leaderboard -

-

- Open an invite link like{" "} - /join?id=XXXXXXXX. -

- - Go to Devpulse - -
-
- ); - } + if (!code) return notFound(); const [leaderboard, { user }] = await Promise.all([ getLeaderboard(code), getCurrentUser(), ]); - if (!leaderboard) { - return ( -
-
-
- -
-

- Invite Not Found -

-

- This invite link is invalid or has expired. -

- - Go to Devpulse - -
-
- ); - } + if (!leaderboard) return notFound(); const memberCount = await getMemberCount(leaderboard.id); @@ -152,7 +108,7 @@ export default async function JoinPage({ searchParams }: Props) { } return ( -
+
@@ -160,7 +116,12 @@ export default async function JoinPage({ searchParams }: Props) {
- Devpulse + Devpulse
diff --git a/app/(public)/(guest)/layout.tsx b/app/(public)/(guest)/layout.tsx new file mode 100644 index 0000000..28b545d --- /dev/null +++ b/app/(public)/(guest)/layout.tsx @@ -0,0 +1,15 @@ +import Nav from "@/app/components/layout/Nav"; + +export default function GuestLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+
+ ); +} diff --git a/app/(public)/(guest)/leaderboard/[slug]/page.tsx b/app/(public)/(guest)/leaderboard/[slug]/page.tsx new file mode 100644 index 0000000..1a8663c --- /dev/null +++ b/app/(public)/(guest)/leaderboard/[slug]/page.tsx @@ -0,0 +1,102 @@ +import LeaderboardTable, { + NonNullableMember, +} from "../../../../components/leaderboard/LeaderboardTable"; +import Footer from "@/app/components/layout/Footer"; +import CTA from "@/app/components/common/ui/CTA"; +import { notFound } from "next/navigation"; +import InviteFriendsButton from "@/app/components/leaderboard/InviteFriendsButton"; +import InternalServerError from "@/app/internal-server-error"; +import { prisma } from "@/app/lib/prisma"; +import LeaderboardStats from "@/app/components/leaderboard/LeaderboardStats"; +import { getCurrentUser } from "@/app/lib/auth/user"; + +export async function generateStaticParams() { + const leaderboards = await prisma.leaderboard.findMany({ + select: { slug: true }, + }); + + return leaderboards.map((item) => ({ slug: item.slug })); +} + +export default async function LeaderboardPage(props: { + params: Promise<{ slug: string }>; +}) { + const { user } = await getCurrentUser(); + const { slug } = await props.params; + + const leaderboard = await prisma.leaderboard.findUnique({ + where: { slug }, + }); + + if (!leaderboard) return notFound(); + + let members: NonNullableMember[] = []; + try { + const rows = await prisma.leaderboardMember.findMany({ + where: { leaderboard_id: leaderboard.id }, + include: { + user: { + select: { + id: true, + email: true, + role: true, + user_stats: { + select: { + total_seconds: true, + languages: true, + operating_systems: true, + editors: true, + }, + }, + }, + }, + }, + }); + + members = rows + .filter((r) => r.user.email) + .map((r) => ({ + user_id: r.user.id, + role: r.role, + email: r.user.email!, + total_seconds: Number(r.user.user_stats?.total_seconds ?? 0), + languages: (r.user.user_stats?.languages as { name: string }[]) ?? [], + operating_systems: + (r.user.user_stats?.operating_systems as { name: string }[]) ?? [], + editors: (r.user.user_stats?.editors as { name: string }[]) ?? [], + })); + } catch { + return InternalServerError(); + } + + return ( + <> +
+
+
+

+ {leaderboard.name} +

+

+ Join {leaderboard.name} to track your coding metrics, compete with + fellow developers, and showcase your engineering skills. +

+
+ + {user && ( + + )} +
+ + + +
+ + +