Files
Epicure/apps/web/components/recipe/translate-button.tsx
T
2026-07-01 11:10:37 +02:00

132 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { Languages, Loader2 } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
const LANGUAGES = [
{ code: "French", label: "French" },
{ code: "Spanish", label: "Spanish" },
{ code: "German", label: "German" },
{ code: "Italian", label: "Italian" },
{ code: "Portuguese", label: "Portuguese" },
{ code: "English", label: "English" },
{ code: "Japanese", label: "Japanese" },
{ code: "Chinese", label: "Chinese" },
{ code: "Arabic", label: "Arabic" },
{ code: "Dutch", label: "Dutch" },
{ code: "Polish", label: "Polish" },
{ code: "Russian", label: "Russian" },
];
export function TranslateButton({ recipeId }: { recipeId: string }) {
const router = useRouter();
const [open, setOpen] = useState(false);
const [targetLanguage, setTargetLanguage] = useState("French");
const [translating, setTranslating] = useState(false);
async function handleTranslate() {
setTranslating(true);
try {
const res = await fetch(`/api/v1/ai/translate/${recipeId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ targetLanguage }),
});
if (!res.ok) {
const err = await res.json() as { error?: string };
toast.error(err.error ?? "Failed to translate recipe");
return;
}
const { id } = await res.json() as { id: string };
toast.success("Translation saved as new draft recipe");
setOpen(false);
router.push(`/recipes/${id}/edit`);
} finally {
setTranslating(false);
}
}
return (
<>
<TooltipProvider>
<Tooltip>
<TooltipTrigger render={
<Button variant="ghost" size="icon" onClick={() => setOpen(true)}>
<Languages className="h-4 w-4" />
</Button>
} />
<TooltipContent>Translate</TooltipContent>
</Tooltip>
</TooltipProvider>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Languages className="h-5 w-5 text-primary" />
Translate Recipe
</DialogTitle>
<DialogDescription>
AI will translate the title, description, ingredients, and steps. Quantities and units are preserved.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-2">
<Label>Target language</Label>
<Select value={targetLanguage} onValueChange={(v) => v && setTargetLanguage(v)} disabled={translating}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{LANGUAGES.map((l) => (
<SelectItem key={l.code} value={l.code}>{l.label}</SelectItem>
))}
</SelectContent>
</Select>
</div>
{translating && (
<p className="text-sm text-muted-foreground">This may take 2030 seconds</p>
)}
<div className="flex gap-2 justify-end">
<Button variant="outline" onClick={() => setOpen(false)} disabled={translating}>
Cancel
</Button>
<Button onClick={handleTranslate} disabled={translating}>
{translating ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
Translating
</>
) : (
<>
<Languages className="h-4 w-4" />
Translate
</>
)}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
</>
);
}