import { useEffect, useState } from "react"; import { fetchAuthenticated } from "../../../auth/api-client"; import { GAP_REASONS, GAP_STATUSES, type DemandGap, type GapStatus, } from "../../../shared/assets/assistant"; export function DemandGaps({ close }: { close: () => void }) { const [items, setItems] = useState([]), [all, setAll] = useState(false), [canManage, setCanManage] = useState(false); const [error, setError] = useState(""), [loading, setLoading] = useState(true), [revision, setRevision] = useState(0); useEffect(() => { const controller = new AbortController(); setLoading(true); setError(""); void fetchAuthenticated( `/api/asset-assistant/gaps?scope=${all ? "all" : "mine"}`, { signal: controller.signal }, ) .then(async (r) => { const data = await r.json(); if (!r.ok) throw new Error(data.error); return data; }) .then((data) => { setItems(data.items); setCanManage(data.canManage); }) .catch((e) => { if (!controller.signal.aborted) setError(e.message || "读取失败。"); }) .finally(() => { if (!controller.signal.aborted) setLoading(false); }); return () => controller.abort(); }, [all, revision]); const update = async (id: number, status: GapStatus) => { setError(""); try { const r = await fetchAuthenticated(`/api/asset-assistant/gaps/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status }), }); if (!r.ok) throw new Error("状态保存失败。"); setRevision((x) => x + 1); } catch { setError("状态保存失败,请重试。"); } }; return (

待完善需求

保留90天 · 记录未完成的问题与反馈,用于完善分析能力

{canManage && ( )}
{error && (

{error}

)} {loading ? (

正在读取需求记录…

) : ( <>

{items.length}项需求 · 累计 {items.reduce((n, v) => n + v.occurrences, 0)}次(最近200项)

{items.length === 0 && !error && (

暂无未满足需求

)} {items.map((item) => (
{item.demand}

{GAP_REASONS[item.reason]} · {item.occurrences}次 · 最近{" "} {new Date(item.lastSeen).toLocaleString("zh-CN")}

{canManage ? ( ) : ( {GAP_STATUSES[item.status]} )}
))} )}
); }