feat: dedicated favorites page for recipes
Favoriting already worked (heart toggle on the recipe detail page, DB table, API route) but there was nowhere to see what you'd favorited — the actual missing piece behind "add a favorites feature". - New /recipes/favorites page: paginated grid of favorited recipes (cover photo, difficulty, time, author), sorted by most-recently favorited. A previously-favorited recipe stays visible even if the author later goes private, matching the existing private-accounts scope decision (existing access isn't revoked, only new discovery is affected). - Unfavoriting from that page removes the card immediately — added an optional onToggle callback to FavoriteButton for this. - Added a lightweight "My Recipes / Favorites" tab pair at the top of /recipes linking between the two, rather than a new top-level nav item (nav is already at 8 entries). Deliberately out of scope: favorite toggles on the main recipe grid, feed cards, or search/explore results — favoriting your own recipes is odd UX, and wiring session-aware favorited state into the public (unauthenticated) search route is a separate, bigger change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import { eq, desc, asc, and, ilike, or } from "@epicure/db";
|
||||
import { RecipesHeader } from "@/components/recipe/recipes-header";
|
||||
import { RecipesEmptyState } from "@/components/recipe/recipes-empty-state";
|
||||
import { RecipesGrid } from "@/components/recipe/recipes-grid";
|
||||
import { getMessages } from "@/lib/i18n/server";
|
||||
|
||||
export const metadata: Metadata = {};
|
||||
|
||||
@@ -35,6 +36,7 @@ type SortKey = keyof typeof SORT_MAP;
|
||||
export default async function RecipesPage({ searchParams }: { searchParams: SearchParams }) {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
if (!session) return null;
|
||||
const m = getMessages((session.user as { locale?: string }).locale);
|
||||
|
||||
const { q, sort, visibility, difficulty, tag, page: pageParam } = await searchParams;
|
||||
const query = (q ?? "").trim().slice(0, 200);
|
||||
@@ -88,6 +90,12 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<span className="rounded-full bg-accent px-3 py-1 font-medium">{m.recipes.tabMine}</span>
|
||||
<Link href="/recipes/favorites" className="rounded-full px-3 py-1 text-muted-foreground hover:bg-accent">
|
||||
{m.recipes.tabFavorites}
|
||||
</Link>
|
||||
</div>
|
||||
<RecipesHeader
|
||||
count={total}
|
||||
initialQuery={query}
|
||||
|
||||
Reference in New Issue
Block a user