import { useMemo, useState } from "react"; import { useMutation } from "@tanstack/react-query"; import { useLocation } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Sparkles, Send, Loader2 } from "lucide-react"; import { coachingService } from "@/services/coaching.service"; import { useToast } from "@/hooks/use-toast"; export default function AiAssistantDrawer() { const [open, setOpen] = useState(false); const [messages, setMessages] = useState<{ role: "user" | "ai"; text: string }[]>([]); const [input, setInput] = useState(""); const location = useLocation(); const { toast } = useToast(); const { t } = useTranslation(); // Re-compute when language switches so quick-action chips show translated // labels immediately. Chips are stored by label (what we send to the // backend), so localizing the label is safe here — the backend treats // them as free-form prompts. const quickActions = useMemo( () => [ t("ai.quickHealth"), t("ai.quickDropouts"), t("ai.quickCompare"), t("ai.quickBatch"), ], [t], ); const chatMutation = useMutation({ mutationFn: (message: string) => coachingService.chat({ message, context: { page: location.pathname } }), onSuccess: (data) => { setMessages((prev) => [...prev, { role: "ai", text: data.reply }]); }, onError: (err: Error) => { toast({ variant: "destructive", title: t("ai.replyErrorTitle"), description: err.message || t("ai.replyErrorDesc"), }); setMessages((prev) => [ ...prev, { role: "ai", text: t("ai.fallbackReply") }, ]); }, }); const handleSend = (text: string) => { if (!text.trim() || chatMutation.isPending) return; const userMsg = text.trim(); setMessages((prev) => [...prev, { role: "user", text: userMsg }]); setInput(""); chatMutation.mutate(userMsg); }; return ( <> setOpen(true)} className="fixed bottom-20 end-6 z-50 flex h-12 w-12 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg hover:opacity-90 transition-opacity" aria-label={t("ai.assistantLabel")} title={t("ai.assistantLabel")} > {t("ai.assistantTitle")} {quickActions.map((action) => ( handleSend(action)} > {action} ))} {messages.length === 0 && ( {t("ai.emptyLine1")} {t("ai.emptyLine2")} )} {messages.map((msg, i) => ( {msg.role === "ai" && ( )} {msg.text} ))} {chatMutation.isPending && ( {t("ai.thinking")} )} setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleSend(input)} /> handleSend(input)} disabled={!input.trim() || chatMutation.isPending} aria-label={t("ai.send")} title={t("ai.send")} > > ); }
{t("ai.emptyLine1")}
{t("ai.emptyLine2")}