import { useState } from "react"; import { useRepos } from "../../data/DataContext"; import type { DemandMap, WatchTermSuggestion } from "../../domain/types"; import { useI18n } from "../../i18n/I18nContext"; import { useFormatApiError } from "../../lib/apiErrors"; import { Badge, Button } from "../ui"; import { QueryPlanPreview } from "./QueryPlanPreview"; type Props = { /** 採用一條:include 進關鍵字、exclude 進排除詞,由呼叫端決定放哪 */ onAdopt: (suggestion: WatchTermSuggestion) => void; onAdoptAll: (suggestions: WatchTermSuggestion[]) => void; /** 採用查詢計畫裡已收成的可搜短詞 */ onAdoptQueries?: (queries: string[]) => void; /** 已在表單裡的詞(含排除詞),用來標示重複,避免使用者按了沒反應 */ adopted: string[]; context?: { brand_id?: string; product_id?: string }; demandMap?: DemandMap; }; const SUGGEST_LIMIT = 8; /** * 關鍵字建議:讀服務檔案問 AI,逐條或全部採用。 * * 這裡只給候選詞,不會自己建立訂閱——建立與否由使用者在表單按下儲存決定。 */ export function WatchSuggestPanel({ onAdopt, onAdoptAll, onAdoptQueries, adopted, context, demandMap }: Props) { const { t } = useI18n(); const repos = useRepos(); const formatError = useFormatApiError(); const [list, setList] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [asked, setAsked] = useState(false); const taken = new Set(adopted.map((s) => s.trim().toLowerCase())); async function ask() { setLoading(true); setError(""); try { setList(await repos.radar.suggestWatchTerms({ limit: SUGGEST_LIMIT, ...context })); setAsked(true); } catch (e) { // 沒服務檔案、沒 API key、AI 失敗都會走到這;照後端原因說,別假裝沒建議。 setError(formatError(e)); } finally { setLoading(false); } } return (

{t("radar.suggest.title")}

{t("radar.suggest.hint")}

{demandMap ? : null}
{list.length ? ( ) : null}
{error ? (

{error}

) : null} {asked && !error && list.length === 0 ? (

{t("radar.suggest.none")}

) : null} {list.length ? (
    {list.map((s) => { const already = taken.has(s.term.toLowerCase()); return (
  • {s.term} {s.usage === "exclude" ? t("radar.suggest.exclude") : t("radar.suggest.include")}

    {s.reason}

    {s.basis_text ?

    {t("radar.suggest.basis", { text: s.basis_text })}

    : null}
  • ); })}
) : null}
); }