feat(agent): support Responses steering and project-aware tools
Use the dedicated TJWATER_AGENT_URL and Responses stream protocol. Keep running turns active while users steer or queue follow-ups and edit pending delivery. Refresh the model catalog before generation and preserve approval mode selection. Integrate scoped investigation messaging/presentation, geographic place selection, current Agent composer styles and supporting workbench contracts. Validation: 77 focused Vitest checks, 21 browser cases, lint, typecheck and production build. Preserve direct Server/Agent routing and project authentication.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { FileText, History, MessageSquare } from "lucide-react";
|
||||
import { MessageSquare } from "lucide-react";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Disclosure } from "@/shared/ui/disclosure";
|
||||
import { StatusBadge } from "@/shared/ui/status";
|
||||
import type { InvestigationDiagram, InvestigationDiscussion } from "../api/investigation-report";
|
||||
import { formatDateTime as dateLabel } from "@/shared/utils/date-time";
|
||||
import { formatInvestigationText } from "../utils/investigation-text";
|
||||
import type { InvestigationDiagram, InvestigationDiscussion, InvestigationMapTarget } from "../api/investigation-report";
|
||||
import type {
|
||||
OperationalTask,
|
||||
OperationalTaskAction,
|
||||
@@ -17,6 +19,8 @@ import {
|
||||
isPendingInvestigationAction
|
||||
} from "../utils/investigation-presentation";
|
||||
import { InvestigationDiagramView } from "./investigation-diagram-view";
|
||||
import { InvestigationExpandCard } from "./investigation-expand-card";
|
||||
import { useReportUpdateMotion } from "../hooks/use-report-update-motion";
|
||||
|
||||
type ReportSelection = { snapshot: OperationalTaskReport; diagram: InvestigationDiagram | null };
|
||||
type Props = {
|
||||
@@ -27,9 +31,9 @@ type Props = {
|
||||
onLoadReports?: (offset: number) => Promise<OperationalTaskReportPage>;
|
||||
onReportViewed?: (generation: number, revision: number) => void;
|
||||
focusActionId?: string | null;
|
||||
onLocate?: (targets: InvestigationMapTarget[]) => void;
|
||||
};
|
||||
|
||||
const dateLabel = (value: string) => new Date(value).toLocaleString("zh-CN", { hour12: false });
|
||||
const roundLabel = (snapshot: OperationalTaskReport) =>
|
||||
snapshot.execution_generation == null ? "历史报告" : `第 ${snapshot.execution_generation} 轮`;
|
||||
const actionTitle = (action: OperationalTaskAction) =>
|
||||
@@ -123,27 +127,35 @@ export function InvestigationTaskReport({
|
||||
renderEvidence,
|
||||
onLoadReports,
|
||||
onReportViewed,
|
||||
focusActionId
|
||||
focusActionId,
|
||||
onLocate
|
||||
}: Props) {
|
||||
const [selection, setSelection] = useState<ReportSelection | null>(() =>
|
||||
task.latest_report
|
||||
? { snapshot: task.latest_report, diagram: investigationDiagram(task, task.latest_report) }
|
||||
: null
|
||||
);
|
||||
// Null follows live task content. Only a deliberate history selection freezes a report.
|
||||
const [selection, setSelection] = useState<ReportSelection | null>(null);
|
||||
const [history, setHistory] = useState<OperationalTaskReport[]>([]);
|
||||
const [historyOffset, setHistoryOffset] = useState<number | null>(0);
|
||||
const [historyLoading, setHistoryLoading] = useState(false);
|
||||
const [historyError, setHistoryError] = useState<string | null>(null);
|
||||
const snapshot = selection?.snapshot ?? null;
|
||||
const live = selection === null;
|
||||
const snapshot = selection?.snapshot ?? task.latest_report;
|
||||
const report = snapshot?.report;
|
||||
const diagram = selection?.diagram ?? (snapshot ? null : investigationDiagram(task, null));
|
||||
const progress = task.latest_progress?.execution_generation === task.execution_generation ? task.latest_progress : null;
|
||||
// A current final report owns its archived graph. During investigation, a
|
||||
// progress snapshot owns its whole graph, including an explicit null. A prior
|
||||
// round's final report must never hide the new round's progress.
|
||||
const currentFinalReport = snapshot?.execution_generation === task.execution_generation
|
||||
&& report?.stage === "final";
|
||||
const diagram = live
|
||||
? progress && !currentFinalReport ? progress.progress.diagram : investigationDiagram(task, snapshot)
|
||||
: selection.diagram;
|
||||
const diagramRunning = Boolean(live && !currentFinalReport && progress && task.active_run?.run_id === progress.run_id && task.active_run.status === "running");
|
||||
const previousRound = Boolean(
|
||||
snapshot && snapshot.execution_generation !== task.execution_generation
|
||||
);
|
||||
const olderRevision = Boolean(
|
||||
snapshot && task.latest_report && snapshot.revision !== task.latest_report.revision
|
||||
);
|
||||
const latestAvailable = Boolean(task.latest_report && (!snapshot || olderRevision));
|
||||
const latestAvailable = !live;
|
||||
const insufficient = report?.outcome === "insufficient_data";
|
||||
const currentInsufficient = task.latest_report?.execution_generation === task.execution_generation
|
||||
&& task.latest_report?.report.outcome === "insufficient_data";
|
||||
@@ -153,14 +165,8 @@ export function InvestigationTaskReport({
|
||||
const majorEvidence = (snapshot ? evidence : task.evidence).slice(0, 3);
|
||||
const remainingEvidence = (snapshot ? evidence : task.evidence).slice(3);
|
||||
const timeline = investigationTimeline(task, snapshot);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selection && task.latest_report)
|
||||
setSelection({
|
||||
snapshot: task.latest_report,
|
||||
diagram: investigationDiagram(task, task.latest_report)
|
||||
});
|
||||
}, [selection, task]);
|
||||
const reportRef = useReportUpdateMotion(task.task_id,
|
||||
`${snapshot?.execution_generation}:${snapshot?.revision}:${progress?.run_id}:${progress?.revision}`, live);
|
||||
|
||||
useEffect(() => {
|
||||
if (snapshot?.execution_generation == null || !onReportViewed) return;
|
||||
@@ -182,6 +188,7 @@ export function InvestigationTaskReport({
|
||||
const discuss = (prompt: string, extra?: { evidence_id?: string; gap_id?: string }) =>
|
||||
onContinue({
|
||||
prompt,
|
||||
reportGeneration: snapshot?.execution_generation,
|
||||
context: { ...(snapshot ? { report_revision: snapshot.revision } : {}), ...extra }
|
||||
});
|
||||
const selectReport = (item: OperationalTaskReport) =>
|
||||
@@ -202,7 +209,7 @@ export function InvestigationTaskReport({
|
||||
};
|
||||
|
||||
return (
|
||||
<article aria-label="调查任务报告" className="investigation-report surface-reading">
|
||||
<article ref={reportRef} aria-label="调查任务报告" className="investigation-report surface-reading">
|
||||
<header className="investigation-report-header">
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs text-[var(--ink-muted)]">
|
||||
@@ -227,8 +234,9 @@ export function InvestigationTaskReport({
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
title="打开调查协作对话,引用此任务,不会直接发送。"
|
||||
onClick={() =>
|
||||
onContinue({ context: snapshot ? { report_revision: snapshot.revision } : {} })
|
||||
onContinue({ context: snapshot ? { report_revision: snapshot.revision } : {}, reportGeneration: snapshot?.execution_generation })
|
||||
}
|
||||
>
|
||||
<MessageSquare size={14} aria-hidden />
|
||||
@@ -238,19 +246,16 @@ export function InvestigationTaskReport({
|
||||
|
||||
{latestAvailable ? (
|
||||
<div className="investigation-report-update" role="status">
|
||||
<span>第 {task.latest_report!.execution_generation ?? "未知"} 轮有新报告</span>
|
||||
<Button variant="outline" size="sm" onClick={() => selectReport(task.latest_report!)}>
|
||||
查看新报告
|
||||
<span>正在阅读历史报告{olderRevision ? ",已有新报告" : ""}</span>
|
||||
<Button variant="outline" size="sm" onClick={() => setSelection(null)}>
|
||||
返回实时内容
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<section aria-label="任务报告" className="investigation-report-section">
|
||||
<InvestigationExpandCard title="当前结果" label="任务报告" disabled={!report}
|
||||
revision={`${snapshot?.execution_generation}:${snapshot?.revision}`} preview={<>
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<h3 className="flex items-center gap-2 text-sm font-semibold text-[var(--ink)]">
|
||||
<FileText size={15} aria-hidden />
|
||||
当前结果
|
||||
</h3>
|
||||
{report ? (
|
||||
<StatusBadge tone={insufficient ? "warning" : "neutral"}>
|
||||
{insufficient
|
||||
@@ -282,37 +287,42 @@ export function InvestigationTaskReport({
|
||||
? "调查结论"
|
||||
: "历史报告内容"}
|
||||
</p>
|
||||
<p className="mt-1 text-sm leading-6 text-[var(--ink)]">{report.current_conclusion}</p>
|
||||
<p className="investigation-conclusion-preview mt-1 text-sm leading-6 text-[var(--ink)]">{formatInvestigationText(report.current_conclusion)}</p>
|
||||
<p className="mt-2 text-xs leading-5 text-[var(--ink-muted)]">
|
||||
结论边界:{report.conclusion_boundary}
|
||||
结论边界:{formatInvestigationText(report.conclusion_boundary)}
|
||||
</p>
|
||||
{insufficient ? (
|
||||
<p className="mt-2 text-sm leading-6 text-[var(--status-warning-foreground)]">
|
||||
结束原因:{report.stop_reason}
|
||||
结束原因:{formatInvestigationText(report.stop_reason)}
|
||||
</p>
|
||||
) : null}
|
||||
<p className="mt-2 text-xs text-[var(--ink-muted)]">
|
||||
正在阅读:{roundLabel(snapshot)} · 报告第 {snapshot.revision} 版 ·{" "}
|
||||
{dateLabel(snapshot.published_at)}
|
||||
</p>
|
||||
{!insufficient &&
|
||||
(report.recommendations.length > 0 || report.pending_items.length > 0) ? (
|
||||
<Disclosure label="判断说明与建议" className="mt-2">
|
||||
{report.recommendations.length > 0 ? (
|
||||
<ReportItems title="处置建议" items={report.recommendations} />
|
||||
) : null}
|
||||
{report.pending_items.length > 0 ? (
|
||||
<ReportItems title="待核实问题" items={report.pending_items} />
|
||||
) : null}
|
||||
</Disclosure>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<p className="mt-3 text-sm leading-6 text-[var(--ink-muted)]">
|
||||
尚未发布本轮调查报告。已保存的调查资料可在下方查看。
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
</>}>
|
||||
{report && snapshot ? <>
|
||||
<p className="text-xs text-[var(--ink-muted)]">{roundLabel(snapshot)} · 报告第 {snapshot.revision} 版 · {dateLabel(snapshot.published_at)}</p>
|
||||
<p className="mt-3 whitespace-pre-wrap text-sm leading-7 text-[var(--ink)]">{formatInvestigationText(report.current_conclusion)}</p>
|
||||
<p className="mt-3 text-sm leading-6 text-[var(--ink-muted)]">结论边界:{formatInvestigationText(report.conclusion_boundary)}</p>
|
||||
{insufficient ? <p className="mt-3 text-sm leading-6 text-[var(--status-warning-foreground)]">结束原因:{formatInvestigationText(report.stop_reason)}</p> : <>
|
||||
{report.recommendations.length > 0 ? <ReportItems title="处置建议" items={report.recommendations} /> : null}
|
||||
{report.pending_items.length > 0 ? <ReportItems title="待核实问题" items={report.pending_items} /> : null}
|
||||
</>}
|
||||
</> : <p className="text-sm text-[var(--ink-muted)]">尚未发布本轮调查报告。</p>}
|
||||
</InvestigationExpandCard>
|
||||
|
||||
{live ? <section aria-label="当前调查进展" className="investigation-report-section">
|
||||
<h3 className="text-sm font-semibold text-[var(--ink)]">当前调查进展</h3>
|
||||
<p className="mt-2 whitespace-pre-wrap break-words text-sm leading-6 text-[var(--ink)]">{formatInvestigationText(progress?.progress.summary ?? "尚未发布本轮调查进展。")}</p>
|
||||
{progress ? <p className="mt-1 text-xs text-[var(--ink-muted)]">实时内容 · 第 {progress.execution_generation} 轮 · 更新 {dateLabel(progress.published_at)}</p> : null}
|
||||
</section> : null}
|
||||
|
||||
{pendingActions.length > 0 ? (
|
||||
<section aria-label="当前需处理" className="investigation-report-section">
|
||||
@@ -387,13 +397,13 @@ export function InvestigationTaskReport({
|
||||
</h3>
|
||||
{report.data_gaps.map((gap) => (
|
||||
<div key={gap.gap_id} className="mt-3">
|
||||
<p className="text-sm font-medium leading-6 text-[var(--ink)]">{gap.missing_data}</p>
|
||||
<p className="text-sm leading-6 text-[var(--ink-muted)]">判断影响:{gap.impact}</p>
|
||||
<p className="text-sm font-medium leading-6 text-[var(--ink)]">{formatInvestigationText(gap.missing_data)}</p>
|
||||
<p className="text-sm leading-6 text-[var(--ink-muted)]">判断影响:{formatInvestigationText(gap.impact)}</p>
|
||||
<div className="mt-1 flex flex-wrap items-center gap-x-4">
|
||||
<Disclosure label="查看查询依据">
|
||||
<div className="space-y-1 py-2 text-xs leading-5 text-[var(--ink-muted)]">
|
||||
<p>查询范围:{gap.scope}</p>
|
||||
<p>需补齐:{gap.required_data}</p>
|
||||
<p>查询范围:{formatInvestigationText(gap.scope)}</p>
|
||||
<p>需补齐:{formatInvestigationText(gap.required_data)}</p>
|
||||
{gap.evidence_refs.map((id) => (
|
||||
<EvidenceReference
|
||||
key={id}
|
||||
@@ -409,7 +419,7 @@ export function InvestigationTaskReport({
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
discuss(
|
||||
`我要补充“${gap.missing_data}”相关的现场事实。请说明需要的记录范围和时间信息。`,
|
||||
`我要补充“${formatInvestigationText(gap.missing_data)}”相关的现场事实。请说明需要的记录范围和时间信息。`,
|
||||
{ gap_id: gap.gap_id }
|
||||
)
|
||||
}
|
||||
@@ -433,8 +443,10 @@ export function InvestigationTaskReport({
|
||||
|
||||
{diagram ? (
|
||||
<InvestigationDiagramView
|
||||
key={`${snapshot?.revision ?? "plan"}:${diagram.mermaid}`}
|
||||
key={live ? `live:${task.task_id}` : `report:${snapshot?.revision}`}
|
||||
diagram={diagram}
|
||||
running={diagramRunning}
|
||||
onLocate={onLocate}
|
||||
renderDetails={(node) => (
|
||||
<>
|
||||
{node.evidence_refs.map((id) => (
|
||||
@@ -473,22 +485,43 @@ export function InvestigationTaskReport({
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<section aria-label="主要依据" className="investigation-report-section">
|
||||
<h3 className="text-sm font-semibold text-[var(--ink)]">主要依据</h3>
|
||||
<InvestigationExpandCard title="主要依据" disabled={task.evidence.length === 0} preview={<>
|
||||
<div className="investigation-evidence-preview">
|
||||
{majorEvidence.map((item) => <div key={item.evidence_id}>
|
||||
<p className="text-sm leading-6 text-[var(--ink)]">{formatInvestigationText(item.summary)}</p>
|
||||
<p className="text-xs leading-5 text-[var(--ink-muted)]">{item.source_label} · {dateLabel(item.observed_at ?? item.recorded_at)}</p>
|
||||
<Button type="button" variant="outline" size="sm" className="mt-2" onClick={() => discuss(
|
||||
`请解释“${formatInvestigationText(item.summary)}”这条证据的范围、来源和判断影响。`, { evidence_id: item.evidence_id }
|
||||
)}><MessageSquare size={14} aria-hidden />围绕此证据提问</Button>
|
||||
</div>)}
|
||||
</div>
|
||||
<p className="mt-2 text-xs text-[var(--ink-muted)]">
|
||||
{(snapshot ? evidence : task.evidence).length > 0
|
||||
? `共 ${(snapshot ? evidence : task.evidence).length} 条引用依据,展开查看来源与原始记录。`
|
||||
: "尚无可展示的引用证据。"}
|
||||
</p>
|
||||
</>}>
|
||||
{(close) => <>
|
||||
{majorEvidence.map((item) => (
|
||||
<div key={item.evidence_id} className="mt-2">
|
||||
{renderEvidence(item)}
|
||||
<button
|
||||
{onLocate && diagram?.node_refs.some((node) => node.evidence_refs.includes(item.evidence_id) && node.map_targets?.length) ? (
|
||||
<button type="button" className="investigation-text-action" onClick={() => { close(false); onLocate(diagram.node_refs.filter((node) => node.evidence_refs.includes(item.evidence_id)).flatMap((node) => node.map_targets ?? [])); }}>在地图查看</button>
|
||||
) : null}
|
||||
<Button
|
||||
type="button"
|
||||
className="investigation-text-action"
|
||||
onClick={() =>
|
||||
discuss(`请解释“${item.summary}”这条证据的范围、来源和判断影响。`, {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="mt-2"
|
||||
onClick={() => { close(false);
|
||||
discuss(`请解释“${formatInvestigationText(item.summary)}”这条证据的范围、来源和判断影响。`, {
|
||||
evidence_id: item.evidence_id
|
||||
})
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MessageSquare size={14} aria-hidden />
|
||||
围绕此证据提问
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{(snapshot ? evidence : task.evidence).length === 0 ? (
|
||||
@@ -499,17 +532,20 @@ export function InvestigationTaskReport({
|
||||
{remainingEvidence.map((item) => (
|
||||
<div key={item.evidence_id}>
|
||||
{renderEvidence(item)}
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
className="investigation-text-action"
|
||||
onClick={() =>
|
||||
discuss(`请解释“${item.summary}”的来源和判断影响。`, {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="mt-2"
|
||||
onClick={() => { close(false);
|
||||
discuss(`请解释“${formatInvestigationText(item.summary)}”的来源和判断影响。`, {
|
||||
evidence_id: item.evidence_id
|
||||
})
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MessageSquare size={14} aria-hidden />
|
||||
围绕此证据提问
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</Disclosure>
|
||||
@@ -522,45 +558,31 @@ export function InvestigationTaskReport({
|
||||
.map(renderEvidence)}
|
||||
</Disclosure>
|
||||
) : null}
|
||||
</section>
|
||||
</>}
|
||||
</InvestigationExpandCard>
|
||||
|
||||
<section className="investigation-report-section">
|
||||
<Disclosure label="任务时间线">
|
||||
<p className="pb-2 text-xs leading-5 text-[var(--ink-muted)]">
|
||||
按时间倒序汇总当前任务记录;报告事件对应当前正在阅读的版本。
|
||||
</p>
|
||||
<ol aria-label="任务时间线记录" className="investigation-timeline">
|
||||
{timeline.map((item) => (
|
||||
<li key={item.id} className="investigation-timeline-item">
|
||||
<time dateTime={item.occurredAt} className="block text-[11px] text-[var(--ink-muted)]">
|
||||
{dateLabel(item.occurredAt)}
|
||||
</time>
|
||||
<p className="text-xs font-medium leading-5 text-[var(--ink)]">{item.title}</p>
|
||||
<p className="text-xs leading-5 text-[var(--ink-muted)]">{item.detail}</p>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</Disclosure>
|
||||
<InvestigationExpandCard title="任务时间线" disabled={timeline.length === 0} preview={
|
||||
<p className="mt-2 text-sm leading-6 text-[var(--ink-muted)]">
|
||||
{timeline[0] ? `${timeline[0].title} · ${dateLabel(timeline[0].occurredAt)},共 ${timeline.length} 条记录。` : "尚无调查记录。"}
|
||||
</p>
|
||||
}>
|
||||
<InvestigationTimeline items={timeline} />
|
||||
</InvestigationExpandCard>
|
||||
{onLoadReports ? (
|
||||
<Disclosure
|
||||
label={
|
||||
<span className="flex items-center gap-2">
|
||||
<History size={14} aria-hidden />
|
||||
报告版本
|
||||
</span>
|
||||
}
|
||||
onToggle={(event) => {
|
||||
if (event.currentTarget.open && history.length === 0 && !historyLoading)
|
||||
void loadHistory(0);
|
||||
}}
|
||||
<InvestigationExpandCard title="报告版本"
|
||||
preview={<p className="mt-2 text-sm leading-6 text-[var(--ink-muted)]">
|
||||
{snapshot ? `${roundLabel(snapshot)} · 报告第 ${snapshot.revision} 版 · ${dateLabel(snapshot.published_at)}` : "尚未发布报告。"}
|
||||
</p>}
|
||||
onOpen={() => { if (history.length === 0 && !historyLoading) void loadHistory(0); }}
|
||||
>
|
||||
{(close) => <>
|
||||
{history.map((item) => (
|
||||
<button
|
||||
key={item.revision}
|
||||
type="button"
|
||||
className="investigation-history-row"
|
||||
aria-pressed={snapshot?.revision === item.revision}
|
||||
onClick={() => selectReport(item)}
|
||||
onClick={() => { selectReport(item); close(); }}
|
||||
>
|
||||
<span>
|
||||
{roundLabel(item)} · 报告第 {item.revision} 版
|
||||
@@ -593,16 +615,30 @@ export function InvestigationTaskReport({
|
||||
{historyError ? "重新加载" : "更多报告"}
|
||||
</Button>
|
||||
) : null}
|
||||
</Disclosure>
|
||||
</>}
|
||||
</InvestigationExpandCard>
|
||||
) : null}
|
||||
<p className="mt-3 text-xs text-[var(--ink-muted)]">
|
||||
<p className="px-4 pb-4 pt-1 text-xs text-[var(--ink-muted)]">
|
||||
任务更新于 {dateLabel(task.updated_at)}
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
function InvestigationTimeline({ items }: { items: InvestigationTimelineItem[] }) {
|
||||
return <>
|
||||
<p className="pb-3 text-xs leading-5 text-[var(--ink-muted)]">按时间倒序汇总当前任务记录;报告事件对应当前正在阅读的版本。</p>
|
||||
<ol aria-label="任务时间线记录" className="investigation-timeline">
|
||||
{items.map((item) => <li key={item.id} className="investigation-timeline-item">
|
||||
<time dateTime={item.occurredAt} className="block text-[11px] text-[var(--ink-muted)]">{dateLabel(item.occurredAt)}</time>
|
||||
<p className="text-xs font-medium leading-5 text-[var(--ink)]">{item.title}</p>
|
||||
<p className="text-xs leading-5 text-[var(--ink-muted)]">{formatInvestigationText(item.detail)}</p>
|
||||
</li>)}
|
||||
</ol>
|
||||
{items.length === 0 ? <p className="text-sm text-[var(--ink-muted)]">尚无调查记录。</p> : null}
|
||||
</>;
|
||||
}
|
||||
|
||||
function EvidenceReference({
|
||||
id,
|
||||
task,
|
||||
@@ -626,7 +662,7 @@ function ReportItems({ title, items }: { title: string; items: string[] }) {
|
||||
<p className="font-medium">{title}</p>
|
||||
<ul className="mt-1 list-disc space-y-1 pl-4">
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
<li key={index}>{formatInvestigationText(item)}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user