"use client"; import Image from "next/image"; import { useTranslations } from "next-intl"; import { Globe, Lock, Link2, ExternalLink, ChefHat, Clock, Users, Utensils } from "lucide-react"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Badge } from "@/components/ui/badge"; import { FavoriteButton } from "@/components/social/favorite-button"; import { getPublicUrl } from "@/lib/storage"; import { cn, stripMarkdown } from "@/lib/utils"; export type GridCardRecipe = { id: string; title: string; description: string | null; baseServings: number; prepMins: number | null; cookMins: number | null; difficulty: "easy" | "medium" | "hard" | null; visibility: "private" | "unlisted" | "public"; tags: string[]; photos?: Array<{ storageKey: string; isCover: boolean }>; isFavorited?: boolean; isBatchCook?: boolean; dishCount?: number; sourceUrl?: string | null; recipeType?: "dish" | "drink"; authorName?: string | null; }; const DIFFICULTY_COLOR = { easy: "default", medium: "secondary", hard: "destructive" } as const; const VISIBILITY_ICON = { private: Lock, unlisted: Link2, public: Globe }; function hostnameOf(url: string): string { try { return new URL(url).hostname.replace(/^www\./, ""); } catch { return url; } } /** Shared cover-photo/emoji-fallback thumbnail used by the recipe grid card. */ export function RecipeThumb({ recipe, className }: { recipe: GridCardRecipe; className?: string }) { const cover = recipe.photos?.find((p) => p.isCover) ?? recipe.photos?.[0]; return (
{recipe.authorName}
)}{recipe.isBatchCook ? stripMarkdown(recipe.description) : recipe.description}
)} {recipe.tags.length > 0 && (