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:
Arnaud
2026-07-10 13:46:15 +02:00
parent 1a40eccad5
commit a5661ef882
7 changed files with 285 additions and 2 deletions
@@ -11,9 +11,12 @@ import { cn } from "@/lib/utils";
export function FavoriteButton({
recipeId,
initialFavorited = false,
onToggle,
}: {
recipeId: string;
initialFavorited?: boolean;
/** Called after a successful toggle, e.g. to remove the recipe from a "Favorites" list view. */
onToggle?: (favorited: boolean) => void;
}) {
const tCommon = useTranslations("common");
const tSocial = useTranslations("social");
@@ -29,6 +32,7 @@ export function FavoriteButton({
method: next ? "POST" : "DELETE",
});
if (!res.ok) throw new Error();
onToggle?.(next);
} catch {
setFavorited(!next);
toast.error(tCommon("updateFailed"));