import type { Metadata } from "next"; import { headers } from "next/headers"; import { db, recipes, users, favorites, userFollows, eq, desc, sql, and, gte, count, } from "@epicure/db"; import { auth } from "@/lib/auth/server"; import { ExplorePageContent } from "@/components/search/explore-page-content"; import { attachCardExtras } from "@/lib/recipe-card-extras"; export const metadata: Metadata = {}; export type RecipeResult = { id: string; title: string; description: string | null; authorName: string | null; difficulty: "easy" | "medium" | "hard" | null; baseServings: number; prepMins: number | null; cookMins: number | null; visibility: "private" | "unlisted" | "public" | "followers"; tags: string[]; isBatchCook: boolean; sourceUrl: string | null; recipeType: "dish" | "drink"; photos: { storageKey: string; isCover: boolean }[]; dishCount: number; isFavorited: boolean; }; export default async function ExplorePage({ searchParams, }: { searchParams: Promise<{ q?: string }>; }) { const { q } = await searchParams; const session = await auth.api.getSession({ headers: await headers() }); const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); const columns = { id: recipes.id, title: recipes.title, description: recipes.description, authorName: users.name, difficulty: recipes.difficulty, baseServings: recipes.baseServings, prepMins: recipes.prepMins, cookMins: recipes.cookMins, visibility: recipes.visibility, tags: recipes.tags, isBatchCook: recipes.isBatchCook, sourceUrl: recipes.sourceUrl, recipeType: recipes.recipeType, coverIcon: recipes.coverIcon, coverColor: recipes.coverColor, }; // Trending: public recipes ordered by favorite count in last 7 days const trendingRows = await db .select({ ...columns, favoriteCount: count(favorites.recipeId) }) .from(recipes) .innerJoin(users, eq(recipes.authorId, users.id)) .leftJoin( favorites, and( eq(favorites.recipeId, recipes.id), gte(favorites.createdAt, sevenDaysAgo) ) ) .where(and(eq(recipes.visibility, "public"), eq(users.isPrivate, false))) .groupBy(recipes.id, users.id) .orderBy(desc(sql`count(${favorites.recipeId})`)) .limit(12); // Recent: public recipes ordered by createdAt desc const recentRows = await db .select(columns) .from(recipes) .innerJoin(users, eq(recipes.authorId, users.id)) .where(and(eq(recipes.visibility, "public"), eq(users.isPrivate, false))) .orderBy(desc(recipes.createdAt)) .limit(12); const [trending, recent] = await Promise.all([ attachCardExtras(trendingRows.map(({ favoriteCount: _fc, ...r }) => r), session?.user.id), attachCardExtras(recentRows, session?.user.id), ]); // Most-used tags across public recipes — shown as filter chips on the // search bar so browsing by tag doesn't require typing the exact word. const popularTagsResult = await db.execute<{ tag: string }>(sql` select tag, count(*) as usage_count from recipes r inner join users u on r.author_id = u.id cross join lateral unnest(r.tags) as tag where r.visibility = 'public' and u.is_private = false group by tag order by usage_count desc limit 12 `); const popularTags = popularTagsResult.map((r) => r.tag); const followedCount = session ? ( await db .select({ followingId: userFollows.followingId }) .from(userFollows) .where(eq(userFollows.followerId, session.user.id)) ).length : 0; return ( ); }