diff --git a/actions/repositories.ts b/actions/repositories.ts index afc0985..8888843 100644 --- a/actions/repositories.ts +++ b/actions/repositories.ts @@ -801,3 +801,42 @@ export async function getUserStarredRepos(username: string) { }, })); } + +export async function getPublicUsers(sortBy: "newest" | "oldest" = "newest", limit: number = 20, offset: number = 0) { + "use cache"; + cacheTag("public-users", `public-users:${sortBy}:${offset}`); + cacheLife("minutes"); + + const allUsers = await db + .select({ + id: users.id, + name: users.name, + username: users.username, + image: users.image, + avatarUrl: users.avatarUrl, + bio: users.bio, + createdAt: users.createdAt, + repoCount: sql`(SELECT COUNT(*) FROM repositories WHERE repositories.owner_id = ${users.id} AND repositories.visibility = 'public')`.as("repo_count"), + }) + .from(users) + .orderBy(sortBy === "newest" ? desc(users.createdAt) : users.createdAt) + .limit(limit + 1) + .offset(offset); + + const hasMore = allUsers.length > limit; + const result = allUsers.slice(0, limit); + + return { + users: result.map((u) => ({ + id: u.id, + name: u.name, + username: u.username, + image: u.image, + avatarUrl: u.avatarUrl, + bio: u.bio, + createdAt: u.createdAt, + repoCount: Number(u.repoCount), + })), + hasMore, + }; +} diff --git a/app/(main)/explore/page.tsx b/app/(main)/explore/page.tsx index f530b31..d9e3df6 100644 --- a/app/(main)/explore/page.tsx +++ b/app/(main)/explore/page.tsx @@ -1,18 +1,24 @@ import { Suspense } from "react"; import { connection } from "next/server"; import Link from "next/link"; -import { getPublicRepositories } from "@/actions/repositories"; +import { getPublicRepositories, getPublicUsers } from "@/actions/repositories"; import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Star, GitBranch, ChevronLeft, ChevronRight, Compass, Clock, Flame, Sparkles, Loader2 } from "lucide-react"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Star, GitBranch, ChevronLeft, ChevronRight, Compass, Clock, Flame, Sparkles, Users, BookOpen } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; -const SORT_OPTIONS = [ +const REPO_SORT_OPTIONS = [ { value: "stars", label: "Most stars", icon: Flame }, { value: "updated", label: "Recently updated", icon: Clock }, { value: "created", label: "Newest", icon: Sparkles }, ] as const; +const USER_SORT_OPTIONS = [ + { value: "newest", label: "Newest", icon: Sparkles }, + { value: "oldest", label: "Oldest", icon: Clock }, +] as const; + async function RepoGrid({ sortBy, page, perPage }: { sortBy: "stars" | "updated" | "created"; page: number; perPage: number }) { await connection(); const offset = (page - 1) * perPage; @@ -65,14 +71,14 @@ async function RepoGrid({ sortBy, page, perPage }: { sortBy: "stars" | "updated" {(page > 1 || hasMore) && (
Page {page} + Page {page} + +
+ )} + + ); +} + +function GridSkeleton() { return (
{[...Array(5)].map((_, i) => ( @@ -105,10 +177,36 @@ function RepoGridSkeleton() { ); } -export default async function ExplorePage({ searchParams }: { searchParams: Promise<{ sort?: string; page?: string }> }) { - const { sort: sortParam, page: pageParam } = await searchParams; +function UserGridSkeleton() { + return ( +
+ {[...Array(6)].map((_, i) => ( +
+
+
+
+
+
+
+
+
+
+ ))} +
+ ); +} + +export default async function ExplorePage({ + searchParams, +}: { + searchParams: Promise<{ tab?: string; sort?: string; page?: string; usort?: string; upage?: string }>; +}) { + const { tab, sort: sortParam, page: pageParam, usort: usortParam, upage: upageParam } = await searchParams; + const activeTab = tab === "users" ? "users" : "repositories"; const sortBy = (["stars", "updated", "created"].includes(sortParam || "") ? sortParam : "stars") as "stars" | "updated" | "created"; const page = parseInt(pageParam || "1", 10); + const userSortBy = (["newest", "oldest"].includes(usortParam || "") ? usortParam : "newest") as "newest" | "oldest"; + const userPage = parseInt(upageParam || "1", 10); const perPage = 20; return ( @@ -118,23 +216,59 @@ export default async function ExplorePage({ searchParams }: { searchParams: Prom

Explore

-

Discover public repositories from the community

+

Discover repositories and users from the community

-
- {SORT_OPTIONS.map(({ value, label, icon: Icon }) => ( - - ))} -
+ + + + + Users + + + - }> - - + +
+ {REPO_SORT_OPTIONS.map(({ value, label, icon: Icon }) => ( + + ))} +
+ + }> + + +
+ + +
+ {USER_SORT_OPTIONS.map(({ value, label, icon: Icon }) => ( + + ))} +
+ + }> + + +
+
); }