"use client"; import { useState, useCallback } from "react"; import { useTranslations } from "next-intl"; import { toast } from "sonner"; import { ListChecks, X, FolderInput, FolderMinus, Check } from "lucide-react"; import { Button, buttonVariants } from "@/components/ui/button"; import { RecipeCard } from "@/components/recipe/recipe-card"; import { AddToCollectionDialog } from "@/components/recipe/add-to-collection-dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { cn } from "@/lib/utils"; type Recipe = { id: string; title: string; description: string | null; baseServings: number; prepMins: number | null; cookMins: number | null; difficulty: "easy" | "medium" | "hard" | null; visibility: "private" | "unlisted" | "public" | "followers"; updatedAt: Date; photos?: Array<{ storageKey: string; isCover: boolean }>; sourceUrl?: string | null; }; export function CollectionRecipesGrid({ collectionId, recipes: initialRecipes }: { collectionId: string; recipes: Recipe[] }) { const t = useTranslations("collections"); const tCommon = useTranslations("common"); const [recipes, setRecipes] = useState(initialRecipes); const [selectMode, setSelectMode] = useState(false); const [selected, setSelected] = useState>(new Set()); const [moveOpen, setMoveOpen] = useState(false); const [removeConfirmOpen, setRemoveConfirmOpen] = useState(false); const [busy, setBusy] = useState(false); const toggleSelect = useCallback((id: string) => { setSelected((prev) => { const next = new Set(prev); next.has(id) ? next.delete(id) : next.add(id); return next; }); }, []); const exitSelect = () => { setSelectMode(false); setSelected(new Set()); }; async function removeFromCollection() { setBusy(true); try { const res = await fetch(`/api/v1/collections/${collectionId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ removeRecipeIds: [...selected] }), }); if (!res.ok) throw new Error(); setRecipes((prev) => prev.filter((r) => !selected.has(r.id))); toast.success(t("removedFromCollection", { count: selected.size })); exitSelect(); } catch { toast.error(t("removeFromCollectionFailed")); } finally { setBusy(false); } } if (recipes.length === 0) return null; return (
{recipes.map((recipe) => (
toggleSelect(recipe.id) : undefined}> {selectMode && (
{selected.has(recipe.id) && }
)}
))}
{selectMode && selected.size > 0 && (
{selected.size}
)} { setRecipes((prev) => prev.filter((r) => !selected.has(r.id))); exitSelect(); }} /> {t("removeFromCollectionConfirmTitle", { count: selected.size })} {t("removeFromCollectionConfirmDescription")} {tCommon("cancel")} { setRemoveConfirmOpen(false); void removeFromCollection(); }} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {t("removeFromCollection")}
); }