"use client"; import { useState, useTransition } from "react"; import Link from "next/link"; import { useRouter, usePathname } from "next/navigation"; import { PlusCircle, Sparkles, Link2, Search, X, SlidersHorizontal, ArrowUpDown } from "lucide-react"; import { useTranslations } from "next-intl"; import { Button, buttonVariants } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { AiGenerateDialog } from "./ai-generate-dialog"; import { UrlImportDialog } from "./url-import-dialog"; function TagFilterInput({ value, onChange }: { value: string; onChange: (v: string) => void }) { const [local, setLocal] = useState(value); const t = useTranslations("recipes"); return ( setLocal(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") onChange(local); }} onBlur={() => onChange(local)} placeholder={t("filterByTag")} className="w-full h-7 px-2 text-sm rounded-md border border-input bg-background focus:outline-none focus:ring-1 focus:ring-ring" /> ); } import { cn } from "@/lib/utils"; const SORT_KEYS: Record = { updated_desc: "sortRecentlyUpdated", updated_asc: "sortOldestUpdated", created_desc: "sortNewestFirst", created_asc: "sortOldestFirst", title_asc: "sortTitleAZ", title_desc: "sortTitleZA", }; const VISIBILITY_KEYS: Record = { "": "all", private: "private", unlisted: "unlisted", public: "public", }; const DIFFICULTY_KEYS: Record = { "": "anyDifficulty", easy: "easy", medium: "medium", hard: "hard", }; const BATCH_COOK_KEYS: Record = { "": "all", "1": "batchCookOnly", "0": "batchCookExclude", }; export function RecipesHeader({ count, initialQuery = "", initialSort = "updated_desc", initialVisibility = "", initialDifficulty = "", initialTag = "", initialBatchCook = "", }: { count: number; initialQuery?: string; initialSort?: string; initialVisibility?: string; initialDifficulty?: string; initialTag?: string; initialBatchCook?: string; }) { const router = useRouter(); const pathname = usePathname(); const t = useTranslations("recipes"); const tRecipe = useTranslations("recipe"); const [aiOpen, setAiOpen] = useState(false); const [urlOpen, setUrlOpen] = useState(false); const [query, setQuery] = useState(initialQuery); const [, startTransition] = useTransition(); function pushParams(overrides: Record) { const params = new URLSearchParams(); const current = { q: query, sort: initialSort, visibility: initialVisibility, difficulty: initialDifficulty, tag: initialTag, batchCook: initialBatchCook, ...overrides, }; if (current.q?.trim()) params.set("q", current.q.trim()); if (current.sort && current.sort !== "updated_desc") params.set("sort", current.sort); if (current.visibility) params.set("visibility", current.visibility); if (current.difficulty) params.set("difficulty", current.difficulty); if (current.tag?.trim()) params.set("tag", current.tag.trim()); if (current.batchCook) params.set("batchCook", current.batchCook); startTransition(() => router.push(`${pathname}?${params.toString()}`)); } function handleSearch(value: string) { setQuery(value); pushParams({ q: value }); } const activeFilterCount = [initialVisibility, initialDifficulty, initialTag, initialBatchCook].filter(Boolean).length; const sortChanged = initialSort !== "updated_desc"; return ( <>

{t("title")}

{count === 0 ? t("subtitle") : t(count !== 1 ? "resultPlural" : "resultSingular", { count })}

{t("new")}
{/* Search */}
handleSearch(e.target.value)} placeholder={t("search")} className="pl-9 pr-9" /> {query && ( )}
{/* Sort */} {SORT_KEYS[initialSort] ? t(SORT_KEYS[initialSort]) : t("sort")} {t("sortBy")} {Object.entries(SORT_KEYS).map(([value, key]) => ( pushParams({ sort: value })} className={cn(initialSort === value && "font-medium text-primary")} > {t(key)} ))} {/* Filters */} 0 ? "secondary" : "outline", size: "sm" }), "gap-1.5" )} > {t("filter")} {activeFilterCount > 0 && ( {activeFilterCount} )} {t("visibilityLabel")} {Object.entries(VISIBILITY_KEYS).map(([value, key]) => ( pushParams({ visibility: value })} className={cn(initialVisibility === value && "font-medium text-primary")} > {value === "" ? t(key) : tRecipe(`visibility.${key}`)} ))} {t("difficultyLabel")} {Object.entries(DIFFICULTY_KEYS).map(([value, key]) => ( pushParams({ difficulty: value })} className={cn(initialDifficulty === value && "font-medium text-primary")} > {value === "" ? t(key) : tRecipe(`difficulty.${key}`)} ))} {t("tagLabel")}
pushParams({ tag: v })} />
{t("batchCookLabel")} {Object.entries(BATCH_COOK_KEYS).map(([value, key]) => ( pushParams({ batchCook: value })} className={cn(initialBatchCook === value && "font-medium text-primary")} > {t(key)} ))} {activeFilterCount > 0 && ( <> pushParams({ visibility: "", difficulty: "", tag: "", batchCook: "" })} className="text-muted-foreground" > {t("clearFilters")} )}
); }