"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { Pencil, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { cn } from "@/lib/utils"; type Props = { listId: string; name: string; /** Called after a successful rename, so the caller can update its own state. If omitted, falls back to router.refresh(). */ onRenamed?: (name: string) => void; /** Called after a successful delete, so the caller can update its own state (e.g. remove the row). */ onDeleted?: () => void; /** Path to navigate to after deleting (e.g. back to the list index from the detail page). */ redirectAfterDeleteTo?: string; className?: string; }; /** Owner-only rename/delete menu for a shopping list. Used on both the list index page and the list detail page. */ export function ShoppingListActionsMenu({ listId, name, onRenamed, onDeleted, redirectAfterDeleteTo, className }: Props) { const t = useTranslations("shoppingLists"); const tCommon = useTranslations("common"); const router = useRouter(); const [renameOpen, setRenameOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); const [newName, setNewName] = useState(name); const [saving, setSaving] = useState(false); const [deleting, setDeleting] = useState(false); function openRename() { setNewName(name); setRenameOpen(true); } async function handleRename() { const trimmed = newName.trim(); if (!trimmed || trimmed === name) { setRenameOpen(false); return; } setSaving(true); try { const res = await fetch(`/api/v1/shopping-lists/${listId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: trimmed }), }); if (!res.ok) throw new Error("failed"); toast.success(t("listRenamed")); setRenameOpen(false); if (onRenamed) onRenamed(trimmed); else router.refresh(); } catch { toast.error(t("listRenameFailed")); } finally { setSaving(false); } } async function handleDelete() { setDeleting(true); try { const res = await fetch(`/api/v1/shopping-lists/${listId}`, { method: "DELETE" }); if (!res.ok) throw new Error("failed"); toast.success(t("listDeleted")); setDeleteOpen(false); onDeleted?.(); if (redirectAfterDeleteTo) router.push(redirectAfterDeleteTo); else router.refresh(); } catch { toast.error(t("listDeleteFailed")); setDeleting(false); } } // Rows on the index page are wrapped in a Link — stop the click from bubbling to it // and prevent the anchor's default navigation. function stopRowNavigation(e: React.MouseEvent) { e.preventDefault(); e.stopPropagation(); } return ( <>
{ stopRowNavigation(e); openRename(); }} > } /> {t("rename")} { stopRowNavigation(e); setDeleteOpen(true); }} > } /> {tCommon("delete")}
{t("renameListTitle")}
setNewName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") void handleRename(); }} />
{t("deleteListConfirmTitle")} {t("deleteListConfirmDescription")} {tCommon("cancel")} { void handleDelete(); }} disabled={deleting} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {tCommon("delete")} ); }