diff --git a/frontend/components/chat/chat-panel.tsx b/frontend/components/chat/chat-panel.tsx index 463b1fd..46ff655 100644 --- a/frontend/components/chat/chat-panel.tsx +++ b/frontend/components/chat/chat-panel.tsx @@ -2,7 +2,7 @@ import { useChat } from "@ai-sdk/react"; import { DefaultChatTransport } from "ai"; -import { AlertTriangle, ShieldCheck } from "lucide-react"; +import { AlertTriangle, ShieldCheck, X } from "lucide-react"; import { nanoid } from "nanoid"; import { useSearchParams } from "next/navigation"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -47,7 +47,6 @@ import type { TemetroUIMessage } from "@/lib/ai-chat"; import { getAiConfig } from "@/lib/ai-settings"; import { API_BASE_URL } from "@/lib/api-client"; import { getPatient } from "@/lib/patients"; -import { notify } from "@/lib/toast"; // Trigger: `/patient 10293` or just `/10293` — a client-side fast-path that // pulls records instantly without the LLM (also works offline). @@ -96,12 +95,13 @@ export function ChatPanel() { }; }, []); - // Pop a toast whenever a request errors, so failures are never silent. + // Surface errors inline (and dismissible) instead of as a toast, so a failure + // stays visible until acknowledged and isn't duplicated. Reset the dismissed + // flag whenever a fresh error arrives. + const [errorDismissed, setErrorDismissed] = useState(false); useEffect(() => { - if (error) { - notify.error(t("chat.error.title"), error.message || t("chat.error.body")); - } - }, [error, t]); + if (error) setErrorDismissed(false); + }, [error]); const isCloudModel = (getModel(model)?.provider ?? "ollama") !== "ollama"; @@ -210,20 +210,29 @@ export function ChatPanel() { /> ) : null; - const errorAlert = error ? ( -
{t("chat.error.title")}
-- {error.message || t("chat.error.body")} -
+ const errorAlert = + error && !errorDismissed ? ( +{t("chat.error.title")}
++ {error.message || t("chat.error.body")} +
+