feat: bulk tag editing, bulk export, and move recipes between collections
Extends the existing bulk-select infra: recipes list gets tag add/remove and multi-recipe Markdown export; collection pages get a select mode to move recipes to another collection or remove them from the current one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,11 +20,14 @@ export function AddToCollectionDialog({
|
||||
onOpenChange,
|
||||
recipeIds,
|
||||
onDone,
|
||||
sourceCollectionId,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
recipeIds: string[];
|
||||
onDone: () => void;
|
||||
/** When set, this is a "move" — after adding to the target, the recipes are removed from this collection. */
|
||||
sourceCollectionId?: string;
|
||||
}) {
|
||||
const t = useTranslations("recipe");
|
||||
const [collections, setCollections] = useState<CollectionSummary[]>([]);
|
||||
@@ -42,10 +45,10 @@ export function AddToCollectionDialog({
|
||||
fetch("/api/v1/collections?limit=50")
|
||||
.then((res) => (res.ok ? res.json() : null))
|
||||
.then((data: { data: CollectionSummary[] } | null) => {
|
||||
setCollections(data?.data ?? []);
|
||||
setCollections((data?.data ?? []).filter((c) => c.id !== sourceCollectionId));
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}, [open]);
|
||||
}, [open, sourceCollectionId]);
|
||||
|
||||
async function addTo(collectionId: string, collectionName: string) {
|
||||
setBusyId(collectionId);
|
||||
@@ -56,7 +59,20 @@ export function AddToCollectionDialog({
|
||||
body: JSON.stringify({ addRecipeIds: recipeIds }),
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
toast.success(t("addToCollectionSuccess", { count: recipeIds.length, name: collectionName }));
|
||||
|
||||
if (sourceCollectionId) {
|
||||
await fetch(`/api/v1/collections/${sourceCollectionId}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ removeRecipeIds: recipeIds }),
|
||||
});
|
||||
}
|
||||
|
||||
toast.success(
|
||||
sourceCollectionId
|
||||
? t("moveToCollectionSuccess", { count: recipeIds.length, name: collectionName })
|
||||
: t("addToCollectionSuccess", { count: recipeIds.length, name: collectionName })
|
||||
);
|
||||
onOpenChange(false);
|
||||
onDone();
|
||||
} catch {
|
||||
@@ -91,7 +107,9 @@ export function AddToCollectionDialog({
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<FolderPlus className="h-5 w-5 text-primary" />
|
||||
{t("addToCollectionTitle", { count: recipeIds.length })}
|
||||
{sourceCollectionId
|
||||
? t("moveToCollectionTitle", { count: recipeIds.length })
|
||||
: t("addToCollectionTitle", { count: recipeIds.length })}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user